|
| 1 | +# Debugging and Testing Dependencies |
| 2 | + |
| 3 | +## Testing the dependency graph |
| 4 | + |
| 5 | +There are various ways to write tests against the dependency graph. |
| 6 | +The simplest mechanisms are the `#[rustc_if_this_changed]` and |
| 7 | +`#[rustc_then_this_would_need]` annotations. These are used in compile-fail |
| 8 | +tests to test whether the expected set of paths exist in the dependency graph. |
| 9 | +As an example, see `src/test/compile-fail/dep-graph-caller-callee.rs`. |
| 10 | + |
| 11 | +The idea is that you can annotate a test like: |
| 12 | + |
| 13 | +```rust |
| 14 | +#[rustc_if_this_changed] |
| 15 | +fn foo() { } |
| 16 | + |
| 17 | +#[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK |
| 18 | +fn bar() { foo(); } |
| 19 | + |
| 20 | +#[rustc_then_this_would_need(TypeckTables)] //~ ERROR no path |
| 21 | +fn baz() { } |
| 22 | +``` |
| 23 | + |
| 24 | +This will check whether there is a path in the dependency graph from `Hir(foo)` |
| 25 | +to `TypeckTables(bar)`. An error is reported for each |
| 26 | +`#[rustc_then_this_would_need]` annotation that indicates whether a path |
| 27 | +exists. `//~ ERROR` annotations can then be used to test if a path is found (as |
| 28 | +demonstrated above). |
| 29 | + |
| 30 | +## Debugging the dependency graph |
| 31 | + |
| 32 | +### Dumping the graph |
| 33 | + |
| 34 | +The compiler is also capable of dumping the dependency graph for your |
| 35 | +debugging pleasure. To do so, pass the `-Z dump-dep-graph` flag. The |
| 36 | +graph will be dumped to `dep_graph.{txt,dot}` in the current |
| 37 | +directory. You can override the filename with the `RUST_DEP_GRAPH` |
| 38 | +environment variable. |
| 39 | + |
| 40 | +Frequently, though, the full dep graph is quite overwhelming and not |
| 41 | +particularly helpful. Therefore, the compiler also allows you to filter |
| 42 | +the graph. You can filter in three ways: |
| 43 | + |
| 44 | +1. All edges originating in a particular set of nodes (usually a single node). |
| 45 | +2. All edges reaching a particular set of nodes. |
| 46 | +3. All edges that lie between given start and end nodes. |
| 47 | + |
| 48 | +To filter, use the `RUST_DEP_GRAPH_FILTER` environment variable, which should |
| 49 | +look like one of the following: |
| 50 | + |
| 51 | +``` |
| 52 | +source_filter // nodes originating from source_filter |
| 53 | +-> target_filter // nodes that can reach target_filter |
| 54 | +source_filter -> target_filter // nodes in between source_filter and target_filter |
| 55 | +``` |
| 56 | + |
| 57 | +`source_filter` and `target_filter` are a `&`-separated list of strings. |
| 58 | +A node is considered to match a filter if all of those strings appear in its |
| 59 | +label. So, for example: |
| 60 | + |
| 61 | +``` |
| 62 | +RUST_DEP_GRAPH_FILTER='-> TypeckTables' |
| 63 | +``` |
| 64 | + |
| 65 | +would select the predecessors of all `TypeckTables` nodes. Usually though you |
| 66 | +want the `TypeckTables` node for some particular fn, so you might write: |
| 67 | + |
| 68 | +``` |
| 69 | +RUST_DEP_GRAPH_FILTER='-> TypeckTables & bar' |
| 70 | +``` |
| 71 | + |
| 72 | +This will select only the predecessors of `TypeckTables` nodes for functions with |
| 73 | +`bar` in their name. |
| 74 | + |
| 75 | +Perhaps you are finding that when you change `foo` you need to re-type-check `bar`, |
| 76 | +but you don't think you should have to. In that case, you might do: |
| 77 | + |
| 78 | +``` |
| 79 | +RUST_DEP_GRAPH_FILTER='Hir & foo -> TypeckTables & bar' |
| 80 | +``` |
| 81 | + |
| 82 | +This will dump out all the nodes that lead from `Hir(foo)` to |
| 83 | +`TypeckTables(bar)`, from which you can (hopefully) see the source |
| 84 | +of the erroneous edge. |
| 85 | + |
| 86 | +### Tracking down incorrect edges |
| 87 | + |
| 88 | +Sometimes, after you dump the dependency graph, you will find some |
| 89 | +path that should not exist, but you will not be quite sure how it came |
| 90 | +to be. **When the compiler is built with debug assertions,** it can |
| 91 | +help you track that down. Simply set the `RUST_FORBID_DEP_GRAPH_EDGE` |
| 92 | +environment variable to a filter. Every edge created in the dep-graph |
| 93 | +will be tested against that filter -- if it matches, a `bug!` is |
| 94 | +reported, so you can easily see the backtrace (`RUST_BACKTRACE=1`). |
| 95 | + |
| 96 | +The syntax for these filters is the same as described in the previous |
| 97 | +section. However, note that this filter is applied to every **edge** |
| 98 | +and doesn't handle longer paths in the graph, unlike the previous |
| 99 | +section. |
| 100 | + |
| 101 | +Example: |
| 102 | + |
| 103 | +You find that there is a path from the `Hir` of `foo` to the type |
| 104 | +check of `bar` and you don't think there should be. You dump the |
| 105 | +dep-graph as described in the previous section and open `dep-graph.txt` |
| 106 | +to see something like: |
| 107 | + |
| 108 | + Hir(foo) -> Collect(bar) |
| 109 | + Collect(bar) -> TypeckTables(bar) |
| 110 | + |
| 111 | +That first edge looks suspicious to you. So you set |
| 112 | +`RUST_FORBID_DEP_GRAPH_EDGE` to `Hir&foo -> Collect&bar`, re-run, and |
| 113 | +then observe the backtrace. Voila, bug fixed! |
0 commit comments