依存関係のデバッグとテスト
依存グラフのテスト
依存グラフに対してテストを書く方法はいくつかあります。最も単純な仕組みは、#[rustc_if_this_changed] と #[rustc_then_this_would_need] アノテーションです。これらは ui テストで、期待されるパスの集合が依存グラフ内に存在するかどうかをテストするために使われます。
例として、tests/ui/dep-graph/dep-graph-caller-callee.rs、または以下のテストを参照してください。
#[rustc_if_this_changed]
fn foo() { }
#[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK
fn bar() { foo(); }
これは次のように読むべきです。
これ(
foo)が変更された場合、この(つまりbar)の TypeckTables を変更する必要がある。
技術的には、このテストは、この行に関連付けられて stderr に文字列 “OK” を出力することが期待されています。
次の行を追加することもできます。
#[rustc_then_this_would_need(TypeckTables)] //~ ERROR no path
fn baz() { }
その意味は次のとおりです。
fooが変更された場合、bazの TypeckTables を変更する必要はない。 マクロはエラーを出力しなければならず、そのエラーメッセージには “no path” が含まれていなければならない。
//~ ERROR OK は、テスト対象の Rust コードの観点からはコメントですが、テスト自体の観点からは意味を持つことを思い出してください。
依存グラフのデバッグ
グラフのダンプ
コンパイラは、デバッグのために依存グラフをダンプすることもできます。そのためには、-Z dump-dep-graph フラグを渡します。グラフは現在のディレクトリの dep_graph.{txt,dot} にダンプされます。RUST_DEP_GRAPH 環境変数でファイル名を上書きできます。
ただし、多くの場合、完全な依存グラフは非常に圧倒的で、特に役立つものではありません。そのため、コンパイラではグラフをフィルタリングすることもできます。フィルタリングには 3 つの方法があります。
- 特定のノード集合(通常は単一ノード)から始まるすべてのエッジ。
- 特定のノード集合に到達するすべてのエッジ。
- 指定された開始ノードと終了ノードの間にあるすべてのエッジ。
フィルタリングするには、RUST_DEP_GRAPH_FILTER 環境変数を使用します。これは次のいずれかの形式である必要があります。
source_filter // source_filter から始まるノード
-> target_filter // target_filter に到達できるノード
source_filter -> target_filter // source_filter と target_filter の間にあるノード
source_filter と target_filter は、& で区切られた文字列のリストです。ノードは、それらの文字列がすべてそのラベルに含まれている場合にフィルタに一致するとみなされます。したがって、たとえば次のようにします。
RUST_DEP_GRAPH_FILTER='-> TypeckTables'
これは、すべての TypeckTables ノードの先行ノードを選択します。ただし通常は、特定の fn に対する TypeckTables ノードが必要なので、次のように書くことがあります。
RUST_DEP_GRAPH_FILTER='-> TypeckTables & bar'
これは、名前に bar を含む関数の TypeckTables ノードの先行ノードのみを選択します。
おそらく、foo を変更したときに bar を再度型チェックする必要があることに気づいたものの、そうする必要はないはずだと考えているかもしれません。その場合は、次のようにすることがあります。
RUST_DEP_GRAPH_FILTER='Hir & foo -> TypeckTables & bar'
これにより、Hir(foo) から TypeckTables(bar) へつながるすべてのノードがダンプされ、そこから誤ったエッジの原因を(うまくいけば)確認できます。
不正なエッジの追跡
依存グラフをダンプしたあと、本来存在すべきでないパスを見つけることがありますが、それがどのようにしてできたのかはよく分からないかもしれません。コンパイラがデバッグアサーション付きでビルドされている場合、 それを追跡するのに役立ちます。単に RUST_FORBID_DEP_GRAPH_EDGE 環境変数にフィルタを設定してください。依存グラフ内で作成されるすべてのエッジがそのフィルタに対してテストされます。一致した場合は bug! が報告されるため、バックトレースを簡単に確認できます(RUST_BACKTRACE=1)。
これらのフィルタの構文は、前のセクションで説明したものと同じです。ただし、前のセクションとは異なり、このフィルタはすべてのエッジに適用され、グラフ内のより長いパスは扱わないことに注意してください。
例:
foo の Hir から bar の型チェックへのパスが存在することが分かり、それは存在すべきではないと考えているとします。前のセクションで説明したように依存グラフをダンプし、dep-graph.txt を開くと、次のような内容が表示されます。
Hir(foo) -> Collect(bar)
Collect(bar) -> TypeckTables(bar)
この最初のエッジは疑わしく見えます。そこで、RUST_FORBID_DEP_GRAPH_EDGE を Hir&foo -> Collect&bar に設定し、再実行してからバックトレースを確認します。これでバグ修正完了です!