# 機能設計書 34-重複インデックス (duplicate-index)

## 概要

本ドキュメントは、Lua Language Serverにおける「重複インデックス検出機能」の設計詳細を記載したものである。テーブル定義内で同じキーが複数回定義されている場合を検出し、意図しない上書きや設定ミスを防止する診断機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：
Luaのテーブル定義において、同じキーを複数回指定した場合、後の値で上書きされる。これは多くの場合、コピー&ペーストのミスや設定の重複によるバグの原因となる。本機能は、テーブル内の重複キーを検出し、開発者に警告することで、意図しない動作を未然に防止する。

**機能の利用シーン**：
- 設定テーブルの定義時に重複キーを検出
- 大規模なテーブル定義でのコピー&ペーストミスを発見
- データ定義ファイルの品質チェック

**主要な処理内容**：
1. 対象ファイルのASTを取得する
2. AST内の`table`タイプのノードを走査する
3. 各テーブルの要素（tablefield/tableindex/tableexp）のキー名を収集する
4. 同じキー名が複数回出現する場合を検出する
5. 重複箇所を関連情報付きで診断として報告する

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

**権限による制御**：
- 設定`Lua.diagnostics.disable`で本診断を無効化可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | 診断情報 | 補助機能 | テーブルの重複キーを検出 |

## 機能種別

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

## 入力仕様

### 入力パラメータ

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

### 入力データソース

- ファイルシステムから読み込まれたLuaソースコード
- `files.getState(uri)`から取得されるAST情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| start | integer | エラー位置の開始オフセット |
| finish | integer | エラー位置の終了オフセット |
| related | table | 関連する重複箇所の位置情報配列 |
| message | string | 診断メッセージ（例：「重複するインデックス `xxx`」） |
| level | integer | 診断レベル（Hint） |
| tags | table | DiagnosticTag.Unnecessary（先行定義用） |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. ファイル状態の取得
   └─ files.getState(uri)でAST付きの状態を取得
2. tableノードの走査
   └─ guide.eachSourceType(ast.ast, 'table', callback)
3. テーブル要素の収集
   └─ tablefield/tableindex/tableexpをチェック
4. キー名の抽出
   └─ guide.getKeyName(obj)でキー名を取得
5. 重複の検出
   └─ 同じキー名が2回以上出現した場合を記録
6. 診断結果の報告
   └─ 先行定義はUnnecessary+Hint、最後の定義は通常警告
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[ファイル状態を取得]
    B --> C{状態が存在するか?}
    C -->|No| D[終了]
    C -->|Yes| E[tableノードを走査]
    E --> F[await.delay]
    F --> G[テーブル要素を収集]
    G --> H[キー名をmarkテーブルに記録]
    H --> I{重複キーあり?}
    I -->|No| J[次のtableへ]
    I -->|Yes| K[related情報を構築]
    K --> L[先行定義にUnnecessary+Hint]
    L --> M[最後の定義に通常警告]
    M --> J
    J --> N{全table処理完了?}
    N -->|No| E
    N -->|Yes| D
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | 対象要素タイプ | tablefield/tableindex/tableexpが対象 | テーブル内要素の判定 |
| BR-34-02 | キー名取得 | guide.getKeyName(obj)でキー名を取得 | nil以外のキー名のみ対象 |
| BR-34-03 | 先行定義の扱い | 最後以外の定義はUnnecessaryタグ+Hintレベル | 上書きされる値の表示 |
| BR-34-04 | 最終定義の扱い | 最後の定義は通常の警告として表示 | 実際に有効な値 |
| BR-34-05 | related情報 | 全ての重複箇所をrelated配列で関連付け | ナビゲーション用 |

### 計算ロジック

#### 重複検出アルゴリズム

```lua
-- mark[name] = { 定義箇所のリスト }
for _, obj in ipairs(source) do
    local name = guide.getKeyName(obj)
    if name then
        if not mark[name] then
            mark[name] = {}
        end
        mark[name][#mark[name]+1] = obj.field or obj.index or obj.value
    end
end

-- 2つ以上の定義がある場合に警告
for name, defs in pairs(mark) do
    if #defs > 1 then
        -- 先行定義（1 ~ n-1）: Hint + Unnecessary
        -- 最終定義（n）: 通常警告
    end
end
```

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

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

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

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

