# 通知設計書 51-非公開シンボル使用警告

## 概要

本ドキュメントは、Julia REPLにおいて非公開（non-public）シンボルが修飾アクセスされた場合に発行される警告通知の設計を記述する。

### 本通知の処理概要

REPLで入力されたコードに対してAST変換を適用し、モジュール修飾アクセス（例: `Mod.name`）が非公開シンボルを参照している場合に、ユーザーへ警告を表示する処理である。

**業務上の目的・背景**：Juliaのモジュールシステムでは、`public` キーワードまたは `export` で明示的に公開されたシンボルのみが安定したAPIとみなされる。非公開シンボルへのアクセスは、将来のバージョンで予告なく変更・削除される可能性がある。この警告は、ユーザーが意図せず非公開APIに依存するコードを書くことを防ぎ、コードの長期的な保守性を向上させるために存在する。

**通知の送信タイミング**：REPLで入力されたコードが評価される前のAST変換段階で、`warn_on_non_owning_accesses` 関数により検査が行われる。修飾アクセス（例: `Module.symbol`）がパースされ、そのシンボルが対象モジュールで `public` でなく、かつシンボルの所有モジュールが対象モジュールのサブモジュールでない場合にトリガーされる。

**通知の受信者**：REPLを使用している開発者本人。ターミナルの標準エラー出力（stderr）を通じて直接表示される。

**通知内容の概要**：警告メッセージには、アクセスされたシンボル名、そのシンボルが定義されているモジュール名、およびアクセス先モジュールで公開されていない旨が含まれる。例: `"minimum is defined in Base and is not public in Iterators"`

**期待されるアクション**：開発者は、非公開シンボルの使用を公開APIに置き換えるか、非公開APIへの依存を認識した上で使用を継続するかを判断する。`maxlog = 1` により同一シンボルに対する警告は1回のみ表示される。

## 通知種別

ログ（Warn） - Julia標準ロギングフレームワーク `@warn` マクロによるコンソール出力

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（REPL AST変換パイプライン内で即座に出力） |
| 優先度 | 低（Warnレベル） |
| リトライ | 無し |

### 送信先決定ロジック

REPLセッション中のコード入力者本人が受信者となる。`@warn` マクロにより現在のグローバルロガーの設定に従いstderrへ出力される。`maxlog = 1` かつ `_id = string("repl-warning-", mod, "-", owner, "-", name)` により、同一のモジュール・シンボルの組み合わせに対する警告は1セッションで1回のみ表示される。

## 通知テンプレート

### メール通知の場合

該当なし（コンソールログ出力のみ）

### 本文テンプレート

