# 機能設計書 53-型チェック (Type Checking)

## 概要

本ドキュメントは、Lua Language Serverにおける型チェック機能の設計を記載する。この機能は、LuaDocアノテーションで定義された型と実際のコードの型が一致しているかを検証し、型の不一致を診断として報告する。

### 本機能の処理概要

型チェック機能は、変数への代入、関数の引数、戻り値などにおいて、期待される型と実際の型が互換性があるかを検証する機能である。型推論機能と連携して、コードの型安全性を保証する。

**業務上の目的・背景**：動的型付け言語であるLuaでは、実行時まで型エラーが検出されない。型チェック機能により、開発段階で型の不整合を検出し、実行時エラーを未然に防ぐことができる。これにより、コードの品質向上とデバッグ時間の削減が期待できる。

**機能の利用シーン**：
- 変数への代入時に型の互換性を検証
- 関数呼び出し時に引数の型を検証
- 関数の戻り値の型を検証
- フィールドアクセス時の型検証

**主要な処理内容**：
1. 子型（代入元/引数）と親型（代入先/パラメータ）の取得
2. vm.isSubType関数による型互換性チェック
3. 型エラーの詳細情報の収集
4. 診断メッセージの生成

**関連システム・外部連携**：型推論機能（vm/infer.lua）、診断機能（diagnostics/）と連携。

**権限による制御**：設定により型チェックの厳密度を調整可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | 診断情報 | 補助機能 | 型の不一致を検出 |

## 機能種別

バリデーション / 診断

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| uri | string | Yes | ファイルURI | 有効なfile:// URI |
| child | vm.node/string/vm.node.object | Yes | 子型（代入元） | - |
| parent | vm.node/string/vm.node.object | Yes | 親型（代入先） | - |
| mark | table | No | 循環参照防止用マーク | - |
| errs | table | No | エラー情報収集用配列 | - |

### 入力データソース

- 型推論結果（vm.node）
- LuaDocアノテーション情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | boolean/nil | true: 互換, false: 非互換, nil: 不明 |
| errs | array | 型エラーの詳細情報 |

### 出力先

- 診断機能への型エラー情報提供
- LSPクライアントへの診断メッセージ

## 処理フロー

### 処理シーケンス

```
1. vm.isSubType呼び出し
   └─ 子型と親型を受け取る

2. 子型の正規化
   └─ 文字列型名またはvm.nodeから型情報を取得

3. 親型の正規化
   └─ 文字列型名またはvm.nodeから型情報を取得

4. 特殊型チェック
   └─ any/unknownは常に互換

5. Union型チェック
   └─ vm.nodeの場合、全メンバーをチェック

6. 名前ベースチェック
   └─ 基本型名の比較

7. 継承チェック
   └─ クラス継承関係の確認

8. 結果返却
   └─ true/false/nilを返却
```

### フローチャート

```mermaid
flowchart TD
    A[vm.isSubType開始] --> B{子型がstring?}
    B -->|Yes| C[グローバル型取得]
    B -->|No| D{子型がvm.node?}
    C --> D
    D -->|Yes| E[Union型チェック]
    D -->|No| F[型名取得]
    E --> G{全メンバー互換?}
    G -->|Yes| H[true返却]
    G -->|No| I[false返却]
    F --> J{any/unknown?}
    J -->|Yes| H
    J -->|No| K{同一型名?}
    K -->|Yes| L[値チェック]
    L --> M{値一致?}
    M -->|Yes| H
    M -->|No| I
    K -->|No| N{継承関係?}
    N -->|Yes| H
    N -->|No| O{Enum?}
    O -->|Yes| P[Enumチェック]
    P --> Q{Enum互換?}
    Q -->|Yes| H
    Q -->|No| I
    O -->|No| I
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-01 | any互換 | any型は全ての型と互換 | 常時 |
| BR-53-02 | unknown互換 | unknown型は全ての型と互換 | 常時 |
| BR-53-03 | number-integer | numberはintegerを包含 | 常時 |
| BR-53-04 | Union互換 | Union型は全メンバーが互換なら互換 | weakUnionCheck=falseの場合 |
| BR-53-05 | weakUnion | Union型のいずれかが互換なら互換 | weakUnionCheck=trueの場合 |
| BR-53-06 | 継承互換 | 子クラスは親クラスと互換 | クラス継承時 |
| BR-53-07 | Enum互換 | Enum値はEnum型と互換 | Enum定義時 |

### 計算ロジック

**型互換性判定**：
```
isSubType(child, parent) =
  if child == any or child == unknown then true
  if parent == any or parent == unknown then true
  if childName == parentName then checkValue(child, parent)
  if parent == number and child == integer then true
  if child extends parent then true
  else false
