# 機能設計書 36-変数の導入

## 概要

本ドキュメントは、Roslynの「変数の導入（Introduce Variable）」機能の設計仕様を記述する。この機能は、選択された式を新しいローカル変数、定数、またはフィールドとして抽出するリファクタリング機能である。

### 本機能の処理概要

**業務上の目的・背景**：複雑な式や繰り返し使用される式に名前を付けることで、コードの可読性と保守性が向上する。また、一度計算した結果を変数に保存することでパフォーマンスが改善される場合もある。この機能は、そのような場面で式を安全に変数として抽出する。

**機能の利用シーン**：開発者が長い式に意味のある名前を付けたい場合や、同じ式の重複計算を避けたい場合に使用する。例えば、`return a * b + c;`を`var result = a * b; return result + c;`に変換する場面で活用される。

**主要な処理内容**：
1. 選択された式の検証（変数化可能かどうかの判定）
2. 式の出現コンテキスト判定（ブロック内、フィールド初期化子内、クエリ内等）
3. 適切な変数種別の決定（ローカル変数、定数、フィールド、クエリ変数）
4. 変数名の生成
5. 同一式のすべての出現箇所の検索と置換（オプション）

**関連システム・外部連携**：セマンティックファクトサービスを使用して変数名を生成する。コードクリーンアップサービスを使用してフォーマットを整える。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | N/A | N/A | コードエディタ上のコンテキストメニューから直接実行 |

## 機能種別

コードリファクタリング / コード変換

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| document | Document | Yes | 操作対象のドキュメント | null不可 |
| textSpan | TextSpan | Yes | 選択された式の範囲 | 有効な範囲 |
| allOccurrences | bool | No | 同一式のすべての出現を置換するか | デフォルトfalse |
| isConstant | bool | No | 定数として抽出するか | 式が定数評価可能な場合のみ有効 |
| isLocal | bool | No | ローカル変数として抽出するか | ブロックコンテキストの場合のみ有効 |
| isQueryLocal | bool | No | クエリ変数として抽出するか | クエリコンテキストの場合のみ有効 |

### 入力データソース

- エディタで選択された式

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| modifiedDocument | Document | 変更が適用されたドキュメント |

### 出力先

- 対象ドキュメント（変数宣言の挿入、式の置換）

## 処理フロー

### 処理シーケンス

```
1. State生成
   └─ State.GenerateAsync()で式の解析と状態生成

2. コンテキスト判定
   └─ InQueryContext: クエリ式内
   └─ InParameterContext: パラメータ初期化子内
   └─ InFieldContext: フィールド初期化子内
   └─ InConstructorInitializerContext: コンストラクタ初期化子内
   └─ InAutoPropertyInitializerContext: 自動プロパティ初期化子内
   └─ InAttributeContext: 属性引数内
   └─ InBlockContext: ブロック内
   └─ InExpressionBodiedMemberContext: 式形式メンバー内
   └─ InGlobalStatementContext: グローバル文内

3. 定数判定
   └─ IsConstant: コンパイル時定数かどうか

4. アクション種別の決定
   └─ コンテキストに応じて適切なアクションを生成
   └─ allOccurrences true/false のペアを生成

5. 変数名の生成
   └─ GenerateUniqueFieldName() or GenerateUniqueLocalName()
   └─ 式の内容に基づく意味のある名前

6. 同一式の検索（allOccurrencesの場合）
   └─ FindMatches()
   └─ SemanticEquivalence.AreEquivalent()で意味的等価性チェック

7. 変数挿入と式置換
   └─ IntroduceLocal() / IntroduceField() / IntroduceQueryLocal()
   └─ Rewrite()で式を変数参照に置換

8. コードクリーンアップ
   └─ 型の簡略化
   └─ フォーマット適用
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[State生成]
    B --> C{式は有効?}
    C -->|No| Z[終了]
    C -->|Yes| D[コンテキスト判定]
    D --> E{クエリ内?}
    E -->|Yes| F[クエリ変数として抽出]
    E -->|No| G{パラメータ内?}
    G -->|Yes| H[定数フィールドとして抽出]
    G -->|No| I{フィールド初期化子内?}
    I -->|Yes| J[フィールドとして抽出]
    I -->|No| K{ブロック内?}
    K -->|Yes| L{定数?}
    L -->|Yes| M[定数ローカルまたはフィールド]
    L -->|No| N[ローカル変数として抽出]
    K -->|No| O[その他コンテキスト処理]
    F --> P[変数名生成]
    H --> P
    J --> P
    M --> P
    N --> P
    O --> P
    P --> Q{全出現置換?}
    Q -->|Yes| R[同一式検索]
    Q -->|No| S[単一箇所のみ]
    R --> T[変数挿入と式置換]
    S --> T
    T --> U[コードクリーンアップ]
    U --> Z[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-36-01 | クエリ変数 | クエリ式内の式はクエリ変数（let句）として抽出 | クエリ内の場合 |
| BR-36-02 | パラメータ定数 | パラメータ初期化子内の式は定数フィールドとして抽出 | パラメータ初期化子内の場合 |
| BR-36-03 | フィールド定数 | 定数式でローカル変数を参照しない場合はフィールド化可能 | 定数で外部参照なしの場合 |
| BR-36-04 | 静的ローカル関数除外 | 静的ローカル関数内の式は全出現置換から除外 | 静的ローカル関数を跨ぐ場合 |
| BR-36-05 | 隠し領域考慮 | 隠し領域を含むブロックは全出現置換から除外 | 隠し領域がある場合 |

### 計算ロジック

アクション種別の決定ロジック：
```
if (InQueryContext) {
    アクション = [クエリ変数（単一）, クエリ変数（全出現）]
} else if (InParameterContext || InAttributeContext) {
    アクション = [定数フィールド（単一）, 定数フィールド（全出現）]
} else if (InFieldContext || InConstructorInitializerContext || InAutoPropertyInitializerContext) {
    アクション = [フィールド（単一）, フィールド（全出現）]
} else if (InBlockContext) {
    if (IsConstant && 外部参照なし && コンテナ生成可能) {
        アクション = [定数フィールド（単一・全出現）]
    }
    if (!隠し領域あり) {
        アクション += [ローカル変数（単一）]
        if (全ブロック隠し領域なし) {
            アクション += [ローカル変数（全出現）]
        }
    }
}
```

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

該当なし（メモリ上のシンボルとソースコードの操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | 検証エラー | 無効な式の選択 | リファクタリングを提供しない |
| N/A | 検証エラー | 置換不可能なコンテキスト | リファクタリングを提供しない |

### リトライ仕様

該当なし（インタラクティブな操作のため）

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

ドキュメント単位での変更を適用。すべての変更が成功するか、すべて適用されないかのいずれかとなる。

## パフォーマンス要件

- セマンティック等価性チェックは必要な範囲に限定
- UIのレスポンシブ性を維持するため、キャンセル可能な操作として実装

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

- ソースコードへの書き込み権限が必要

## 備考

- var使用時は型推論の結果を考慮
- FormattableStringへの暗黙変換がある場合は変換後の型を使用
- 式形式メンバーをブロック形式に変換して変数を挿入可能

---

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

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

### 推奨読解順序

#### Step 1: エントリーポイントとデータ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AbstractIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs` | IIntroduceVariableServiceの実装。IntroduceVariableAsyncがエントリーポイント |