```
Warning: {シンボル名} is defined in {定義元モジュール名} and is not public in {アクセス先モジュール名}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| name | アクセスされたシンボル名 | AST解析結果（`name_being_accessed::Symbol`） | Yes |
| owner | シンボルが定義されているモジュール | `which(mod, name_being_accessed)` の返り値 | Yes |
| mod | アクセス先モジュール | AST解析結果（修飾アクセスのベースモジュール） | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| REPL入力 | コード入力後のAST変換フェーズ | 修飾アクセスが非公開シンボルを参照 | `repl_ast_transforms` に登録された `warn_on_non_owning_accesses` が実行される |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `Base.ispublic(mod, name) == true` | シンボルがアクセス先モジュールで公開されている場合 |
| `has_ancestor(owner, mod) == true` | シンボルの所有モジュールがアクセス先モジュールのサブモジュールである場合 |
| `mod === Base && Base.ispublic(Core, name) == true` | BaseモジュールでCoreの公開シンボルにアクセスする場合 |
| `maxlog = 1` | 同一シンボルに対して既に1回警告が表示されている場合 |
| ローカル変数と同名のモジュール | AST上でローカル変数として代入されたシンボルと同名のモジュール修飾アクセスは除外 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[REPLコード入力] --> B[AST変換パイプライン実行]
    B --> C[warn_on_non_owning_accesses呼び出し]
    C --> D[collect_qualified_access_warnings]
    D --> E{修飾アクセスあり?}
    E -->|なし| F[警告なし・終了]
    E -->|あり| G[各アクセスについてチェック]
    G --> H{シンボルは公開?}
    H -->|Yes| F
    H -->|No| I{所有モジュールはサブモジュール?}
    I -->|Yes| F
    I -->|No| J[print_qualified_access_warning呼び出し]
    J --> K[@warn メッセージ出力]
    K --> L[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（データベースは使用しない。Juliaのモジュールシステムのメタデータを参照）

### テーブル別参照項目詳細

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| モジュール解決失敗 | `getproperty(current_module, mod_name)` が例外をスロー | `retrieve_modules` 内で `catch` し `(nothing,)` を返却、警告をスキップ |
| `which` 関数失敗 | シンボルの所有モジュールが特定できない | `collect_names_to_warn!` 内で `catch` し `return`、該当シンボルの警告をスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（ただし `maxlog = 1` により同一シンボルは1回のみ） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（REPLセッション中いつでも発生し得る）

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

本通知は内部APIアクセスに関する情報を含むが、セキュリティ上の機密情報は含まない。モジュール名とシンボル名はJuliaの公開情報である。

## 備考

- この警告は `repl_ast_transforms` 配列に `warn_on_non_owning_accesses` として登録されており、REPLのAST変換パイプラインの一部として実行される（REPL.jl 300行目）
- `maxlog = 1` と固有の `_id` パラメータにより、同一のモジュール・所有者・シンボルの組み合わせに対する警告はセッション中1回のみ表示される
- `_line = nothing _file = nothing _module = nothing` により、警告メッセージからソースの位置情報が除外される

---

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

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

### 推奨読解順序

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

まず、修飾アクセス警告の判定に使われるデータ構造とモジュールシステムの基本を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 182-189行目: `has_ancestor` 関数。モジュールの親子関係を判定するロジック |
| 1-2 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 192-209行目: `retrieve_modules` 関数群。修飾アクセスからモジュールを解決するロジック |

**読解のコツ**: `retrieve_modules` はネストされたモジュールアクセス（`A.B.C`）に対応するため再帰的に処理する。返り値はタプルで、最初がouterモジュール、最後がinnerモジュールとなる。

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

処理の起点となる関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 300行目: `repl_ast_transforms` 配列に `warn_on_non_owning_accesses` が登録されている |
| 2-2 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 291-298行目: `warn_on_non_owning_accesses` がAST変換として呼び出される |

**主要処理フロー**:
1. **300行目**: `repl_ast_transforms` に登録
2. **291行目**: `warn_on_non_owning_accesses(current_mod, ast)` が呼び出される
3. **292行目**: `collect_qualified_access_warnings` で警告対象を収集
4. **293-294行目**: 各警告に対して `print_qualified_access_warning` を呼び出す

#### Step 3: 警告収集ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 224-279行目: `collect_names_to_warn!` 関数。ASTを再帰的に走査し修飾アクセスを検出 |
| 3-2 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 281-289行目: `collect_qualified_access_warnings` 関数。ローカル変数との衝突をフィルタリング |

**主要処理フロー**:
- **230行目**: `Meta.isexpr(ast, :., 2)` で修飾アクセスを検出
- **241-245行目**: `which(mod, name)` でシンボルの所有モジュールを特定
- **248行目**: `has_ancestor(owner, mod)` でサブモジュール判定
- **250行目**: `Base.ispublic(mod, name)` で公開状態チェック

#### Step 4: 警告出力を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | REPL.jl | `stdlib/REPL/src/REPL.jl` | 178-180行目: `print_qualified_access_warning` 関数。`@warn` マクロで警告メッセージを生成 |

**主要処理フロー**:
- **179行目**: `@warn string(name, " is defined in ", owner, " and is not public in ", mod)` で警告を出力。`maxlog = 1` と固有の `_id` で重複抑制。

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

```
REPL AST Transform Pipeline
    |
    +-- warn_on_non_owning_accesses(current_mod, ast)  [REPL.jl:291]
           |
           +-- collect_qualified_access_warnings(current_mod, ast)  [REPL.jl:281]
           |       |
           |       +-- collect_names_to_warn!(warnings, locals, current_module, ast)  [REPL.jl:224]
           |       |       |
           |       |       +-- retrieve_modules(current_module, mod_name)  [REPL.jl:192-209]
           |       |       +-- which(mod, name_being_accessed)  [Base]
           |       |       +-- has_ancestor(owner, mod)  [REPL.jl:182]
           |       |       +-- Base.ispublic(mod, name)  [Base]
           |       |
           |       +-- filter!(warnings)  [REPL.jl:285-287]
           |
           +-- print_qualified_access_warning(mod, owner, name)  [REPL.jl:178]
                   |
                   +-- @warn (Julia logging framework)
```

### データフロー図

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

REPL入力コード(AST) ------> warn_on_non_owning_accesses ------> 変換なしAST(パススルー)
                                    |
                                    +-> collect_qualified_access_warnings
                                    |       |
                                    |       +-> ASTから修飾アクセスを抽出
                                    |       +-> モジュール・シンボル情報を解決
                                    |       +-> 公開状態を判定
                                    |
                                    +-> print_qualified_access_warning
                                            |
                                            +-> @warn メッセージ --> stderr
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| REPL.jl | `stdlib/REPL/src/REPL.jl` | ソース | 警告検出・出力の主要ロジック（178-300行目） |
| precompile.jl | `stdlib/REPL/src/precompile.jl` | ソース | プリコンパイル時に警告を事前トリガー（158行目） |
