Skip to main content

fallow_types/
trace.rs

1//! Shared trace output contracts for analysis and integration surfaces.
2
3use std::path::PathBuf;
4
5use serde::Serialize;
6
7use crate::duplicates::{CloneInstance, RefactoringSuggestion};
8use crate::serde_path;
9
10/// Result of tracing an export: why it is considered used or unused.
11#[derive(Debug, Serialize)]
12#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
13pub struct ExportTrace {
14    /// The file containing the export.
15    #[serde(serialize_with = "serde_path::serialize")]
16    pub file: PathBuf,
17    /// The export name being traced.
18    pub export_name: String,
19    /// Namespace selected for this trace. Producers always emit it; the schema
20    /// permits omission by payloads created before namespaces were exposed.
21    #[cfg_attr(feature = "schema", schemars(default))]
22    pub namespace: crate::semantic::SemanticNamespace,
23    /// Whether the file is reachable from an entry point.
24    pub file_reachable: bool,
25    /// Whether the file is an entry point.
26    pub is_entry_point: bool,
27    /// Whether the export is considered used.
28    pub is_used: bool,
29    /// Files that reference this export directly.
30    pub direct_references: Vec<ExportReference>,
31    /// Re-export chains that pass through this export.
32    pub re_export_chains: Vec<ReExportChain>,
33    /// Human-readable reason summary.
34    pub reason: String,
35    /// Exact checker-backed references when type-aware tracing is enabled.
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub semantic: Option<crate::semantic::SemanticSymbolTrace>,
38}
39
40/// Result of tracing a class / enum / store MEMBER: the `--trace FILE:NAME`
41/// fallback when `NAME` is not a top-level export but a member declared on one
42/// (issue #1744). The trace runs on the module graph only, so it reports the
43/// OWNING export's reachability and usage (the gating precondition for
44/// member-level crediting) plus a pointer to the right `--unused-*-members`
45/// command, rather than per-member crediting provenance.
46#[derive(Debug, Serialize)]
47#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
48pub struct ClassMemberTrace {
49    /// The file containing the member.
50    #[serde(serialize_with = "serde_path::serialize")]
51    pub file: PathBuf,
52    /// The member name being traced.
53    pub member_name: String,
54    /// The member kind: `class-method`, `class-property`, `enum-member`,
55    /// `store-member`, or `namespace-member`.
56    pub member_kind: String,
57    /// The export that declares this member (the class / enum / store name).
58    pub owner_export: String,
59    /// Whether the owning export is considered used.
60    pub owner_is_used: bool,
61    /// Whether the file is reachable from an entry point.
62    pub owner_file_reachable: bool,
63    /// Whether the file is an entry point.
64    pub owner_is_entry_point: bool,
65    /// Files that reference the owning export directly.
66    pub owner_direct_references: Vec<ExportReference>,
67    /// Re-export chains through which the owning export is reachable. Populated
68    /// so a machine consumer can tell "used via a barrel" (empty direct refs but
69    /// non-empty chains) from "genuinely unreferenced".
70    pub owner_re_export_chains: Vec<ReExportChain>,
71    /// Human-readable reason summary plus the follow-up command to inspect the
72    /// member finding.
73    pub reason: String,
74    /// Exact checker-backed member references when type-aware tracing is enabled.
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub semantic: Option<crate::semantic::SemanticSymbolTrace>,
77}
78
79/// A direct reference to an export.
80#[derive(Debug, Serialize)]
81#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
82pub struct ExportReference {
83    /// File that contains the reference.
84    #[serde(serialize_with = "serde_path::serialize")]
85    pub from_file: PathBuf,
86    /// Reference kind, such as named import, default import, or re-export.
87    pub kind: String,
88}
89
90/// A re-export chain showing how an export is propagated.
91#[derive(Debug, Serialize)]
92#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
93pub struct ReExportChain {
94    /// The barrel file that re-exports this symbol.
95    #[serde(serialize_with = "serde_path::serialize")]
96    pub barrel_file: PathBuf,
97    /// The name it is re-exported as.
98    pub exported_as: String,
99    /// Number of references on the barrel's re-exported symbol.
100    pub reference_count: usize,
101}
102
103/// Result of tracing all edges for a file.
104#[derive(Debug, Serialize)]
105#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
106pub struct FileTrace {
107    /// The traced file.
108    #[serde(serialize_with = "serde_path::serialize")]
109    pub file: PathBuf,
110    /// Whether this file is reachable from entry points.
111    pub is_reachable: bool,
112    /// Whether this file is an entry point.
113    pub is_entry_point: bool,
114    /// Exports declared by this file.
115    pub exports: Vec<TracedExport>,
116    /// Files that this file imports from.
117    #[serde(serialize_with = "serde_path::serialize_vec")]
118    pub imports_from: Vec<PathBuf>,
119    /// Files that import from this file.
120    #[serde(serialize_with = "serde_path::serialize_vec")]
121    pub imported_by: Vec<PathBuf>,
122    /// Re-exports declared by this file.
123    pub re_exports: Vec<TracedReExport>,
124}
125
126/// An export with usage information.
127#[derive(Debug, Serialize)]
128#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
129pub struct TracedExport {
130    /// Export name.
131    pub name: String,
132    /// Whether the export is type-only.
133    pub is_type_only: bool,
134    /// Number of references to this export.
135    pub reference_count: usize,
136    /// Files that reference this export.
137    pub referenced_by: Vec<ExportReference>,
138}
139
140/// A re-export with source information.
141#[derive(Debug, Serialize)]
142#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
143pub struct TracedReExport {
144    /// Source file being re-exported from.
145    #[serde(serialize_with = "serde_path::serialize")]
146    pub source_file: PathBuf,
147    /// Imported symbol name.
148    pub imported_name: String,
149    /// Exported symbol name.
150    pub exported_name: String,
151}
152
153/// Result of tracing a dependency: where it is used.
154#[derive(Debug, Serialize)]
155#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
156pub struct DependencyTrace {
157    /// The dependency name being traced.
158    pub package_name: String,
159    /// Files that import this dependency.
160    #[serde(serialize_with = "serde_path::serialize_vec")]
161    pub imported_by: Vec<PathBuf>,
162    /// Files that import this dependency with type-only imports.
163    #[serde(serialize_with = "serde_path::serialize_vec")]
164    pub type_only_imported_by: Vec<PathBuf>,
165    /// Whether the dependency is invoked from package.json scripts or CI configs.
166    pub used_in_scripts: bool,
167    /// Whether the dependency is used at all.
168    pub is_used: bool,
169    /// Total import count.
170    pub import_count: usize,
171}
172
173/// Pipeline performance timings.
174#[derive(Debug, Clone, Serialize)]
175#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
176pub struct PipelineTimings {
177    /// Time spent discovering files.
178    pub discover_files_ms: f64,
179    /// Number of discovered files.
180    pub file_count: usize,
181    /// Time spent discovering workspaces.
182    pub workspaces_ms: f64,
183    /// Number of discovered workspaces.
184    pub workspace_count: usize,
185    /// Time spent running plugin discovery.
186    pub plugins_ms: f64,
187    /// Time spent analyzing package scripts and CI configuration.
188    pub script_analysis_ms: f64,
189    /// Wall-clock time spent parsing and extracting modules.
190    pub parse_extract_ms: f64,
191    /// Summed parser CPU time across workers.
192    pub parse_cpu_ms: f64,
193    /// Number of extracted modules.
194    pub module_count: usize,
195    /// Number of files loaded from the parse cache.
196    pub cache_hits: usize,
197    /// Number of files parsed without a cache hit.
198    pub cache_misses: usize,
199    /// Time spent updating the parse cache.
200    pub cache_update_ms: f64,
201    /// Time spent categorizing entry points.
202    pub entry_points_ms: f64,
203    /// Number of entry points considered.
204    pub entry_point_count: usize,
205    /// Time spent resolving imports.
206    pub resolve_imports_ms: f64,
207    /// Time spent building the module graph.
208    pub build_graph_ms: f64,
209    /// Time spent running analysis.
210    pub analyze_ms: f64,
211    /// Time spent running duplicate-code analysis, when included.
212    #[serde(default, skip_serializing_if = "Option::is_none")]
213    pub duplication_ms: Option<f64>,
214    /// Total pipeline time.
215    pub total_ms: f64,
216}
217
218/// Result of computing the impact closure for a single file as the seed.
219#[derive(Debug, Serialize)]
220#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
221pub struct ImpactClosureTrace {
222    /// The seed file, root-relative.
223    pub seed: String,
224    /// Root-relative paths transitively affected by the seed.
225    pub affected_not_shown: Vec<String>,
226    /// Coordination gaps between the seed and consumers.
227    pub coordination_gap: Vec<ImpactClosureGap>,
228}
229
230/// One coordination-gap entry in an [`ImpactClosureTrace`].
231#[derive(Debug, Serialize)]
232#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
233pub struct ImpactClosureGap {
234    /// Root-relative path of the consumer module.
235    pub consumer_file: String,
236    /// Exported symbol names the consumer references.
237    pub consumed_symbols: Vec<String>,
238    /// Scope note for the syntactic trace.
239    pub note: String,
240}
241
242/// Result of tracing a clone: all groups containing the code at a source
243/// location or addressed by a stable clone fingerprint.
244#[derive(Debug, Serialize)]
245#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
246pub struct CloneTrace {
247    /// File passed to the trace request, root-relative when a group matches.
248    #[serde(serialize_with = "serde_path::serialize")]
249    pub file: PathBuf,
250    /// 1-based line passed to the trace request or representative group line.
251    pub line: usize,
252    /// The matched clone instance, if one exists.
253    pub matched_instance: Option<CloneInstance>,
254    /// Clone groups matched by the trace request.
255    pub clone_groups: Vec<TracedCloneGroup>,
256}
257
258/// One clone group returned from a clone trace request.
259#[derive(Debug, Serialize)]
260#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
261pub struct TracedCloneGroup {
262    /// Stable content fingerprint, usually `dup:<8hex>` and widened on rare
263    /// report collisions.
264    pub fingerprint: String,
265    /// Number of tokens in the duplicated block.
266    pub token_count: usize,
267    /// Number of lines in the duplicated block.
268    pub line_count: usize,
269    /// Maximum directory-tree or same-file line distance between instances.
270    pub spread: usize,
271    /// Lowest all-pairs similarity for a near-miss clone group.
272    #[serde(default, skip_serializing_if = "Option::is_none")]
273    #[cfg_attr(feature = "schema", schemars(with = "f64"))]
274    pub similarity: Option<f64>,
275    /// Root-relative clone instances in this group.
276    pub instances: Vec<CloneInstance>,
277    /// Group-level refactoring suggestion.
278    pub suggestion: RefactoringSuggestion,
279    /// Best-effort name for the extracted function. Advisory only.
280    #[serde(default, skip_serializing_if = "Option::is_none")]
281    pub suggested_name: Option<String>,
282}