**主要処理フロー**:
1. **62-87行目**: IntroduceVariableAsync - メイン処理
2. **70-72行目**: State.GenerateAsync - 状態生成
3. **73行目**: CreateActions - アクション生成
4. **82行目**: CodeAction.Create - ネストされたアクション生成

#### Step 2: コンテキスト判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AbstractIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs` | AddActionsAndGetTitleメソッド |

**主要処理フロー**:
- **100-182行目**: AddActionsAndGetTitle - コンテキストごとのアクション生成
- **102-107行目**: InQueryContext処理
- **109-114行目**: InParameterContext処理
- **116-121行目**: InFieldContext処理
- **144-161行目**: InBlockContext処理 - 定数フィールドとローカル変数の組み合わせ

#### Step 3: 同一式の検索を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs` | FindMatches、NodeMatchesExpression |

**主要処理フロー**:
- **293-311行目**: FindMatches - 同一式の検索
- **313-345行目**: NodeMatchesExpression - 意味的等価性チェック
- **338-339行目**: 静的ローカル関数の除外処理

#### Step 4: 変数挿入を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AbstractIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs` | GenerateUnique*Name、Rewrite |

**主要処理フロー**:
- **257-274行目**: GenerateUniqueFieldName - フィールド名生成
- **276-291行目**: GenerateUniqueLocalName - ローカル変数名生成
- **347-366行目**: Rewrite - 式を変数参照に置換
- **374-405行目**: GetTypeSymbol - 型シンボルの取得（FormattableString考慮）

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

```
AbstractIntroduceVariableService.IntroduceVariableAsync
    │
    ├─ SemanticDocument.CreateAsync
    │
    ├─ State.GenerateAsync
    │      └─ コンテキスト判定（Query/Parameter/Field/Block等）
    │
    └─ CreateActions
           │
           └─ AddActionsAndGetTitle
                  │
                  ├─ [Query] CreateAction(isQueryLocal: true)
                  ├─ [Parameter/Attribute] CreateAction(isConstant: true, isLocal: false)
                  ├─ [Field/Constructor/AutoProperty] CreateAction(isLocal: false)
                  └─ [Block] CreateConstantFieldActions + CreateAction(isLocal: true)
                         │
                         └─ IntroduceVariableCodeAction
                                │
                                ├─ IntroduceQueryLocal
                                ├─ IntroduceLocal
                                └─ IntroduceFieldAsync
                                       │
                                       ├─ GenerateUniqueName
                                       ├─ FindMatches (allOccurrences時)
                                       │      └─ SemanticEquivalence.AreEquivalent
                                       └─ Rewrite
```

### データフロー図

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

選択された式 ─────────▶ AbstractIntroduceVariableService.IntroduceVariableAsync
                                 │
                                 ▼
                        State.GenerateAsync ───▶ State (コンテキスト情報)
                                 │
                                 ▼
                        AddActionsAndGetTitle ───▶ CodeActionリスト
                                 │
                                 ▼
                        IntroduceVariableCodeAction.GetOperationsAsync
                                 │
                        ┌────────┼────────┐
                        ▼        ▼        ▼
              IntroduceLocal IntroduceField IntroduceQueryLocal
                        │        │        │
                        └────────┼────────┘
                                 ▼
                        FindMatches (allOccurrences時)
                                 │
                                 ▼
                        Rewrite ───▶ 変数挿入 + 式置換
                                 │
                                 ▼
                        更新されたドキュメント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AbstractIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/` | ソース | サービスの基底クラス |
| AbstractIntroduceVariableService.State.cs | `src/Features/Core/Portable/IntroduceVariable/` | ソース | 状態管理 |
| AbstractIntroduceVariableService.CodeAction.cs | `src/Features/Core/Portable/IntroduceVariable/` | ソース | CodeAction実装 |
| IIntroduceVariableService.cs | `src/Features/Core/Portable/IntroduceVariable/` | ソース | サービスインターフェース |
| CSharpIntroduceVariableService.cs | `src/Features/CSharp/Portable/IntroduceVariable/` | ソース | C#固有の実装 |
