コンテンツにスキップ

ADR-32 — オプトインプラグインとしてのインラインRBSコメント取り込み

ステータス: Accepted、2026-05-25; v0.1.10で実装

バンドルされたrigor-rbs-inlineプラグイン、source_rbs_synthesizer:マニフェストフィールド、env構築時の合成フック、マジックコメントゲーティング、require_magic_comment:設定ノブ、ファイル単位のキャッシュ / フェイルソフト処理がすべて出荷された;--treat-all-as-inline-rbs CLIフラグも同じリリースでランディングした。上流のrbs-inlineコメント語彙(# @rbs name: T#: () -> T# @rbs return: T、属性#:など)を消費するという決定を記録する。上流ライブラリをenv構築時に実行し合成RBSを解析環境に貢献するrigor-rbs-inlineプラグインを出荷することで実現する。Rigorのコアはゼロランタイム依存のまま(ADR-0);rbs-inline gemはコアの責任ではなくプラグインの責任になる。

修正2026-05-25 WD10追加 — ホストコンテキストがファイルごとのマジックコメントゲートをスキップできるrequire_magic_comment:プラグイン設定ノブ(デフォルトtrue)。ADR-29ブラウザプレイグラウンドはこれをfalseに設定することで、ユーザーが# rbs_inline: enabledを入力することなく、貼り付けられたスニペットがインラインRBSとして解析される。

以下のようなRubyファイルは

# @rbs asc_or_desc: :asc | :desc
def ascdesc(asc_or_desc)
asc_or_desc
end
p ascdesc :desc
p ascdesc :bad

今日asc_or_descDynamic[Top]として型付けし、ascdesc :badは診断を発しない。原因は明確: Rigorのパイプラインは# @rbsコメントを見ない。lib/spec/examples/のgrepで確認済み;MethodParameterBinderはRBS環境、RBS::Extended %a{rigor:v1:param:}アノテーション、ADR-28プロトコル契約(contract)のみを読む。

下流の機構は既に揃っている。同じ契約を.rbsファイルで再エンコードすると

class AscDesc
def ascdesc: (:asc | :desc) -> (:asc | :desc)
def ascdesc2: (:asc | :desc) -> (:asc | :desc)
end

エンジン変更なしで期待通りの動作が得られる。つまり欠けているリンクは純粋にrbs-inline形コメントから実際の.rbsファイルが埋めるのと同じRBS環境へのパスだけである。

上流のrbs-inline文法は実質的なもので、以下を含む:

  • メソッド型(3形式): #: () -> T# @rbs () -> T、docスタイルの# @rbs name: T + # @rbs return: T
  • ジェネリクス# @rbs generic A)、mixin generics# @rbs inherits# @rbs override
  • ブロック導入型class_methods do...endブロック内の# @rbs class ClassMethods)。
  • 属性attr_reader :name #: String)。
  • インスタンス変数# @rbs @name: String)。
  • 定数VERSION = ... #: String)。
  • エイリアス# @rbs skip# @rbs!生RBS埋め込み# @rbs %a{…}アノテーション
  • ファイルの標準オプトイントリガーとしてのマジックコメント # rbs_inline: enabled;オプトアウトは# rbs_inline: disabled

この文法をRigor内で再実装すると、非自明な上流の努力を複製しすべてのrbs-inlineリリースで分岐するリスクがある。既に整形RBSを出力する上流ライブラリを呼び出す方が、大幅に小さく耐久性の高いエンジニアリング選択である。

しかしRigorのコアはゼロランタイム依存スタンスを表明しており(ADR-0)、rbs-inlineは非自明なgemである(prismrbsに依存するが、それら自体は既に必要;ただし独自のバージョン管理サーフェス(surface)を導入する)。それをコアのランタイム依存として追加することはADR-0と矛盾する。

このADRはADR-25が類似の「envへのRBS貢献」フック用に既に使っているプラグインをオプトイン境界として採用することで緊張を解消する: インラインRBSリーダーはrigor-rbs-inlineプラグインgemに住み、プラグインのgemspecがrbs-inline依存を宣言し、プロジェクトは.rigor.ymlplugins:に1行追加することで動作を有効化する。

plugins/rigor-rbs-inline/以下に新しいバンドルプラグインrigor-rbs-inlineを出荷する:

  1. 独自のgemspec(rigortype gemspecではない)で上流rbs-inline gemへのランタイム依存を宣言する。
  2. 上流の# rbs_inline: enabledマジックコメントをファイルごとのオプトイントリガーとして尊重する——RigorのインラインRBSリーダーは上流rbs-inlineと同じ有効化ルールを持つ。マジックコメントのないファイルは今日と全く同様に処理される。
  3. env構築時に、プロジェクトのチェック対象Rubyファイルを走査し、マジックコメントのある各ファイルに対してrbs-inlineのパーサAPIを呼び出し、合成RBSをsignature_paths:bundler:rbs_collection:が埋めるのと同じ環境に貢献する。

新しいオプションのPlugin::Manifestフィールド、source_rbs_synthesizer:がエンジン側のフックを公開する。プラグインはフィールドをcallable (source_file_path) -> rbs_source_string | nilに設定する。Plugin::LoaderがcallableをプラグインIDごとに記録し、Environment.for_projectがenv構築中にソースファイルごとにそれを呼び出し、結果のRBSストリームをシグネチャ入力の残りとマージする。

WD1 — コア機能ではなくプラグイン

Section titled “WD1 — コア機能ではなくプラグイン”

コアのrigortyperbs-inline取り込みを追加することは却下される。

  • ADR-0はアナライザーをprism / rbs / language_server-protocol以外のゼロランタイム依存にコミットしている。
  • プラグイン境界はオプトインの追加RBS貢献のための確立されたメカニズムである(ADR-25はこれを静的バンドル用に承認;このADRは合成ソースRBSに拡張する)。
  • 有効化は.rigor.ymlの1行——plugins: [rigor-activesupport-core-ext]と同じUX。

WD2 — 上流の# rbs_inline: enabledマジックコメントを尊重する

Section titled “WD2 — 上流の# rbs_inline: enabledマジックコメントを尊重する”

プラグインは最初の非空白行に# rbs_inline: enabledを含むファイルに対してのみRBSを合成する。マジックコメントのないファイルは手つかずに通過する。

  • 上流rbs-inlineとのスペック整合性: 同じファイルをソース変更なしでどちらのツールへの入力としても有効。
  • プロジェクトはインラインRBSファイルとプレーンなRubyファイルを自由に混在させられる。

WD3 — 上流rbs-inlineライブラリを使用する

Section titled “WD3 — 上流rbs-inlineライブラリを使用する”

プラグインはインプロセスでrbs-inlineのパーサAPIを呼び出し(サブプロセスなし)、結果のRBS ASTまたはソーステキストを受け取り、RbsLoaderの合成ソースチャネルに供給する。

WD4 — 新しいsource_rbs_synthesizer:マニフェストフィールド

Section titled “WD4 — 新しいsource_rbs_synthesizer:マニフェストフィールド”

エンジン側のフックは新しいオプションのPlugin::Manifestフィールドで、callable (source_file_path) -> rbs_source_string | nilを保持する。

  • ADR-25のsignature_paths:とは別物。ADR-25はプラグインgem内に住む静的バンドルRBSを出荷し;このADRはenv構築時にアナライズ対象のプロジェクト自身のソースからRBSを合成する。
  • 将来のプラグインが同じフックを他の「Rubyソース → RBS」ブリッジ用に再利用できる(rbs-inlineの語彙ではなくYARD @param / @returnタグを読む仮想的なrigor-yardなど)。

WD5 — 合成はファイルごと;キャッシュキーはソースファイル

Section titled “WD5 — 合成はファイルごと;キャッシュキーはソースファイル”

プラグインはソースファイルごとに一度実行される。ファイルFの合成RBSはFのコンテンツのみの関数——隣接ファイルを見ない。Rigorのファイルごとキャッシュ(ADR-6)が自然なキャッシュレイヤーである: 既存のファイルごとキー(path + sha + Rigorバージョン)が「(path、sha、プラグインID、プラグインバージョン)」に拡張される。

WD6 — 合成エラーのフェイルソフト

Section titled “WD6 — 合成エラーのフェイルソフト”

rbs-inlineがプロジェクトファイルでパースエラーを発生させた場合、プラグインはnilを返し(貢献なし)、ファイルと上流エラーメッセージを名前付きでsource-rbs-synthesis-failedインフォ診断を発行する。解析は続行する——ファイルはノーインラインRBS状態(影響を受ける定義に対するDynamic[Top])にフォールバックし、今日の動作と一致する。

WD7 — インラインRBS宣言のパラメータ型は実際の.rbsと同一に扱われる

Section titled “WD7 — インラインRBS宣言のパラメータ型は実際の.rbsと同一に扱われる”

# @rbs name: :asc | :descで宣言されたパラメータ型はdef f: (:asc | :desc) -> ...と全く同様にパラメータをバインドする。パラメータバインダーは区別しない;診断コードは同じ;ADR-5のロバストネス非対称の著作ルールが同じ境界で適用される。

WD8 — pre-1.0プラグイン契約への加法的追加

Section titled “WD8 — pre-1.0プラグイン契約への加法的追加”

新しいsource_rbs_synthesizer:マニフェストフィールドはオプション。それを宣言しない既存プラグインは影響を受けない。プラグイン契約(ADR-2)はまだpre-1.0;これはADR-25のWD6がカバーする種類の加法的拡張で、v0.1.xライン内に安全に着地できる。v0.2.0が固める契約サーフェスにはこのフィールドを含めるべきである。

WD9 — トップレベルdefの意味論は上流動作に依存する

Section titled “WD9 — トップレベルdefの意味論は上流動作に依存する”

ユーザーの診断例はトップレベルdefを使う。上流rbs-inline wikiはclass / module内のdefを明示的に文書化するが、トップレベルdefは列挙されていない。したがってトップレベルdefに対するプラグインの挙動は「Prismでパースされたトップレベルdefに対してrbs-inlineが行うことそのもの」である——おそらく生成されるObject#…インスタンスメソッドだろうが、スライス1でインストール済みのrbs-inlineバージョンに対して確認する。

回避策は既に存在する: defをクラスにラップする(確認済み——このADRの診断のクラスラップバリアントがargument type mismatchを生成した)。

WD10 — ホストコンテキストオーバーライド: require_magic_comment:プラグイン設定

Section titled “WD10 — ホストコンテキストオーバーライド: require_magic_comment:プラグイン設定”

プラグインは単一のboolean設定キーrequire_magic_comment:(デフォルトtrue、これはWD2をそのまま通常の.rigor.yml駆動プロジェクトに対して保持する)を公開する。分析スコープ全体を所有するホストコンテキストはfalseに設定でき、その場合シンセサイザーが見るすべてのファイルはマジックコメントを持つかのように扱われ、先頭のファイルチェックはない。

ADR-29プレイグラウンドはこれをfalseに設定することで、# @rbs形コメントを持つ貼り付けスニペットがユーザーが# rbs_inline: enabledを入力することなく解析される。プレイグラウンドは単一バッファの単一リクエストの探索サーフェスであり、WD2が緩和しようとしている複数ファイルプロジェクトの摩擦(他のファイルが誤ってオプトインされる)が存在しない。

  • エスケープハッチはプラグインインスタンスごと、プラグイン設定経由——Rigor全体のポリシー変更ではない。
  • プラグインのconfig_schemaがキーを宣言;ユーザーは.rigor.ymlplugins:エントリーの既存プラグイン設定サーフェスを通じて提供する。
  • ユーザーの診断例が1行のアクティベーションで機能するようになるplugins: [rigor-rbs-inline]追加 + # rbs_inline: enabledマジックコメントでascdesc :badが実際の.rbsパスと同じargument-type-mismatchを発生させ、ascdesc2の戻り型が:asc | :descにナローイング(narrowing)される。
  • Rigorは無料で完全なrbs-inline文法を継承する
  • フックが他の「Rubyソース → RBS」プラグインに汎化される。YARDタグリーダー、typeprofブリッジ、カスタムアノテーション規約——各々が独立したプラグインとして出荷できる。
  • 新しいトップレベル設定軸なし。有効化は既存のplugins:リストを再利用。
  • pre-1.0契約に1つの新しいオプションフィールド。加法的で低リスク(WD8)。
  • プラグインgemがユーザーのbundleにrbs-inlineを追加する。コアアナライザーはゼロ依存のまま(WD1)。
  • マジックコメントのあるファイルのファイルごとの呼び出しオーバーヘッド。ファイルごとキャッシュ(WD5)とマジックコメント要件のスコープゲート効果(WD2)で軽減。
  • rigor-rbs-inline著作SKILLはこのADRに含まれない。プラグイン独自のREADME + インラインRBSのハンドブックカバレッジがv1ユーザー向けサーフェス。
  • LSPインクリメンタルフローはこのADRでシンセサイザーフックの周囲に具体的に設計されていない。ファイルごとキャッシュ(WD5)によって明白なインクリメンタルストーリーが機能するが、エンドツーエンドのLSP統合はADR-19 LSPロードマップ下に別途キュー済み。

スライス1 — マニフェストフィールド + エンジンフック + プラグインスケルトン

Section titled “スライス1 — マニフェストフィールド + エンジンフック + プラグインスケルトン”
  • Plugin::Manifestがオプションのsource_rbs_synthesizer:を獲得する(callable;マニフェスト構築時に凍結)。
  • Plugin::LoaderがプラグインIDごとにcallableを記録する。
  • Environment.for_projectがチェック対象ソースファイルを走査し、ファイルごとに登録された各シンセサイザーを呼び出し、非nilのRBS文字列を設定のsignature_paths:と並んでRbsLoaderに供給する。
  • plugins/rigor-rbs-inline/のスキャフォールド: rbs-inlineに依存するgemspec、require_magic_comment:のためのconfig_schemaを持つプラグインクラス(デフォルトtrue、WD10)、マジックコメントを確認するかどうかにconfig knobを参照してRBS::Inline::Parserまたは等価なものにディスパッチするシンセサイザーcallable。

スライス2 — 失敗ハンドリング + キャッシュ

Section titled “スライス2 — 失敗ハンドリング + キャッシュ”
  • シンセサイザーエラーパス: source-rbs-synthesis-failedインフォ診断を発行し、プラグインがnilを返す(WD6)。
  • プラグインID + バージョン + require_magic_comment:値を含むファイルごとキャッシュキー拡張(WD5)。

スライス3 — rigor-rbs-inlineプラグインポリッシュ + ドキュメント

Section titled “スライス3 — rigor-rbs-inlineプラグインポリッシュ + ドキュメント”
  • アクティベーションフロー、# rbs_inline: enabled要件、既知のトップレベルdefキャビアット(WD9)、診断例の再現を文書化したプラグインREADME。
  • 関連する章(おそらく「型ソース」または「RBSの著作」)に1段落とコードサンプルを付けてハンドブックを更新。
  • ADR-25(静的バンドルの先例)からのクロスリファレンス。
  • ADR-0 — プラグイン境界が保持するゼロランタイム依存スタンス(WD1)。
  • ADR-2 — このADRが新しいオプションマニフェストフィールドで拡張するプラグイン契約(WD8)。
  • ADR-5 — ロバストネス非対称の著作;インラインRBSユーザー宣言パラメータは等価な実.rbs宣言と全く同様に扱われる(WD7)。
  • ADR-6 — シンセサイザーフックが拡張するファイルごとキャッシュ(WD5)。
  • ADR-25 — 静的バンドルRBS貢献;ここで使われるプラグイン境界の先例。
  • ADR-29 — ブラウザプレイグラウンド;WD10(require_magic_comment: false)を行使する最初のホストコンテキスト。

© 2026 TypedDuck. Licensed under CC BY-SA 4.0.