## パフォーマンス要件

- 単一ファイルの診断は通常0.5秒以内に完了すること
- `await.delay()`により大規模テーブルでの処理ブロッキングを防止

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

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

## 備考

- 先行定義と最終定義で診断レベルを変えることで、どの定義が有効かを明確に表示
- related情報により、エディタ上で関連箇所への素早いナビゲーションが可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | define.lua | `script/proto/define.lua` | DiagnosticSeverity.Hint、DiagnosticTag.Unnecessaryの定義 |
| 1-2 | guide.lua | `script/parser/guide.lua` | tableノードの構造、getKeyName関数 |

**読解のコツ**: tableノードは配列部分に各要素（tablefield/tableindex/tableexp）を持つ。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | duplicate-index.lua | `script/core/diagnostics/duplicate-index.lua` | メイン診断関数 |

**主要処理フロー**:
1. **8-12行目**: ファイル状態取得
2. **14行目**: guide.eachSourceType(ast.ast, 'table', callback)でtableノード走査
3. **15行目**: await.delay()で非同期処理
4. **16行目**: markテーブルを初期化
5. **17-29行目**: テーブル要素を走査してキー名を収集
6. **31-62行目**: 重複キーの検出と診断報告

#### Step 3: 重複検出ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | duplicate-index.lua | `script/core/diagnostics/duplicate-index.lua` | 重複検出部分 |

**主要処理フロー**:
- **17-29行目**: キー名収集ループ
  - **18-20行目**: tablefield/tableindex/tableexpのみ対象
  - **21行目**: guide.getKeyName(obj)でキー名取得
  - **22-27行目**: markテーブルに位置情報を追加
- **31-62行目**: 診断報告ループ
  - **32行目**: #defs > 1で重複判定
  - **33-41行目**: related配列の構築
  - **42-51行目**: 先行定義（Hint + Unnecessary）
  - **53-60行目**: 最終定義（通常警告）

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

```
script/core/diagnostics/init.lua (診断エントリー)
    │
    ├─ require('core.diagnostics.duplicate-index')
    │      │
    │      ├─ files.getState(uri)
    │      │
    │      ├─ guide.eachSourceType(ast.ast, 'table', callback)
    │      │      │
    │      │      ├─ await.delay()
    │      │      │
    │      │      └─ guide.getKeyName(obj)
    │      │
    │      └─ callback(result)
    │             │
    │             ├─ related配列構築
    │             ├─ 先行定義報告 (Hint + Unnecessary)
    │             └─ 最終定義報告 (Warning)
    │
    └─ LSP診断通知
```

### データフロー図

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

Luaソースコード ───▶ パーサー(AST生成) ───▶ AST
                                              │
                                              ▼
                    guide.eachSourceType('table')
                                              │
                                              ▼
                    テーブル要素を走査 ───▶ mark[name] = { 位置情報 }
                                              │
                                              ▼
                    重複判定 (#defs > 1) ───▶ related配列構築
                                              │
                                              ▼
                    診断メッセージ生成 ───▶ LSP診断通知
                                              │
                                              ▼
                                        エディタ表示
                                        （先行: 淡色、最終: 警告）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| duplicate-index.lua | `script/core/diagnostics/duplicate-index.lua` | ソース | 重複インデックス診断のメインロジック |
| init.lua | `script/core/diagnostics/init.lua` | ソース | 診断処理のエントリーポイント |
| define.lua | `script/proto/define.lua` | ソース | DiagnosticSeverity、DiagnosticTag定義 |
| guide.lua | `script/parser/guide.lua` | ソース | getKeyName関数、AST操作 |
| files.lua | `script/files.lua` | ソース | ファイル状態管理 |
| await.lua | `script/await.lua` | ソース | 非同期処理ユーティリティ |
| language.lua | `script/language.lua` | ソース | 診断メッセージ定義 |