```

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| TYPE_ERROR_DISMATCH | 型不一致 | 基本的な型の不一致 | エラー情報を収集 |
| TYPE_ERROR_INTEGER_DISMATCH | 整数値不一致 | リテラル整数値が不一致 | エラー情報を収集 |
| TYPE_ERROR_STRING_DISMATCH | 文字列値不一致 | リテラル文字列値が不一致 | エラー情報を収集 |
| TYPE_ERROR_BOOLEAN_DISMATCH | 真偽値不一致 | リテラル真偽値が不一致 | エラー情報を収集 |
| TYPE_ERROR_ENUM_LITERAL_DISMATCH | Enum値不一致 | Enum値が一致しない | エラー情報を収集 |
| TYPE_ERROR_UNION_DISMATCH | Union型不一致 | Union型のメンバーが不一致 | エラー情報を収集 |

### リトライ仕様

リトライは行わない。

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

トランザクションは使用しない。

## パフォーマンス要件

- markテーブルで循環参照を防止
- maxUnionVariants設定でUnion型のチェック数を制限

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

特になし。

## 備考

- 設定により型チェックの厳密度を調整可能
  - `Lua.type.weakUnionCheck`: Union型チェックを緩和
  - `Lua.type.weakNilCheck`: nilチェックを緩和
  - `Lua.type.castNumberToInteger`: number→integerの暗黙変換を許可

---

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

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

### 推奨読解順序

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

型チェックで使用するエラー情報の構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | type.lua | `script/vm/type.lua` | typcheck.err型エイリアス（13行目） |
| 1-2 | type.lua | `script/vm/type.lua` | ErrorMessageMap（818-837行目） |

**読解のコツ**:
- `typecheck.err`は`vm.node.object|string|vm.node`の配列
- エラーは種別名とパラメータの組み合わせで表現
- ErrorMessageMapでエラー種別からパラメータ構造を取得

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | type.lua | `script/vm/type.lua` | vm.isSubType関数（365-627行目） |

**主要処理フロー**:
1. **368-373行目**: 子型が文字列の場合、グローバル型を取得
2. **374-456行目**: 子型がvm.nodeの場合、Union型チェック
3. **458-468行目**: 子型名がany/unknownの場合、true返却
4. **470-513行目**: 親型がvm.nodeの場合、Union型チェック
5. **528-533行目**: 型名が一致する場合、値チェック
6. **535-560行目**: number/integer互換性チェック
7. **562-570行目**: Enumチェック
8. **584-603行目**: クラス継承チェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | type.lua | `script/vm/type.lua` | vm.getNodeName関数（17-53行目） |
| 3-2 | type.lua | `script/vm/type.lua` | checkParentEnum関数（61-144行目） |
| 3-3 | type.lua | `script/vm/type.lua` | checkValue関数（186-274行目） |

**主要処理フロー**:
- **vm.getNodeName**: オブジェクトから型名を取得
- **checkParentEnum**: 親がEnum型の場合のチェック
- **checkValue**: リテラル値の比較

#### Step 4: エラーメッセージ生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | type.lua | `script/vm/type.lua` | vm.viewTypeErrorMessage関数（842-896行目） |

**主要処理フロー**:
- **843-887行目**: エラー配列からメッセージを組み立て
- **888-895行目**: 長すぎる場合は切り詰め

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

```
vm.isSubType(uri, child, parent, mark, errs)
    │
    ├─ [子型がstring]
    │      └─ vm.getGlobal('type', child)
    │
    ├─ [子型がvm.node]
    │      └─ child:eachObject() [Union型のメンバーを列挙]
    │             └─ vm.isSubType(uri, n, parent, mark, errs) [再帰]
    │
    ├─ vm.getNodeName(child)
    │
    ├─ [親型がstring]
    │      └─ vm.getGlobal('type', parent)
    │
    ├─ [親型がvm.node]
    │      └─ parent:eachObject() [Union型のメンバーを列挙]
    │             └─ vm.isSubType(uri, child, n, mark, errs) [再帰]
    │
    ├─ checkValue(parent, child, mark, errs)
    │      └─ リテラル値の比較
    │
    ├─ checkParentEnum(parentName, child, uri, mark, errs)
    │      └─ Enum型との互換性チェック
    │
    └─ [継承チェック]
           └─ childClass:getSets(uri) → set.extends
                  └─ vm.isSubType(uri, ext[1], parent, mark, errs) [再帰]

vm.canCastType(uri, defNode, refNode, errs)
    │
    ├─ vm.getInfer(defNode)
    ├─ vm.getInfer(refNode)
    │
    └─ vm.isSubType(uri, refNode, defNode, {}, errs)

vm.viewTypeErrorMessage(uri, errs)
    │
    └─ ErrorMessageMap[name] → パラメータ構造
           └─ lang.script(name, lparams) → ローカライズされたメッセージ
```

### データフロー図

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

child (vm.node)    ───▶  vm.isSubType            ───▶  boolean/nil
parent (vm.node)          │
                         ├─ getNodeName
                         ├─ checkValue
                         ├─ checkParentEnum
                         └─ 継承チェック
                                │
                                ▼
                         errs (エラー配列)
                                │
                                ▼
                         vm.viewTypeErrorMessage  ───▶  string
                                                       (エラーメッセージ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| type.lua | `script/vm/type.lua` | ソース | 型チェックのメインロジック |
| infer.lua | `script/vm/infer.lua` | ソース | 型推論・ビュー生成 |
| node.lua | `script/vm/node.lua` | ソース | vm.nodeクラス |
| global.lua | `script/vm/global.lua` | ソース | グローバル型管理 |
| assign-type-mismatch.lua | `script/core/diagnostics/assign-type-mismatch.lua` | ソース | 代入型不一致診断 |
| param-type-mismatch.lua | `script/core/diagnostics/param-type-mismatch.lua` | ソース | 引数型不一致診断 |
| return-type-mismatch.lua | `script/core/diagnostics/return-type-mismatch.lua` | ソース | 戻り値型不一致診断 |
| language.lua | `script/language.lua` | ソース | エラーメッセージローカライズ |
