130 lines
4.5 KiB
Markdown
130 lines
4.5 KiB
Markdown
# 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!
|