Navigate the graph
The navigation commands answer relationship questions directly from the typed symbol graph. You pass the symbol name without looking it up first.
greppy who-calls visit_content_map # who calls this function
greppy callees deserialize_body # what it calls
greppy brief expand_derive_deserialize # what it does, its body sketched, its callers
greppy impact Ctxt::error_spanned_by # what breaks if I change it
greppy path --from derive_deserialize --to deserialize_body # every call chain between two symbols
All examples on this page run on serde 1.0.229.
Symbols and results
A symbol is a function, a method, a class or a type. When a name is defined more than once, the command lists the definitions instead of an answer and exits with code 1:
$ greppy who-calls from_ast
`from_ast` is 2 definitions
serde_derive/src/internals/ast.rs:62
serde_derive/src/internals/attr.rs:237
Qualify the name with its file, relative to the repository root:
$ greppy who-calls serde_derive/src/internals/ast.rs::from_ast
serde_derive/src/de.rs:29 expand_derive_deserialize
serde_derive/src/ser.rs:17 expand_derive_serialize
greppy also accepts Type::method and the fully qualified names that other commands print. greppy who-calls Field::from_ast and greppy who-calls serde_derive/src/internals/attr.rs::Field::from_ast return the same answer, serde_derive/src/internals/ast.rs:212 fields_from_ast.
A result line has the form file:line name. A trailing test marks a result that lives in a test. A sentence after an em dash is a generated navigation hint, not source.
who-calls
who-calls finds every place that uses the symbol: calls, imports and type references, from incoming CALLS and USAGE edges. A long answer starts with the count per file and shows the first lines:
$ greppy who-calls Ctxt::error_spanned_by
27 callers: serde_derive/src/ser.rs 1, serde_derive/src/de.rs 2, serde_derive/src/internals/ast.rs 2, serde_derive/src/internals/check.rs 10, serde_derive/src/internals/attr.rs 12
serde_derive/src/ser.rs:73 precondition
serde_derive/src/de.rs:93 precondition_sized
serde_derive/src/de.rs:106 precondition_no_de_lifetime
serde_derive/src/internals/ast.rs:80 Container::from_ast
serde_derive/src/internals/ast.rs:166 enum_from_ast
Callers in tests carry the test marker:
$ greppy who-calls test_suite/tests/test_de.rs::test --path test_suite/tests/test_de.rs
94 callers: test_suite/tests/test_de.rs 94
test_suite/tests/test_de.rs:216 test_bool test
test_suite/tests/test_de.rs:241 test_i8 test
…
who-calls answers several symbols in one call and groups the answer by symbol. With --code, it prints the whole source span of each caller, followed by a handle: line for a later edit. greppy shortens long spans with … (truncated, N more line(s)):
$ greppy who-calls visit_content_seq visit_content_map --code
visit_content_seq
serde/src/private/de.rs:1126 ContentDeserializer::deserialize_any
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
…
serde/src/private/de.rs:1365 ContentDeserializer::deserialize_seq
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
match self.content {
Content::Seq(v) => visit_content_seq(v, visitor),
_ => Err(self.invalid_type(&visitor)),
}
}
…
visit_content_map
…
… 1 more — greppy who-calls visit_content_seq visit_content_map --code --offset 6
The last line names the command that continues the answer.
callees
callees returns the indexed definitions the symbol calls directly (outgoing CALLS edges), with the location of their definition:
$ greppy callees deserialize_body
serde_derive/src/de/enum_.rs:15 deserialize
serde_derive/src/de/identifier.rs:14 deserialize_custom
serde_derive/src/de/struct_.rs:17 deserialize
serde_derive/src/de/tuple.rs:13 deserialize
serde_derive/src/de/unit.rs:8 deserialize
serde_derive/src/de.rs:383 deserialize_transparent
serde_derive/src/de.rs:426 deserialize_from
serde_derive/src/de.rs:435 deserialize_try_from
External and unresolved calls are not in the list, so an empty answer does not prove that the code calls nothing. Like who-calls, callees takes several symbols in one call.
brief
brief is the orientation aid. It prints in one sentence what the symbol does, then its signature and its body as a sketch: one line per step, with the symbol used there. The callers follow and, when the call tree below is worth it, an expand offer:
$ greppy brief expand_derive_deserialize
Expands a syn::DeriveInput into a TokenStream.
serde_derive/src/de.rs:25
pub fn expand_derive_deserialize(input: &mut syn::DeriveInput) -> syn::Result<TokenStream> {
26 replace_receiver — rewrites a DeriveInput by replacing its receiver type
28 Ctxt::new — creates a new Ctxt with an empty errors vector
29 Container::from_ast — converts a syn::DeriveInput into a Container, handling enums and structs
31 Err
33 precondition — runs two precondition checks on cont
37 Parameters::new — constructs a Parameters struct from a Container
39 Stmts
39 deserialize_body — dispatches to the matching deserialize function based on the container's data type
41 allow_deprecated — returns a TokenStream with an allow(deprecated) attribute when enabled
45 pretend_used — generates TokenStream from Container's pretend fields and variants
60 deserialize_in_place_body — returns None unconditionally, ignoring its inputs
78 wrap_in_const — wraps serde code in a const attribute
}
called by derive_deserialize
expand be76477a311cb85b — the call tree below expand_derive_deserialize sketched, 100 functions, 1186 lines
greppy expand be76477a311cb85b prints the sketched call tree in one call. Structs, enums and traits have no body to sketch, so brief prints their whole definition:
$ greppy brief Ctxt
A type to collect errors together and format them. Dropping this object will cause a panic. It must be consumed using `check`. References can be shared since this type uses run-time exclusive mut checking.
serde_derive/src/internals/ctxt.rs:11-16
#[derive(Default)]
pub struct Ctxt {
// The contents will be set to `None` during checking. This is so that checking can be
// enforced.
errors: RefCell<Option<Vec<syn::Error>>>,
}
You brief several symbols in one call, in the order you give them: greppy brief deserialize_body expand_derive_deserialize.
impact
impact shows the transitive blast radius in one call, as a tree in which every line says what it does. Tests are marked, and (above) points to a node that already appears further up:
$ greppy impact Ctxt::error_spanned_by
serde_derive/src/de.rs:89 precondition_sized — reports an error when a struct's last field is a slice
serde_derive/src/de.rs:84 precondition — runs two precondition checks on cont
serde_derive/src/de.rs:25 expand_derive_deserialize — expands a syn::DeriveInput into a TokenStream
serde_derive/src/lib.rs:122 derive_deserialize — derives deserialize code from a TokenStream
serde_derive/src/de.rs:102 precondition_no_de_lifetime — reports an error when the container contains a de lifetime parameter
serde_derive/src/de.rs:84 precondition (above)
serde_derive/src/internals/ast.rs:62 Container::from_ast — converts a syn::DeriveInput into a Container, handling enums and structs
serde_derive/src/de.rs:25 expand_derive_deserialize (above)
serde_derive/src/ser.rs:13 expand_derive_serialize — expands a syn::DeriveInput into a TokenStream of serialize methods
serde_derive/src/lib.rs:114 derive_serialize — derives serialization from a TokenStream
…
… 22 more reached — greppy impact Ctxt::error_spanned_by --all
greppy impact Ctxt::error_spanned_by # --direction incoming is the default
greppy impact deserialize_body --direction outgoing # what deserialize_body ultimately reaches
greppy impact deserialize_body --depth 3 --edge CALLS
$ greppy impact deserialize_body --depth 3 --edge CALLS
serde_derive/src/de.rs:25 expand_derive_deserialize — expands a syn::DeriveInput into a TokenStream
serde_derive/src/lib.rs:122 derive_deserialize — derives deserialize code from a TokenStream
| Flag | Effect |
|---|---|
--direction incoming |
transitive callers: what breaks if you change the symbol (default) |
--direction outgoing |
what the symbol ultimately reaches |
--depth N |
maximum distance in hops (default 6) |
--edge TYPE |
follow one edge type; incoming follows all reference edges by default, outgoing follows CALLS |
Use impact instead of walking who-calls by hand, level by level.
path
path shows every call chain from A to B as one tree of the call sites they hang on. The start appears at its definition. Each indented child is the site in its parent that calls the next symbol:
$ greppy path --from derive_deserialize --to deserialize_body
serde_derive/src/lib.rs:122 derive_deserialize
serde_derive/src/lib.rs:124 expand_derive_deserialize
serde_derive/src/de.rs:39 deserialize_body
path prints branching chains as one shared tree:
$ greppy path --from expand_derive_deserialize --to Ctxt::error_spanned_by
serde_derive/src/de.rs:25 expand_derive_deserialize
serde_derive/src/de.rs:29 Container::from_ast
serde_derive/src/internals/ast.rs:68 Container::from_ast
serde_derive/src/internals/attr.rs:350 parse_lit_into_expr_path
serde_derive/src/internals/attr.rs:1481 get_lit_str
serde_derive/src/internals/attr.rs:1416 get_lit_str2
serde_derive/src/internals/attr.rs:1444 Ctxt::error_spanned_by
serde_derive/src/internals/attr.rs:1488 Ctxt::error_spanned_by
serde_derive/src/internals/attr.rs:380 Ctxt::error_spanned_by
…
at least 8 paths shown
--edge takes CALLS (default), USAGE, TYPE_ASSIGN or IMPORTS. path is a bounded tree of call sites and rejects --code with exit code 64:
$ greppy path --from derive_deserialize --to deserialize_body --code
`path` prints the bounded call-site chain and does not accept `--code`; run it without `--code`, then use `greppy read SYMBOL` for a returned definition body
usage: greppy path --from SYMBOL --to SYMBOL [--root DIR]
You read the body of a returned symbol with greppy read.
Orientation and lookup
| Command | Answers with |
|---|---|
greppy where-am-i |
the repository at a glance: layout, languages, entry points, test roots, each module with its most used symbols |
greppy graph-locate src/lib.rs:42 |
the innermost symbol that encloses a file:line location; --file F --line N works as well |
greppy fan-in / greppy fan-out |
the most-called and the most-calling symbols; --edge picks the edge type (default CALLS) |
greppy trace --symbol S |
a trace through the call graph; --direction outgoing (default) or incoming, --edge, --depth (default 4) |
greppy search-graph --name NAME |
a structured graph query |
The first line of where-am-i names the path, the languages and the number of files and definitions, for serde rust, toml, markdown, 339 files, 3,908 definitions. After that comes each module with its most used symbols and an expand offer:
$ greppy where-am-i
…
test_suite/ 271 files 1,949 defs — greppy expand da6c1ac451c40b4e
serde_core/ 22 files 735 defs — Type::Error, Deserializer, Serializer — greppy expand 362eb3329926c8b5
serde/ 7 files 696 defs — Type::Ok, Unsupported, Content — greppy expand 5618d48721fcf976
serde_derive/ 30 files 527 defs — Fragment, Ctxt, Container — greppy expand 26946d64ddc27ba5
serde_derive_internals/ 3 files 1 def — greppy expand c074986cbec47118
6 further files hold no definitions
docs: 4 files, 12 sections · config: 7 files, 169 keys
entry points: serde/build.rs, serde_core/build.rs, serde_derive/build.rs, serde_derive_internals/build.rs, test_suite/no_std/src/main.rs … 118 more — greppy expand 982c5556472e8a2e
tests: inline #[test] modules, test_suite/tests/
$ greppy graph-locate serde/src/private/de.rs:1105
Function serde/src/private/de.rs::Function::visit_content_map serde/src/private/de.rs:1103-1115 match=enclosing
Output flags
| Flag | Effect |
|---|---|
--code |
also prints source: for who-calls the whole span of each caller, for callees the definition of each callee, each with an edit handle. brief and impact accept the flag and print the same answer |
--all |
every result instead of the first few |
--json |
the same answer as data, with exact counts |
--path P |
results under this file or directory; repeatable. The flag filters the results and does not pick an ambiguous target symbol |
--limit N, --offset K |
page through a --json answer: at most N results, starting at the Kth. --offset also continues a truncated text answer |
--root DIR |
works on another repository |
$ greppy who-calls Ctxt::error_spanned_by --path serde_derive/src/de.rs
serde_derive/src/de.rs:93 precondition_sized
serde_derive/src/de.rs:106 precondition_no_de_lifetime
Chain commands
Every command also takes its input from the pipe, so a result goes straight into the next command:
$ greppy callees visit_content_map --json | greppy read -
serde/src/private/de.rs:1608-1615 MapDeserializer::new
fn new(content: Vec<(Content<'de>, Content<'de>)>) -> Self {
MapDeserializer {
iter: content.into_iter(),
value: None,
count: 0,
error: PhantomData,
}
}
Empty and unanswerable questions
A question with an empty answer, such as an existing symbol without callers, says so and exits 0. A question that cannot be answered, such as an unknown symbol or an ambiguous name, says why and exits with a non-zero code:
$ greppy who-calls derive_deserialize # exit code 0
no callers
$ greppy who-calls deserialize_json_config # exit code 1
no symbol `deserialize_json_config`
derive_deserialize is a good example of the limit of every static graph: the compiler calls the function through #[proc_macro_derive(Deserialize, attributes(serde))], and no Rust code in the repository does. An empty graph answer therefore does not prove that a relationship is missing. Reflection, dependency injection, dynamic dispatch and generated code lie outside every static graph. Switch methods with search or search-pattern before you draw a conclusion. Read more in Limits.