1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Symbol-level call-chain output contracts.
use std::path::PathBuf;
use serde::Serialize;
use crate::serde_path;
/// Default chain depth when `--depth` is unset.
pub const DEFAULT_TRACE_DEPTH: u32 = 2;
/// Which directions to walk.
#[derive(Debug, Clone, Copy)]
pub struct TraceDirections {
/// Walk up to callers.
pub callers: bool,
/// Walk down to callees.
pub callees: bool,
}
/// The result of a symbol-level call-chain trace. Its own surface (`kind:
/// "trace"`), NOT folded into the ranked brief.
#[derive(Debug, Clone, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct SymbolChainTrace {
/// The file containing the traced symbol (project-root-relative).
#[serde(serialize_with = "serde_path::serialize")]
pub file: PathBuf,
/// The traced symbol name.
pub symbol: String,
/// Whether the symbol's defining export was found in the graph. When
/// `false`, the chains are empty and `reason` explains why.
pub symbol_found: bool,
/// The chain depth applied to both directions.
pub depth: u32,
/// Whether this trace is best-effort (always `true`: symbol-level chains are
/// labeled best-effort, syntactic per ADR-001).
pub best_effort: bool,
/// Caller chain hops (UP). Present only when `--callers` was requested.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub callers: Option<Vec<ChainHop>>,
/// Callee chain hops (DOWN) resolved to an import-symbol edge. Present only
/// when `--callees` was requested.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub callees: Option<Vec<ChainHop>>,
/// Callees referenced at a call site in the symbol's module that the
/// syntactic walk could NOT resolve to an import-symbol edge (locals,
/// globals, dynamic dispatch, re-bound callees). Reported, never dropped.
/// Present only when `--callees` was requested.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub unresolved_callees: Option<Vec<UnresolvedCallee>>,
/// A human-readable summary of the trace outcome.
pub reason: String,
}
/// One hop in a caller / callee chain.
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct ChainHop {
/// The file at this hop (project-root-relative). For a caller hop this is
/// the importing module; for a callee hop the imported module.
#[serde(serialize_with = "serde_path::serialize")]
pub file: PathBuf,
/// The symbol name as imported across the edge (`default`, `*` for namespace,
/// the imported name otherwise).
pub imported_as: String,
/// The local binding name in the file at this hop.
pub local_name: String,
/// Whether the import edge is type-only (`import type { ... }`).
pub type_only: bool,
/// The hop's depth (1 = direct caller/callee of the symbol).
pub depth: u32,
}
/// A callee referenced at a call site that did not resolve to an import-symbol
/// edge. Surfaced so a missing callee is never silently dropped.
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct UnresolvedCallee {
/// The callee path as written at the call site (e.g. `helper`,
/// `obj.method`).
pub callee: String,
/// Why it is unresolved (best-effort classification).
pub reason: UnresolvedReason,
}
/// Best-effort classification of why a callee did not resolve to an edge.
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "kebab-case")]
pub enum UnresolvedReason {
/// A bare identifier call with no matching import binding (a same-module
/// local function, a global, or a re-bound callee).
LocalOrGlobal,
/// A computed / member-expression callee (`obj.method`, dynamic dispatch).
MemberOrDynamic,
}
/// Target and traversal parameters for a symbol-chain trace.
#[derive(Debug, Clone, Copy)]
pub struct SymbolChainQuery<'a> {
/// File path of the target symbol, root-relative or absolute.
pub file: &'a str,
/// Exported symbol name to trace.
pub symbol: &'a str,
/// Maximum traversal depth in each direction.
pub depth: u32,
/// Which directions to walk.
pub directions: TraceDirections,
}