1use std::path::Path;
2use std::time::Instant;
3
4use fallow_config::ProductionAnalysis;
5use fallow_engine::{
6 dead_code::DeadCodeAnalysisArtifacts,
7 project_config::{ProjectConfig, ProjectConfigOptions},
8 session::AnalysisSession,
9};
10use fallow_output::{
11 CHECK_SCHEMA_VERSION, CheckOutputInput, DeadCodeNextStepsInput, DiffIndex, build_check_output,
12 build_dead_code_next_steps, check_meta,
13};
14use fallow_types::output_format::OutputFormat;
15use fallow_types::path_util::is_absolute_path_any_platform;
16use fallow_types::results::{AnalysisResults, TraceHopRole};
17use rustc_hash::FxHashSet;
18
19use crate::{
20 AnalysisOptions, BoundaryViolationsProgrammaticOutput, CircularDependenciesProgrammaticOutput,
21 DeadCodeFilters, DeadCodeOptions, DeadCodeProgrammaticOutput, ProgrammaticError,
22 analysis_context::{
23 ProgrammaticAnalysisContext, changed_files_for_run,
24 resolve_programmatic_analysis_context_deferred_workspace, workspace_roots_for_session,
25 },
26 next_steps::{
27 default_workspace_ref_for_workspaces, setup_pointer_applicable, suggestions_enabled,
28 },
29};
30
31use super::{ProgrammaticResult, root_envelope_mode};
32
33pub(super) struct DeadCodeProgrammaticRunWithArtifacts {
34 pub output: DeadCodeProgrammaticOutput,
35 pub artifacts: DeadCodeAnalysisArtifacts,
36}
37
38pub fn run_dead_code(options: &DeadCodeOptions) -> ProgrammaticResult<DeadCodeProgrammaticOutput> {
46 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
47 resolved.install(|| run_dead_code_inner(options, &resolved, |_| {}))
48}
49
50pub(super) fn map_engine_error(
56 err: &fallow_engine::EngineError,
57 failure_message: &str,
58 failure_code: &'static str,
59 context: &'static str,
60) -> ProgrammaticError {
61 if err.is_cancelled() {
62 return crate::analysis_context::cancelled_error_message(err.message())
63 .with_context(context);
64 }
65 ProgrammaticError::new(format!("{failure_message}: {err}"), 2)
66 .with_code(failure_code)
67 .with_context(context)
68}
69
70pub fn run_circular_dependencies(
76 options: &DeadCodeOptions,
77) -> ProgrammaticResult<CircularDependenciesProgrammaticOutput> {
78 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
79 resolved.install(|| {
80 run_dead_code_inner(options, &resolved, keep_circular_dependencies).map(Into::into)
81 })
82}
83
84pub fn run_boundary_violations(
90 options: &DeadCodeOptions,
91) -> ProgrammaticResult<BoundaryViolationsProgrammaticOutput> {
92 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
93 resolved.install(|| {
94 run_dead_code_inner(options, &resolved, keep_boundary_violations).map(Into::into)
95 })
96}
97
98fn run_dead_code_inner(
99 options: &DeadCodeOptions,
100 resolved: &ProgrammaticAnalysisContext,
101 post_filter: impl FnOnce(&mut AnalysisResults),
102) -> ProgrammaticResult<DeadCodeProgrammaticOutput> {
103 let start = Instant::now();
104 resolved.ensure_not_cancelled("config load and file discovery")?;
105 let session = load_dead_code_session(options, resolved)?;
106 run_dead_code_with_session(options, resolved, &session, None, post_filter, start)
107}
108
109pub(super) fn run_dead_code_with_session(
110 options: &DeadCodeOptions,
111 resolved: &ProgrammaticAnalysisContext,
112 session: &AnalysisSession,
113 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
114 post_filter: impl FnOnce(&mut AnalysisResults),
115 start: Instant,
116) -> ProgrammaticResult<DeadCodeProgrammaticOutput> {
117 resolved.ensure_not_cancelled("dead-code analysis")?;
118 let analysis = session.analyze_dead_code().map_err(|err| {
119 map_engine_error(
120 &err,
121 "dead-code analysis failed",
122 "FALLOW_DEAD_CODE_FAILED",
123 "dead-code",
124 )
125 })?;
126 let mut results = analysis.results;
127 let unfiltered_unused_files = results.unused_files.clone();
128
129 let type_aware_meta = finish_dead_code_results(
130 DeadCodeReportInputs {
131 options,
132 resolved,
133 session,
134 changed_files,
135 unfiltered_unused_files,
136 },
137 &mut results,
138 post_filter,
139 )?;
140
141 Ok(build_dead_code_programmatic_output(
142 options,
143 resolved,
144 session,
145 results,
146 type_aware_meta,
147 start,
148 ))
149}
150
151pub(super) fn run_dead_code_with_session_artifacts(
152 options: &DeadCodeOptions,
153 resolved: &ProgrammaticAnalysisContext,
154 session: &AnalysisSession,
155 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
156 post_filter: impl FnOnce(&mut AnalysisResults),
157 start: Instant,
158) -> ProgrammaticResult<DeadCodeProgrammaticRunWithArtifacts> {
159 resolved.ensure_not_cancelled("dead-code analysis")?;
160 let mut artifacts = session
161 .analyze_dead_code_with_artifacts(true, true)
162 .map_err(|err| {
163 map_engine_error(
164 &err,
165 "dead-code analysis failed",
166 "FALLOW_DEAD_CODE_FAILED",
167 "dead-code",
168 )
169 })?;
170 let unfiltered_unused_files = artifacts.results.unused_files.clone();
171
172 let type_aware_meta = finish_dead_code_results(
173 DeadCodeReportInputs {
174 options,
175 resolved,
176 session,
177 changed_files,
178 unfiltered_unused_files,
179 },
180 &mut artifacts.results,
181 post_filter,
182 )?;
183
184 Ok(build_dead_code_run_with_artifacts(
185 options,
186 resolved,
187 session,
188 artifacts,
189 type_aware_meta,
190 start,
191 ))
192}
193
194pub(super) fn run_dead_code_from_artifacts(
195 options: &DeadCodeOptions,
196 resolved: &ProgrammaticAnalysisContext,
197 session: &AnalysisSession,
198 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
199 mut artifacts: DeadCodeAnalysisArtifacts,
200 start: Instant,
201) -> ProgrammaticResult<DeadCodeProgrammaticRunWithArtifacts> {
202 let unfiltered_unused_files = artifacts.results.unused_files.clone();
203 let type_aware_meta = finish_dead_code_results(
204 DeadCodeReportInputs {
205 options,
206 resolved,
207 session,
208 changed_files,
209 unfiltered_unused_files,
210 },
211 &mut artifacts.results,
212 |_| {},
213 )?;
214
215 Ok(build_dead_code_run_with_artifacts(
216 options,
217 resolved,
218 session,
219 artifacts,
220 type_aware_meta,
221 start,
222 ))
223}
224
225struct DeadCodeReportInputs<'a> {
228 options: &'a DeadCodeOptions,
229 resolved: &'a ProgrammaticAnalysisContext,
230 session: &'a AnalysisSession,
231 changed_files: Option<&'a FxHashSet<std::path::PathBuf>>,
232 unfiltered_unused_files: Vec<fallow_types::output_dead_code::UnusedFileFinding>,
235}
236
237fn finish_dead_code_results(
246 inputs: DeadCodeReportInputs<'_>,
247 results: &mut AnalysisResults,
248 post_filter: impl FnOnce(&mut AnalysisResults),
249) -> ProgrammaticResult<Option<fallow_types::envelope::TypeAwareMeta>> {
250 let DeadCodeReportInputs {
251 options,
252 resolved,
253 session,
254 changed_files,
255 unfiltered_unused_files,
256 } = inputs;
257 apply_dead_code_scope(options, resolved, session, changed_files, results)?;
258 apply_dead_code_filters(&options.filters, results);
259 fallow_engine::dead_code::apply_rule_severities(results, session.config());
260 post_filter(results);
261 refine_with_unfiltered_unused_files(
262 &options.analysis.type_aware,
263 &options.filters,
264 session,
265 results,
266 unfiltered_unused_files,
267 )
268}
269
270fn refine_with_unfiltered_unused_files(
271 options: &crate::TypeAwareOptions,
272 filters: &DeadCodeFilters,
273 session: &AnalysisSession,
274 results: &mut AnalysisResults,
275 unfiltered_unused_files: Vec<fallow_types::output_dead_code::UnusedFileFinding>,
276) -> ProgrammaticResult<Option<fallow_types::envelope::TypeAwareMeta>> {
277 let reported_unused_files =
278 std::mem::replace(&mut results.unused_files, unfiltered_unused_files);
279 let outcome =
280 crate::type_aware::refine_programmatic_dead_code(options, filters, session, results);
281 results.unused_files = reported_unused_files;
282 if options.enabled {
283 fallow_engine::dead_code::apply_rule_severities(results, session.config());
287 }
288 outcome
289}
290
291fn build_dead_code_run_with_artifacts(
292 options: &DeadCodeOptions,
293 resolved: &ProgrammaticAnalysisContext,
294 session: &AnalysisSession,
295 artifacts: DeadCodeAnalysisArtifacts,
296 type_aware_meta: Option<fallow_types::envelope::TypeAwareMeta>,
297 start: Instant,
298) -> DeadCodeProgrammaticRunWithArtifacts {
299 let output = build_dead_code_programmatic_output(
300 options,
301 resolved,
302 session,
303 artifacts.results.clone(),
304 type_aware_meta,
305 start,
306 );
307 DeadCodeProgrammaticRunWithArtifacts { output, artifacts }
308}
309
310fn build_dead_code_programmatic_output(
311 options: &DeadCodeOptions,
312 resolved: &ProgrammaticAnalysisContext,
313 session: &AnalysisSession,
314 results: AnalysisResults,
315 type_aware_meta: Option<fallow_types::envelope::TypeAwareMeta>,
316 start: Instant,
317) -> DeadCodeProgrammaticOutput {
318 let root = session.root();
319 let next_steps = build_dead_code_next_steps(DeadCodeNextStepsInput {
320 suggestions_enabled: suggestions_enabled(),
321 results: &results,
322 root,
323 offer_setup: setup_pointer_applicable(root),
324 impact_digest: None,
325 workspace_ref: default_workspace_ref_for_workspaces(root, session.workspaces()).as_deref(),
326 audit_changed: fallow_engine::churn::is_git_repo(root),
327 has_external_plugins: !fallow_config::discover_external_plugins(root, &[]).is_empty(),
328 baseline_recheck: None,
331 });
332 let config_fixable =
333 fallow_config::is_config_fixable(&resolved.root, resolved.config_path.as_ref());
334 let mut meta = options.analysis.explain.then(check_meta);
335 if let Some(type_aware) = type_aware_meta {
336 meta.get_or_insert_with(Default::default).type_aware = Some(type_aware);
337 }
338 let output = build_check_output(CheckOutputInput {
339 schema_version: CHECK_SCHEMA_VERSION,
340 version: env!("CARGO_PKG_VERSION").to_string(),
341 elapsed: start.elapsed(),
342 results,
343 config_fixable,
344 meta,
345 workspace_diagnostics: session.current_workspace_diagnostics(),
346 next_steps,
347 });
348 DeadCodeProgrammaticOutput {
349 output,
350 root: session.root().to_path_buf(),
351 config_fixable,
352 envelope_mode: root_envelope_mode(),
353 telemetry_analysis_run_id: None,
354 }
355}
356
357fn keep_circular_dependencies(results: &mut AnalysisResults) {
358 let entry_point_summary = results.entry_point_summary.take();
359 let circular_dependencies = std::mem::take(&mut results.circular_dependencies);
360 *results = AnalysisResults::default();
361 results.entry_point_summary = entry_point_summary;
362 results.circular_dependencies = circular_dependencies;
363}
364
365fn keep_boundary_violations(results: &mut AnalysisResults) {
366 let entry_point_summary = results.entry_point_summary.take();
367 let boundary_violations = std::mem::take(&mut results.boundary_violations);
368 let boundary_coverage_violations = std::mem::take(&mut results.boundary_coverage_violations);
369 let boundary_call_violations = std::mem::take(&mut results.boundary_call_violations);
370 *results = AnalysisResults::default();
371 results.entry_point_summary = entry_point_summary;
372 results.boundary_violations = boundary_violations;
373 results.boundary_coverage_violations = boundary_coverage_violations;
374 results.boundary_call_violations = boundary_call_violations;
375}
376
377pub(super) fn load_dead_code_session(
378 options: &DeadCodeOptions,
379 resolved: &ProgrammaticAnalysisContext,
380) -> ProgrammaticResult<AnalysisSession> {
381 let project_config = fallow_engine::project_config::config_for_project_analysis(
382 &resolved.root,
383 resolved.config_path.as_deref(),
384 ProjectConfigOptions {
385 output: OutputFormat::Json,
386 no_cache: resolved.no_cache,
387 threads: resolved.threads,
388 production_override: resolved.production_override,
389 quiet: true,
390 analysis: ProductionAnalysis::DeadCode,
391 allow_remote_extends: resolved.allow_remote_extends,
392 },
393 )
394 .map_err(|err| {
395 ProgrammaticError::new(format!("failed to load config: {err}"), 2)
396 .with_code("FALLOW_CONFIG_LOAD_FAILED")
397 .with_context("analysis.configPath")
398 })?;
399 let project_config = configure_project_for_dead_code(project_config, options);
400 Ok(attach_cancellation(
401 AnalysisSession::from_config(project_config),
402 resolved,
403 ))
404}
405
406pub(super) fn attach_cancellation(
411 session: AnalysisSession,
412 resolved: &ProgrammaticAnalysisContext,
413) -> AnalysisSession {
414 match resolved.cancellation() {
415 Some(cancellation) => session.with_cancellation(std::sync::Arc::clone(cancellation)),
416 None => session,
417 }
418}
419
420pub(super) fn default_dead_code_options_for_context(
421 resolved: &ProgrammaticAnalysisContext,
422) -> DeadCodeOptions {
423 DeadCodeOptions {
424 analysis: AnalysisOptions {
425 root: Some(resolved.root().to_path_buf()),
426 config_path: resolved.config_path().clone(),
427 no_cache: resolved.no_cache(),
428 threads: Some(resolved.threads()),
429 production_override: resolved.production_override(),
430 changed_since: resolved.changed_since().map(str::to_owned),
431 workspace: resolved.workspace().map(<[String]>::to_vec),
432 changed_workspaces: resolved.changed_workspaces().map(str::to_owned),
433 explain: resolved.explain_enabled(),
434 ..AnalysisOptions::default()
435 },
436 filters: DeadCodeFilters::default(),
437 files: Vec::new(),
438 include_entry_exports: false,
439 }
440}
441
442fn configure_project_for_dead_code(
443 mut project_config: ProjectConfig,
444 options: &DeadCodeOptions,
445) -> ProjectConfig {
446 if options.include_entry_exports {
447 project_config.config.include_entry_exports = true;
448 }
449 activate_explicit_dead_code_opt_ins(&options.filters, &mut project_config.config.rules);
450 project_config
451}
452
453fn activate_explicit_dead_code_opt_ins(
454 filters: &DeadCodeFilters,
455 rules: &mut fallow_config::RulesConfig,
456) {
457 if filters.private_type_leaks && rules.private_type_leaks == fallow_config::Severity::Off {
458 rules.private_type_leaks = fallow_config::Severity::Warn;
459 }
460}
461
462fn apply_dead_code_scope(
463 options: &DeadCodeOptions,
464 resolved: &ProgrammaticAnalysisContext,
465 session: &AnalysisSession,
466 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
467 results: &mut AnalysisResults,
468) -> ProgrammaticResult<()> {
469 let workspace_roots = workspace_roots_for_session(resolved, session.workspaces())?;
470 if let Some(workspace_roots) = workspace_roots.as_ref() {
471 fallow_engine::dead_code::filter_to_workspaces(results, workspace_roots);
472 }
473 let resolved_changed_files = if changed_files.is_some() {
474 None
475 } else {
476 changed_files_for_run(resolved)?
477 };
478 if let Some(changed_files) = changed_files.or(resolved_changed_files.as_ref()) {
479 fallow_engine::dead_code::filter_by_changed_files(results, changed_files);
480 }
481 if let Some(diff) = resolved.diff.as_ref() {
482 filter_dead_code_by_diff(results, diff, session.root());
483 }
484 apply_dead_code_file_filter(options, session.root(), results);
485 Ok(())
486}
487
488fn filter_dead_code_by_diff(results: &mut AnalysisResults, diff: &DiffIndex, root: &Path) {
489 let touches_file = |path: &Path| -> bool {
490 diff.key_for(path, root)
491 .is_none_or(|rel| diff.touches_file(&rel))
492 };
493 let line_in_diff = |path: &Path, line: u32| -> bool {
494 diff.key_for(path, root)
495 .is_none_or(|rel| diff.line_is_added(&rel, u64::from(line)))
496 };
497
498 filter_dead_code_source_findings(results, &touches_file, &line_in_diff);
499 filter_dead_code_security_findings(results, &touches_file, &line_in_diff);
500 filter_dead_code_dependency_findings(results, &line_in_diff);
501 filter_dead_code_graph_findings(results, &touches_file, &line_in_diff);
502 filter_dead_code_framework_findings(results, &line_in_diff);
503}
504
505fn filter_dead_code_source_findings(
506 results: &mut AnalysisResults,
507 touches_file: &dyn Fn(&Path) -> bool,
508 line_in_diff: &dyn Fn(&Path, u32) -> bool,
509) {
510 results
511 .unused_files
512 .retain(|finding| touches_file(&finding.file.path));
513 results
514 .unused_exports
515 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
516 results
517 .unused_types
518 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
519 results
520 .private_type_leaks
521 .retain(|finding| line_in_diff(&finding.leak.path, finding.leak.line));
522 results
523 .unused_enum_members
524 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
525 results
526 .unused_class_members
527 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
528 results
529 .unused_store_members
530 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
531 results
532 .unprovided_injects
533 .retain(|finding| line_in_diff(&finding.inject.path, finding.inject.line));
534 results
535 .unrendered_components
536 .retain(|finding| line_in_diff(&finding.component.path, finding.component.line));
537 results
538 .unused_component_props
539 .retain(|finding| line_in_diff(&finding.prop.path, finding.prop.line));
540 results
541 .unused_component_emits
542 .retain(|finding| line_in_diff(&finding.emit.path, finding.emit.line));
543 results
544 .unused_component_inputs
545 .retain(|finding| line_in_diff(&finding.input.path, finding.input.line));
546 results
547 .unused_component_outputs
548 .retain(|finding| line_in_diff(&finding.output.path, finding.output.line));
549 results
550 .unused_svelte_events
551 .retain(|finding| line_in_diff(&finding.event.path, finding.event.line));
552 results
553 .unused_server_actions
554 .retain(|finding| line_in_diff(&finding.action.path, finding.action.line));
555 results
556 .unused_load_data_keys
557 .retain(|finding| line_in_diff(&finding.key.path, finding.key.line));
558 results
559 .unresolved_imports
560 .retain(|finding| line_in_diff(&finding.import.path, finding.import.line));
561}
562
563fn filter_dead_code_security_findings(
564 results: &mut AnalysisResults,
565 touches_file: &dyn Fn(&Path) -> bool,
566 line_in_diff: &dyn Fn(&Path, u32) -> bool,
567) {
568 results.security_findings.retain(|finding| {
569 line_in_diff(&finding.path, finding.line)
570 || finding.trace.iter().any(|hop| {
571 line_in_diff(&hop.path, hop.line)
572 || (matches!(hop.role, TraceHopRole::SecretSource) && touches_file(&hop.path))
573 })
574 || finding.reachability.as_ref().is_some_and(|reachability| {
575 reachability
576 .untrusted_source_trace
577 .iter()
578 .any(|hop| line_in_diff(&hop.path, hop.line))
579 })
580 });
581 results
582 .security_unresolved_callee_diagnostics
583 .retain(|finding| line_in_diff(&finding.path, finding.line));
584}
585
586fn filter_dead_code_dependency_findings(
587 results: &mut AnalysisResults,
588 line_in_diff: &dyn Fn(&Path, u32) -> bool,
589) {
590 for finding in &mut results.unlisted_dependencies {
591 finding
592 .dep
593 .imported_from
594 .retain(|source| line_in_diff(&source.path, source.line));
595 }
596 results
597 .unlisted_dependencies
598 .retain(|finding| !finding.dep.imported_from.is_empty());
599}
600
601fn filter_dead_code_graph_findings(
602 results: &mut AnalysisResults,
603 touches_file: &dyn Fn(&Path) -> bool,
604 line_in_diff: &dyn Fn(&Path, u32) -> bool,
605) {
606 results.duplicate_exports.retain(|finding| {
607 finding
608 .export
609 .locations
610 .iter()
611 .any(|location| line_in_diff(&location.path, location.line))
612 });
613 results
614 .circular_dependencies
615 .retain(|cycle| cycle.cycle.files.iter().any(|path| touches_file(path)));
616 results
617 .re_export_cycles
618 .retain(|cycle| cycle.cycle.files.iter().any(|path| touches_file(path)));
619 results
620 .boundary_violations
621 .retain(|finding| line_in_diff(&finding.violation.from_path, finding.violation.line));
622 results
623 .stale_suppressions
624 .retain(|finding| line_in_diff(&finding.path, finding.line));
625}
626
627fn filter_dead_code_framework_findings(
628 results: &mut AnalysisResults,
629 line_in_diff: &dyn Fn(&Path, u32) -> bool,
630) {
631 results
632 .invalid_client_exports
633 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
634 results
635 .mixed_client_server_barrels
636 .retain(|finding| line_in_diff(&finding.barrel.path, finding.barrel.line));
637 results
638 .misplaced_directives
639 .retain(|finding| line_in_diff(&finding.directive_site.path, finding.directive_site.line));
640 results
641 .route_collisions
642 .retain(|finding| line_in_diff(&finding.collision.path, finding.collision.line));
643 results
644 .dynamic_segment_name_conflicts
645 .retain(|finding| line_in_diff(&finding.conflict.path, finding.conflict.line));
646}
647
648fn apply_dead_code_file_filter(
649 options: &DeadCodeOptions,
650 root: &Path,
651 results: &mut AnalysisResults,
652) {
653 if options.files.is_empty() {
654 return;
655 }
656 let file_set = options
657 .files
658 .iter()
659 .map(|path| {
660 if is_absolute_path_any_platform(path) {
661 path.clone()
662 } else {
663 root.join(path)
664 }
665 })
666 .collect::<FxHashSet<_>>();
667 fallow_engine::dead_code::filter_by_changed_files(results, &file_set);
668 clear_dead_code_dependency_findings(results);
669}
670
671fn apply_dead_code_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
672 if !dead_code_filters_active(filters) {
673 return;
674 }
675 apply_dead_code_core_filters(filters, results);
676 apply_dead_code_component_filters(filters, results);
677 apply_dead_code_graph_filters(filters, results);
678 apply_dead_code_policy_filters(filters, results);
679 apply_dead_code_catalog_filters(filters, results);
680}
681
682fn dead_code_filters_active(filters: &DeadCodeFilters) -> bool {
683 filters.unused_files
684 || filters.unused_exports
685 || filters.unused_deps
686 || filters.unused_types
687 || filters.private_type_leaks
688 || filters.unused_enum_members
689 || filters.unused_class_members
690 || filters.unused_store_members
691 || filters.unprovided_injects
692 || filters.unrendered_components
693 || filters.unused_component_props
694 || filters.unused_component_emits
695 || filters.unused_component_inputs
696 || filters.unused_component_outputs
697 || filters.unused_svelte_events
698 || filters.unused_server_actions
699 || filters.unused_load_data_keys
700 || filters.unresolved_imports
701 || filters.unlisted_deps
702 || filters.duplicate_exports
703 || filters.circular_deps
704 || filters.re_export_cycles
705 || filters.boundary_violations
706 || filters.policy_violations
707 || filters.stale_suppressions
708 || filters.unused_catalog_entries
709 || filters.empty_catalog_groups
710 || filters.unresolved_catalog_references
711 || filters.unused_dependency_overrides
712 || filters.misconfigured_dependency_overrides
713}
714
715fn apply_dead_code_core_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
716 if !filters.unused_files {
717 results.unused_files.clear();
718 }
719 if !filters.unused_exports {
720 results.unused_exports.clear();
721 }
722 if !filters.unused_types {
723 results.unused_types.clear();
724 }
725 if !filters.private_type_leaks {
726 results.private_type_leaks.clear();
727 }
728 if !filters.unused_deps {
729 clear_dead_code_dependency_findings(results);
730 }
731 if !filters.unused_enum_members {
732 results.unused_enum_members.clear();
733 }
734 if !filters.unused_class_members {
735 results.unused_class_members.clear();
736 }
737 if !filters.unused_store_members {
738 results.unused_store_members.clear();
739 }
740 if !filters.unlisted_deps {
741 results.unlisted_dependencies.clear();
742 }
743}
744
745fn clear_dead_code_dependency_findings(results: &mut AnalysisResults) {
746 results.unused_dependencies.clear();
747 results.unused_dev_dependencies.clear();
748 results.unused_optional_dependencies.clear();
749 results.type_only_dependencies.clear();
750 results.test_only_dependencies.clear();
751 results.dev_dependencies_in_production.clear();
752}
753
754fn apply_dead_code_component_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
755 if !filters.unprovided_injects {
756 results.unprovided_injects.clear();
757 }
758 if !filters.unrendered_components {
759 results.unrendered_components.clear();
760 }
761 if !filters.unused_component_props {
762 results.unused_component_props.clear();
763 }
764 if !filters.unused_component_emits {
765 results.unused_component_emits.clear();
766 }
767 if !filters.unused_component_inputs {
768 results.unused_component_inputs.clear();
769 }
770 if !filters.unused_component_outputs {
771 results.unused_component_outputs.clear();
772 }
773 if !filters.unused_svelte_events {
774 results.unused_svelte_events.clear();
775 }
776 if !filters.unused_server_actions {
777 results.unused_server_actions.clear();
778 }
779 if !filters.unused_load_data_keys {
780 results.unused_load_data_keys.clear();
781 }
782 if !filters.unresolved_imports {
783 results.unresolved_imports.clear();
784 }
785}
786
787fn apply_dead_code_graph_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
788 if !filters.duplicate_exports {
789 results.duplicate_exports.clear();
790 }
791 if !filters.circular_deps {
792 results.circular_dependencies.clear();
793 }
794 if !filters.re_export_cycles {
795 results.re_export_cycles.clear();
796 }
797 if !filters.boundary_violations {
798 results.boundary_violations.clear();
799 results.boundary_coverage_violations.clear();
800 results.boundary_call_violations.clear();
801 }
802}
803
804fn apply_dead_code_policy_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
805 if !filters.policy_violations {
806 results.policy_violations.clear();
807 }
808 if !filters.stale_suppressions {
809 results.stale_suppressions.clear();
810 }
811}
812
813fn apply_dead_code_catalog_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
814 if !filters.unused_catalog_entries {
815 results.unused_catalog_entries.clear();
816 }
817 if !filters.empty_catalog_groups {
818 results.empty_catalog_groups.clear();
819 }
820 if !filters.unresolved_catalog_references {
821 results.unresolved_catalog_references.clear();
822 }
823 if !filters.unused_dependency_overrides {
824 results.unused_dependency_overrides.clear();
825 }
826 if !filters.misconfigured_dependency_overrides {
827 results.misconfigured_dependency_overrides.clear();
828 }
829}