| # 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/master/tests/ui/dep-graph/dep-graph-caller-callee.rs |
| [ui]: tests/ui.html |
| |
| 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! |