Skip to main content

fallow_core/
results.rs

1pub use fallow_types::output_dead_code::{
2    BoundaryCallViolationFinding, BoundaryCoverageViolationFinding, BoundaryViolationFinding,
3    CircularDependencyFinding, DevDependencyInProductionFinding, DuplicateExportFinding,
4    DuplicatePropShapeFinding, DynamicSegmentNameConflictFinding, EmptyCatalogGroupFinding,
5    InvalidClientExportFinding, MisconfiguredDependencyOverrideFinding, MisplacedDirectiveFinding,
6    MixedClientServerBarrelFinding, PolicyViolationFinding, PrivateTypeLeakFinding,
7    ReExportCycleFinding, RouteCollisionFinding, TestOnlyDependencyFinding,
8    TypeOnlyDependencyFinding, UnlistedDependencyFinding, UnprovidedInjectFinding,
9    UnrenderedComponentFinding, UnresolvedCatalogReferenceFinding, UnresolvedImportFinding,
10    UnusedCatalogEntryFinding, UnusedClassMemberFinding, UnusedComponentEmitFinding,
11    UnusedComponentInputFinding, UnusedComponentOutputFinding, UnusedComponentPropFinding,
12    UnusedDependencyFinding, UnusedDependencyOverrideFinding, UnusedDevDependencyFinding,
13    UnusedEnumMemberFinding, UnusedExportFinding, UnusedFileFinding, UnusedLoadDataKeyFinding,
14    UnusedOptionalDependencyFinding, UnusedServerActionFinding, UnusedStoreMemberFinding,
15    UnusedSvelteEventFinding, UnusedTypeFinding,
16};
17pub use fallow_types::results::{
18    ActiveSuppression, AnalysisResults, BoundaryCallViolation, BoundaryCoverageViolation,
19    BoundaryViolation, CircularDependency, CircularDependencyEdge, DependencyLocation,
20    DependencyOverrideMisconfigReason, DependencyOverrideSource, DevDependencyInProduction,
21    DuplicateExport, DuplicateLocation, DuplicatePropShape, DuplicatePropShapeMember,
22    DynamicSegmentNameConflict, EmptyCatalogGroup, EntryPointSummary, ExportUsage, FeatureFlag,
23    FlagConfidence, FlagKind, ImportSite, InvalidClientExport, MisconfiguredDependencyOverride,
24    MisplacedDirective, MixedClientServerBarrel, PolicyRuleKind, PolicyViolation,
25    PolicyViolationSeverity, PrivateTypeLeak, PropDrillHop, PropDrillingChain, ReExportCycle,
26    ReExportCycleKind, ReactComponentIntel, ReactHookSummary, ReactPropDrill, ReactPropIntel,
27    ReferenceLocation, RenderFanInComponent, RenderFanInMetric, RouteCollision,
28    SecurityAttackSurfaceEntry, SecurityCandidate, SecurityCandidateBoundary,
29    SecurityCandidateSink, SecurityDeadCodeContext, SecurityDeadCodeKind,
30    SecurityDefensiveBoundary, SecurityDefensiveControl, SecurityFinding, SecurityFindingKind,
31    SecurityNetworkContext, SecurityReachability, SecuritySeverity, SecurityTaintFlow,
32    SecurityUnresolvedCalleeDiagnostic, SecurityZoneCrossing, StaleSuppression, SuppressionOrigin,
33    TaintConfidence, TaintEndpoint, TaintPath, TestOnlyDependency, ThinWrapper, TraceHop,
34    TraceHopRole, TypeOnlyDependency, UnlistedDependency, UnprovidedInject, UnrenderedComponent,
35    UnresolvedCatalogReference, UnresolvedImport, UnusedCatalogEntry, UnusedComponentEmit,
36    UnusedComponentInput, UnusedComponentOutput, UnusedComponentProp, UnusedDependency,
37    UnusedDependencyOverride, UnusedExport, UnusedFile, UnusedLoadDataKey, UnusedMember,
38    UnusedServerAction, UnusedSvelteEvent,
39};
40
41#[cfg(test)]
42mod tests {
43    use std::path::PathBuf;
44
45    use super::*;
46    use crate::extract::MemberKind;
47    use fallow_types::output_dead_code::{UnresolvedImportFinding, UnusedFileFinding};
48
49    #[test]
50    fn empty_results_no_issues() {
51        let results = AnalysisResults::default();
52        assert_eq!(results.total_issues(), 0);
53        assert!(!results.has_issues());
54    }
55
56    #[test]
57    fn results_with_unused_file() {
58        let mut results = AnalysisResults::default();
59        results
60            .unused_files
61            .push(UnusedFileFinding::with_actions(UnusedFile {
62                path: PathBuf::from("test.ts"),
63            }));
64        assert_eq!(results.total_issues(), 1);
65        assert!(results.has_issues());
66    }
67
68    #[test]
69    fn results_with_unused_export() {
70        let mut results = AnalysisResults::default();
71        results
72            .unused_exports
73            .push(UnusedExportFinding::with_actions(UnusedExport {
74                path: PathBuf::from("test.ts"),
75                export_name: "foo".to_string(),
76                is_type_only: false,
77                line: 1,
78                col: 0,
79                span_start: 0,
80                is_re_export: false,
81            }));
82        assert_eq!(results.total_issues(), 1);
83        assert!(results.has_issues());
84    }
85
86    #[test]
87    #[expect(
88        clippy::too_many_lines,
89        reason = "test fixture; linear setup/assert, length is not a maintainability concern"
90    )]
91    fn results_total_counts_all_types() {
92        let mut results = AnalysisResults::default();
93        results
94            .unused_files
95            .push(UnusedFileFinding::with_actions(UnusedFile {
96                path: PathBuf::from("a.ts"),
97            }));
98        results
99            .unused_exports
100            .push(UnusedExportFinding::with_actions(UnusedExport {
101                path: PathBuf::from("b.ts"),
102                export_name: "x".to_string(),
103                is_type_only: false,
104                line: 1,
105                col: 0,
106                span_start: 0,
107                is_re_export: false,
108            }));
109        results
110            .unused_types
111            .push(UnusedTypeFinding::with_actions(UnusedExport {
112                path: PathBuf::from("c.ts"),
113                export_name: "T".to_string(),
114                is_type_only: true,
115                line: 1,
116                col: 0,
117                span_start: 0,
118                is_re_export: false,
119            }));
120        results
121            .unused_dependencies
122            .push(UnusedDependencyFinding::with_actions(UnusedDependency {
123                package_name: "dep".to_string(),
124                location: DependencyLocation::Dependencies,
125                path: PathBuf::from("package.json"),
126                line: 5,
127                used_in_workspaces: Vec::new(),
128            }));
129        results
130            .unused_dev_dependencies
131            .push(UnusedDevDependencyFinding::with_actions(UnusedDependency {
132                package_name: "dev".to_string(),
133                location: DependencyLocation::DevDependencies,
134                path: PathBuf::from("package.json"),
135                line: 5,
136                used_in_workspaces: Vec::new(),
137            }));
138        results
139            .unused_enum_members
140            .push(UnusedEnumMemberFinding::with_actions(UnusedMember {
141                path: PathBuf::from("d.ts"),
142                parent_name: "E".to_string(),
143                member_name: "A".to_string(),
144                kind: MemberKind::EnumMember,
145                line: 1,
146                col: 0,
147            }));
148        results
149            .unused_class_members
150            .push(UnusedClassMemberFinding::with_actions(UnusedMember {
151                path: PathBuf::from("e.ts"),
152                parent_name: "C".to_string(),
153                member_name: "m".to_string(),
154                kind: MemberKind::ClassMethod,
155                line: 1,
156                col: 0,
157            }));
158        results
159            .unresolved_imports
160            .push(UnresolvedImportFinding::with_actions(UnresolvedImport {
161                path: PathBuf::from("f.ts"),
162                specifier: "./missing".to_string(),
163                line: 1,
164                col: 0,
165                specifier_col: 0,
166            }));
167        results
168            .unlisted_dependencies
169            .push(UnlistedDependencyFinding::with_actions(
170                UnlistedDependency {
171                    package_name: "unlisted".to_string(),
172                    imported_from: vec![ImportSite {
173                        path: PathBuf::from("g.ts"),
174                        line: 1,
175                        col: 0,
176                    }],
177                },
178            ));
179        results
180            .duplicate_exports
181            .push(DuplicateExportFinding::with_actions(DuplicateExport {
182                export_name: "dup".to_string(),
183                locations: vec![
184                    DuplicateLocation {
185                        path: PathBuf::from("h.ts"),
186                        line: 15,
187                        col: 0,
188                    },
189                    DuplicateLocation {
190                        path: PathBuf::from("i.ts"),
191                        line: 30,
192                        col: 0,
193                    },
194                ],
195            }));
196
197        assert_eq!(results.total_issues(), 10);
198        assert!(results.has_issues());
199    }
200}