| # Debugging and testing dependencies | |
| ## Testing the dependency graph | |
| There are various ways to write tests against the dependency graph. | |
| The simplest mechanisms are the `#[rustc_if_this_changed]` and | |
| `#[rustc_then_this_would_need]` annotations. | |
| These are used in [ui] tests to test | |
| whether the expected set of paths exist in the dependency graph. | |
| [`tests/ui/dep-graph/dep-graph-caller-callee.rs`]: https://github.com/rust-lang/rust/blob/HEAD/tests/ui/dep-graph/dep-graph-caller-callee.rs | |
| [ui]: tests/ui.md | |
| As an example, see [`tests/ui/dep-graph/dep-graph-caller-callee.rs`], or the tests below. | |
| ```rust,ignore | |
| #[rustc_if_this_changed] | |
| fn foo() { } | |
| #[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK | |
| fn bar() { foo(); } | |
| ``` | |
| This should be read as | |
| > If this (`foo`) is changed, then this (i.e. `bar`)'s TypeckTables would need to be changed. | |
| Technically, what occurs is that the test is expected to emit the string "OK" on | |
| stderr, associated to this line. | |
| You could also add the lines | |
| ```rust,ignore | |
| #[rustc_then_this_would_need(TypeckTables)] //~ ERROR no path | |
| fn baz() { } | |
| ``` | |
| Whose meaning is | |
| > If `foo` is changed, then `baz`'s TypeckTables does not need to be changed. | |
| > The macro must emit an error, and the error message must contains "no path". | |
| Recall that the `//~ ERROR OK` is a comment from the point of view of the Rust | |
| code we test, but is meaningful from the point of view of the test itself. | |
| ## Debugging the dependency graph | |
| ### Dumping the graph | |
| The compiler is also capable of dumping the dependency graph for your debugging pleasure. | |
| To do so, pass the `-Z dump-dep-graph` flag. | |
| The graph will be dumped to `dep_graph.{txt,dot}` in the current | |
| directory. | |
| You can override the filename with the `RUST_DEP_GRAPH` environment variable. | |
| Frequently, though, the full dep graph is quite overwhelming and not particularly helpful. | |
| Therefore, the compiler also allows you to filter the graph. | |
| You can filter in three ways: | |
| 1. All edges originating in a particular set of nodes (usually a single node). | |
| 2. All edges reaching a particular set of nodes. | |
| 3. All edges that lie between given start and end nodes. | |
| To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should | |
| look like one of the following: | |
| ```text | |
| source_filter // nodes originating from source_filter | |
| -> target_filter // nodes that can reach target_filter | |
| source_filter -> target_filter // nodes in between source_filter and target_filter | |
| ``` | |
| `source_filter` and `target_filter` are a `&`-separated list of strings. | |
| A node is considered to match a filter if all of those strings appear in its label. | |
| So, for example: | |
| ```text | |
| RUST_DEP_GRAPH_FILTER='-> TypeckTables' | |
| ``` | |
| would select the predecessors of all `TypeckTables` nodes. | |
| Usually though you want the `TypeckTables` node for some particular fn, so you might write: | |
| ```text | |
| RUST_DEP_GRAPH_FILTER='-> TypeckTables & bar' | |
| ``` | |
| This will select only the predecessors of `TypeckTables` nodes for functions | |
| with `bar` in their name. | |
| Perhaps you are finding that when you change `foo` you need to re-type-check | |
| `bar`, but you don't think you should have to. | |
| In that case, you might do: | |
| ```text | |
| RUST_DEP_GRAPH_FILTER='Hir & foo -> TypeckTables & bar' | |
| ``` | |
| This will dump out all the nodes that lead from `Hir(foo)` to | |
| `TypeckTables(bar)`, from which you can (hopefully) see the source of the erroneous edge. | |
| ### Tracking down incorrect edges | |
| Sometimes, after you dump the dependency graph, you will find some | |
| path that should not exist, but you will not be quite sure how it came to be. | |
| **When the compiler is built with debug assertions,** it can help you track that down. | |
| Simply set the `RUST_FORBID_DEP_GRAPH_EDGE` environment variable to a filter. | |
| Every edge created in the dep-graph | |
| will be tested against that filter – if it matches, a `bug!` is | |
| reported, so you can easily see the backtrace (`RUST_BACKTRACE=1`). | |
| The syntax for these filters is the same as described in the previous section. | |
| However, note that this filter is applied to every **edge** | |
| and doesn't handle longer paths in the graph, unlike the previous section. | |
| Example: | |
| You find that there is a path from the `Hir` of `foo` to the type | |
| check of `bar` and you don't think there should be. | |
| You dump the dep-graph as described in the previous section and open `dep-graph.txt` | |
| to see something like: | |
| ```text | |
| Hir(foo) -> Collect(bar) | |
| Collect(bar) -> TypeckTables(bar) | |
| ``` | |
| That first edge looks suspicious to you. | |
| So you set `RUST_FORBID_DEP_GRAPH_EDGE` to `Hir&foo -> Collect&bar`, re-run, and | |
| then observe the backtrace. | |
| Voila, bug fixed! |