診断ポリシー
Rigorは静かな拡幅よりも精密な診断を優先すべきです(SHOULD)。この文書は診断識別子の分類体系、表示規則、抑制マーカー文法を定義します。
推論バジェットが使うカットオフ識別子はstatic.*ファミリーにあります(inference-budgets.md参照)。否定的事実と差分型の表示規則はtype-operators.mdにあります。Dynamic[T]の表示規則はここにあります。
診断ガイドライン
Section titled “診断ガイドライン”- 値として
voidを使うことは一次診断です;下流のリカバリーはtopを使い、同じ式に対して重複するカスケードレポートを避けるべきです(SHOULD)。 - 証明なしに
topのメソッドを呼ぶことは診断です。 - 生の
untypedのメソッドを呼ぶことは許されますが、チェックされていない境界に追跡可能であるべきです(SHOULD)。 Dynamic[T]のメソッドを呼ぶことは静的ファセットTを使う場合があります(MAY)が、診断は証明が動的由来の値に依存していることを説明できるべきです(SHOULD)。- ストリクト動的モードは、動的から精密な代入、引数、戻り値、
Array[Dynamic[top]]のようなジェネリックスロットのリークを報告できます(MAY)。 - ストリクト静的モードはさらに、チェックされた静的事実ではなく動的由来の事実に安全性が依存するメソッド呼び出しやブランチ証明を報告できます(MAY)。
- 否定的事実によってナローイング(narrowing)されたブランチは、それが有用な場合にその事実を表示すべきです(SHOULD)。例:
String - ""または~"foo"。 - 診断は裸の
~"foo"が曖昧になる場合、String - "foo"のような明示的なドメインを持つ表示を優先すべきです(SHOULD)。 - 読み取り専用シェイプ(shape)エントリーを通じた書き込みは、Rigorがその事実を持つとき診断です。
- クローズドキーワードまたはオプションハッシュシェイプへの予期しないキーの渡しは診断です。
- 無効または矛盾する
RBS::Extendedアノテーションは診断です。 - メソッド実装はソースに関係なく受け付けられたシグネチャ契約(contract)に対してチェックされます: インライン
#:、# @rbs、rbs-inlineパラメータアノテーション、生成されたスタブ、および外部.rbs宣言はすべて同じ実装側の力を持ちます。 - 再帰、演算子の曖昧さ、動的ディスパッチ、またはバジェット枯渇のために推論が停止するとき、Rigorはカットオフを報告しなければならず(MUST)、推論された型が精密であるふりをするのではなく、境界契約を提案すべきです(SHOULD)。
- 明示的な名前的パラメータ型が呼び出しを拒否するがメソッド本体がより小さな推論されたケイパビリティ(capability)ロールのみを必要とする場合、Rigorはアドホックなユニオン(union、合併型とも)を追加するよりもインターフェースに公開シグネチャを汎化することを提案できます(MAY)。
- プラグイン、生成済み、または
RBS::Extendedの事実を含む診断は安定した識別子を持つべきです(SHOULD)。公開識別子はソースファミリーを明確にするプレフィックスを使うべきです(SHOULD)(plugin.<plugin-id>.<name>、rbs_extended.<name>、generated.<provider>.<name>など)。一方、内部診断メタデータはより豊富なprovenanceを保持できます(MAY)。 - RBSエクスポート中の精度損失は、ユーザーが説明またはストリクトエクスポートモードを要求したとき報告可能であるべきです(SHOULD)。
識別子分類体系
Section titled “識別子分類体系”診断識別子はプラグイン著者、RBSメタデータ、ユーザーの抑制マーカーが内部の番号付けと衝突せずにアドレス指定できるように階層的です。識別子はメジャーバージョン内で安定しています。新しい診断はどのプレフィックスの下にも追加できます(MAY);名前変更または削除には非推奨ウィンドウが必要です。
| プレフィックス | 使用 |
|---|---|
dynamic.* | untypedとDynamic[T]の境界越境、チェックされていないジェネリックリーク、動的由来に証明が依存するメソッド呼び出し。ADR-10(解析器契約: docs/internal-spec/dependency-source-inference.md)に従ったオプトインGemソース推論パス向けのdynamic.dependency-source.*(例: gem-not-found)を含む。 |
static.* | 不完全推論カットオフを含む、証明に至らない静的チェック |
flow.* | 制御フローのナローイング失敗、等価性と述語のリファインメント問題、事実安定性の違反 |
compat.* | RBS、rbs-inline、Steep互換シグネチャの互換性 |
call.* | メソッド呼び出しサイトの診断: call.undefined-method(メソッドがレシーバーの静的に既知のクラスに定義されていない)、call.unresolved-toplevel(トップレベルの暗黙的self呼び出しが、同一ファイルのdef・pre_eval:パッチ・Kernel / Objectメソッドのいずれにも解決しない、ADR-34)、call.wrong-arity(位置引数の数がどのシグネチャにもマッチしない)、call.argument-type-mismatch(引数がパラメータ契約を証明可能に違反する)、call.possible-nil-receiver(レシーバーが`T |
def.* | メソッド定義の診断。オーバーライドシグネチャ互換性ファミリーdef.override-visibility-reduced / def.override-return-widened / def.override-param-narrowed(ADR-35)を含み、これらはオーバーライドを、プロジェクト定義の先祖から継承するシグネチャに対して検証する。発火するのはオーバーライドと隠された先祖の両方が著者供給のシグネチャを持つときのみ(どちらか一方が推論のみなら沈黙する)で、severity_profile:を通じて深刻度をマップする;リスコフの推論はrobustness-principle.mdにある。 |
rbs_extended.* | RBS::Extendedペイロードの有効性、バージョン互換性、競合レポート |
rbs.coverage.* | RBS環境のカバレッジ/整形式性テレメトリ。rbs.coverage.missing-gemは利用可能なRBSがないロック済みgemを報告する;rbs.coverage.synthesized-namespaceはプロジェクトのsignature_paths: RBSが、囲む名前空間なしに修飾名(class Foo::Bar)を宣言しているものを報告する——これはupstreamでは不正であり(rbs validateが拒否する)、Rigorはシグネチャが依然として解決できるようmoduleを合成する。どちらも:infoで発行する。 |
plugin.<plugin-id>.* | プラグインが貢献した診断 |
generated.<provider>.* | 生成シグネチャプロバイダの診断 |
hint.* | スタイルとリファクタリングの提案、設定でゲート(例: hint.role-generalization.*) |
sig.* | ADR-14に基づくRBSシグネチャ生成器のテレメトリ。sig.generated.new-file / sig.generated.new-method / sig.generated.tighter-return(rigor sig-genコマンドがRBSを生成する際に発行するメソッドごとの分類)と、sig.skipped.complex-shape / sig.skipped.user-authored / sig.skipped.untyped-return(生成器が発行を控えたメソッドごとの理由)を予約する。スライス1のMVPはこれらの識別子を診断ストリームではなくコマンドのJSON出力で公開する。後続のスライスで--writeパスがランディングした際に:info診断として接続する。 |
Dynamic[T]の表示規則
Section titled “Dynamic[T]の表示規則”Dynamic[T]のprovenanceは診断プレフィックスファミリーによってレンダリングされます:
dynamic.*ファミリー外の診断はナローイングされた静的ファセットTを小さなfrom untypedのprovenanceノートと共にレンダリングします。ナローイングされたファセットはユーザーが推論できるものです;ラップされた形式は動的境界自体についてではないメッセージにはノイズを追加するだけです。dynamic.*の診断、およびrigor explainまたは--explainで要求された説明は完全なDynamic[T]形式を表示します。なぜなら、まさにそれが浮上させるために存在する情報だからです。- 内部トレース、キャッシュキー、プラグインの
Scopeクエリはメッセージのレンダリング方法に関係なく常に完全なDynamic[T]形式を保持します。より高い層の診断を構成するために動的ファセットが必要なプラグインはそれを再構築する必要はありません。
ルールは各診断を著者が定めた深刻度(ルール自身のデフォルト)で発行します。診断が結果に届く前に、アクティブな深刻度プロファイルとルールごとのオーバーライドがその深刻度を再スタンプします。抑制パイプラインでは、これはインラインマーカーとベースライン(baseline)の間に位置します: インラインの# rigor:disable → 深刻度解決 → プロジェクトベースライン(ADR-22)。
これを駆動する.rigor.ymlキーは2つあります(ADR-8):
severity_profile:—lenient/balanced(デフォルト)/strictのいずれか。各プロファイルは正規ルールidを深刻度にマップするルールごとのテーブルで、プロファイルは:errorの広さと採用しやすさをトレードオフします(lenientは不確実なルールを:warning/:offに落とし、strictはすべてのルールを:errorに上げます)。アクティブプロファイルのテーブルに存在しないルールは著者が定めた深刻度を保ちます。severity_overrides:—{ rule_id => severity }マップ。キーは正確な正規ルールid(call.undefined-method)か、ファミリーワイルドカード(call)——最初のドット区切りセグメントがキーに等しいすべてのルールにマッチします。
解決された深刻度は:error / :warning / :info / :offのいずれかです;:offは診断を完全にドロップします。Configuration::SeverityProfile.resolveはこの優先順位(高い順)をMUST適用します:
nilのルールidは著者が定めた深刻度を保ちます(参照するものがありません)。- ルールidに対する正確な
severity_overridesエントリー。 - それ以外ではファミリーワイルドカードの
severity_overridesエントリー(ルールidの最初のセグメント)。 - それ以外ではアクティブプロファイルテーブルのルールidに対するエントリー。
- それ以外では著者が定めた深刻度。
未知のseverity_profile:値はbalancedにフォールバックします。この解決が、def.override-*とprotocol_contracts:(ADR-28)ルールの言う「深刻度はseverity_profile:を通じてマップする」の意味です: ルールは固定された著者深刻度で発行し、プロファイルがそれをエラー・警告として表面化させるか、抑制するかを決めます。
抑制マーカー
Section titled “抑制マーカー”Rigorは、特定の診断を1行単位またはファイル全体で抑制するための、ソース内コメント文法を認識します。以下のRigorネイティブのマーカーが出荷済みの表面です。他のエコシステムのマーカーの認識は、設計済みだが未出荷の互換性拡張です。
Rigorネイティブのマーカー
Section titled “Rigorネイティブのマーカー”Rigorネイティブのマーカーは、PHPStanのアノテーションの感覚を踏まえながらアプリケーション側の型DSLを発明しないRubyコメント文法を使います。
- 行形式:
# rigor:disable <rule1>, <rule2>— その物理行で列挙したルールを抑制します。# rigor:disable allはその行のすべてのルールを抑制します。 - ファイルレベル形式(v0.1.2):
# rigor:disable-file <rule1>, <rule2>— ファイル内のすべての行について列挙したルールを抑制します。# rigor:disable-file allはファイル内のすべての診断を抑制します。
ルールリストはカンマ区切りおよび/または空白区切りで、上記のルールIDプレフィックス(call.undefined-method)を使います。リテラルのallキーワードと短い旧来のエイリアスは、rigor explainが使うのと同じ展開を通じて解決されます。ブロックスコープ(start / end)の形式はありません。
インラインマーカーは、設定されたseverity_profile:より前、そしてプロジェクトベースライン(ADR-22、最後の抑制レイヤー)より前に適用されます。運用ガイドはユーザーマニュアル § 「診断」を参照してください。
トークン解決
Section titled “トークン解決”ルールトークン——# rigor:disable[-file]マーカー内、または.rigor.ymlのdisable:リスト内のもの——は、パース時に正規ルールidのセットへ展開されます(resolve_rule_token);行ごと/ファイルごとの抑制マッチは、その後そのセットに対する診断の正規ruleの正確なメンバーシップテストになります。認識されるトークン形は4つです:
all— リテラルのワイルドカード;スコープ内のすべてのルールを抑制します。ルールリストへ展開されるのではなく、センチネルのallとして保たれます。- 旧来の非プレフィックスエイリアス(
undefined-method)——単一の正規id(call.undefined-method)にマップされます。 - ファミリーワイルドカード——診断ファミリー
call/flow/assert/dump/defのいずれか——<family>.配下のすべての正規idに展開されます。 - 正確な正規id(
call.undefined-method)——それ自身として保たれます。
認識されないトークンはそのまま保たれるので、ruleが文字通りその文字列である診断にのみマッチします(実質的にノーオペレーション——妥当性ルールを参照)。ruleがnilの診断は決して抑制されません。ファミリー展開はトークン時に起こるので、マッチ自体は決してプレフィックスマッチングをしません——常に展開された正規idセットに対する正確な等価です。
エコシステム互換マーカー(計画中、未実装)
Section titled “エコシステム互換マーカー(計画中、未実装)”他のエコシステムのマーカーの認識 — Steepの行スコープの# steep:ignore、および.rigor.ymlのcompat.*スイッチによるオプトインのSorbet # typed: / RuboCop # rubocop:disable — は、設計済みだが未出荷の互換性表面です。それがランディングするまでは、上記のRigorネイティブのマーカーのみが尊重され、外来のマーカーは通常のコメントとして扱われます。
- 未知または空のマーカーは、現在のところフラグされるのではなく通常のコメントとして扱われます(黙って無視されます)。死んだ(未知ルールの)抑制をリファクタリング中に浮上させるために警告することは、計画中の改良です。
© 2026 TypedDuck. Licensed under CC BY-SA 4.0.