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 });
329 let config_fixable =
330 fallow_config::is_config_fixable(&resolved.root, resolved.config_path.as_ref());
331 let mut meta = options.analysis.explain.then(check_meta);
332 if let Some(type_aware) = type_aware_meta {
333 meta.get_or_insert_with(Default::default).type_aware = Some(type_aware);
334 }
335 let output = build_check_output(CheckOutputInput {
336 schema_version: CHECK_SCHEMA_VERSION,
337 version: env!("CARGO_PKG_VERSION").to_string(),
338 elapsed: start.elapsed(),
339 results,
340 config_fixable,
341 meta,
342 workspace_diagnostics: session.current_workspace_diagnostics(),
343 next_steps,
344 });
345 DeadCodeProgrammaticOutput {
346 output,
347 root: session.root().to_path_buf(),
348 config_fixable,
349 envelope_mode: root_envelope_mode(),
350 telemetry_analysis_run_id: None,
351 }
352}
353
354fn keep_circular_dependencies(results: &mut AnalysisResults) {
355 let entry_point_summary = results.entry_point_summary.take();
356 let circular_dependencies = std::mem::take(&mut results.circular_dependencies);
357 *results = AnalysisResults::default();
358 results.entry_point_summary = entry_point_summary;
359 results.circular_dependencies = circular_dependencies;
360}
361
362fn keep_boundary_violations(results: &mut AnalysisResults) {
363 let entry_point_summary = results.entry_point_summary.take();
364 let boundary_violations = std::mem::take(&mut results.boundary_violations);
365 let boundary_coverage_violations = std::mem::take(&mut results.boundary_coverage_violations);
366 let boundary_call_violations = std::mem::take(&mut results.boundary_call_violations);
367 *results = AnalysisResults::default();
368 results.entry_point_summary = entry_point_summary;
369 results.boundary_violations = boundary_violations;
370 results.boundary_coverage_violations = boundary_coverage_violations;
371 results.boundary_call_violations = boundary_call_violations;
372}
373
374pub(super) fn load_dead_code_session(
375 options: &DeadCodeOptions,
376 resolved: &ProgrammaticAnalysisContext,
377) -> ProgrammaticResult<AnalysisSession> {
378 let project_config = fallow_engine::project_config::config_for_project_analysis(
379 &resolved.root,
380 resolved.config_path.as_deref(),
381 ProjectConfigOptions {
382 output: OutputFormat::Json,
383 no_cache: resolved.no_cache,
384 threads: resolved.threads,
385 production_override: resolved.production_override,
386 quiet: true,
387 analysis: ProductionAnalysis::DeadCode,
388 allow_remote_extends: resolved.allow_remote_extends,
389 },
390 )
391 .map_err(|err| {
392 ProgrammaticError::new(format!("failed to load config: {err}"), 2)
393 .with_code("FALLOW_CONFIG_LOAD_FAILED")
394 .with_context("analysis.configPath")
395 })?;
396 let project_config = configure_project_for_dead_code(project_config, options);
397 Ok(attach_cancellation(
398 AnalysisSession::from_config(project_config),
399 resolved,
400 ))
401}
402
403pub(super) fn attach_cancellation(
408 session: AnalysisSession,
409 resolved: &ProgrammaticAnalysisContext,
410) -> AnalysisSession {
411 match resolved.cancellation() {
412 Some(cancellation) => session.with_cancellation(std::sync::Arc::clone(cancellation)),
413 None => session,
414 }
415}
416
417pub(super) fn default_dead_code_options_for_context(
418 resolved: &ProgrammaticAnalysisContext,
419) -> DeadCodeOptions {
420 DeadCodeOptions {
421 analysis: AnalysisOptions {
422 root: Some(resolved.root().to_path_buf()),
423 config_path: resolved.config_path().clone(),
424 no_cache: resolved.no_cache(),
425 threads: Some(resolved.threads()),
426 production_override: resolved.production_override(),
427 changed_since: resolved.changed_since().map(str::to_owned),
428 workspace: resolved.workspace().map(<[String]>::to_vec),
429 changed_workspaces: resolved.changed_workspaces().map(str::to_owned),
430 explain: resolved.explain_enabled(),
431 ..AnalysisOptions::default()
432 },
433 filters: DeadCodeFilters::default(),
434 files: Vec::new(),
435 include_entry_exports: false,
436 }
437}
438
439fn configure_project_for_dead_code(
440 mut project_config: ProjectConfig,
441 options: &DeadCodeOptions,
442) -> ProjectConfig {
443 if options.include_entry_exports {
444 project_config.config.include_entry_exports = true;
445 }
446 activate_explicit_dead_code_opt_ins(&options.filters, &mut project_config.config.rules);
447 project_config
448}
449
450fn activate_explicit_dead_code_opt_ins(
451 filters: &DeadCodeFilters,
452 rules: &mut fallow_config::RulesConfig,
453) {
454 if filters.private_type_leaks && rules.private_type_leaks == fallow_config::Severity::Off {
455 rules.private_type_leaks = fallow_config::Severity::Warn;
456 }
457}
458
459fn apply_dead_code_scope(
460 options: &DeadCodeOptions,
461 resolved: &ProgrammaticAnalysisContext,
462 session: &AnalysisSession,
463 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
464 results: &mut AnalysisResults,
465) -> ProgrammaticResult<()> {
466 let workspace_roots = workspace_roots_for_session(resolved, session.workspaces())?;
467 if let Some(workspace_roots) = workspace_roots.as_ref() {
468 fallow_engine::dead_code::filter_to_workspaces(results, workspace_roots);
469 }
470 let resolved_changed_files = if changed_files.is_some() {
471 None
472 } else {
473 changed_files_for_run(resolved)?
474 };
475 if let Some(changed_files) = changed_files.or(resolved_changed_files.as_ref()) {
476 fallow_engine::dead_code::filter_by_changed_files(results, changed_files);
477 }
478 if let Some(diff) = resolved.diff.as_ref() {
479 filter_dead_code_by_diff(results, diff, session.root());
480 }
481 apply_dead_code_file_filter(options, session.root(), results);
482 Ok(())
483}
484
485fn filter_dead_code_by_diff(results: &mut AnalysisResults, diff: &DiffIndex, root: &Path) {
486 let touches_file = |path: &Path| -> bool {
487 diff.key_for(path, root)
488 .is_none_or(|rel| diff.touches_file(&rel))
489 };
490 let line_in_diff = |path: &Path, line: u32| -> bool {
491 diff.key_for(path, root)
492 .is_none_or(|rel| diff.line_is_added(&rel, u64::from(line)))
493 };
494
495 filter_dead_code_source_findings(results, &touches_file, &line_in_diff);
496 filter_dead_code_security_findings(results, &touches_file, &line_in_diff);
497 filter_dead_code_dependency_findings(results, &line_in_diff);
498 filter_dead_code_graph_findings(results, &touches_file, &line_in_diff);
499 filter_dead_code_framework_findings(results, &line_in_diff);
500}
501
502fn filter_dead_code_source_findings(
503 results: &mut AnalysisResults,
504 touches_file: &dyn Fn(&Path) -> bool,
505 line_in_diff: &dyn Fn(&Path, u32) -> bool,
506) {
507 results
508 .unused_files
509 .retain(|finding| touches_file(&finding.file.path));
510 results
511 .unused_exports
512 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
513 results
514 .unused_types
515 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
516 results
517 .private_type_leaks
518 .retain(|finding| line_in_diff(&finding.leak.path, finding.leak.line));
519 results
520 .unused_enum_members
521 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
522 results
523 .unused_class_members
524 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
525 results
526 .unused_store_members
527 .retain(|finding| line_in_diff(&finding.member.path, finding.member.line));
528 results
529 .unprovided_injects
530 .retain(|finding| line_in_diff(&finding.inject.path, finding.inject.line));
531 results
532 .unrendered_components
533 .retain(|finding| line_in_diff(&finding.component.path, finding.component.line));
534 results
535 .unused_component_props
536 .retain(|finding| line_in_diff(&finding.prop.path, finding.prop.line));
537 results
538 .unused_component_emits
539 .retain(|finding| line_in_diff(&finding.emit.path, finding.emit.line));
540 results
541 .unused_component_inputs
542 .retain(|finding| line_in_diff(&finding.input.path, finding.input.line));
543 results
544 .unused_component_outputs
545 .retain(|finding| line_in_diff(&finding.output.path, finding.output.line));
546 results
547 .unused_svelte_events
548 .retain(|finding| line_in_diff(&finding.event.path, finding.event.line));
549 results
550 .unused_server_actions
551 .retain(|finding| line_in_diff(&finding.action.path, finding.action.line));
552 results
553 .unused_load_data_keys
554 .retain(|finding| line_in_diff(&finding.key.path, finding.key.line));
555 results
556 .unresolved_imports
557 .retain(|finding| line_in_diff(&finding.import.path, finding.import.line));
558}
559
560fn filter_dead_code_security_findings(
561 results: &mut AnalysisResults,
562 touches_file: &dyn Fn(&Path) -> bool,
563 line_in_diff: &dyn Fn(&Path, u32) -> bool,
564) {
565 results.security_findings.retain(|finding| {
566 line_in_diff(&finding.path, finding.line)
567 || finding.trace.iter().any(|hop| {
568 line_in_diff(&hop.path, hop.line)
569 || (matches!(hop.role, TraceHopRole::SecretSource) && touches_file(&hop.path))
570 })
571 || finding.reachability.as_ref().is_some_and(|reachability| {
572 reachability
573 .untrusted_source_trace
574 .iter()
575 .any(|hop| line_in_diff(&hop.path, hop.line))
576 })
577 });
578 results
579 .security_unresolved_callee_diagnostics
580 .retain(|finding| line_in_diff(&finding.path, finding.line));
581}
582
583fn filter_dead_code_dependency_findings(
584 results: &mut AnalysisResults,
585 line_in_diff: &dyn Fn(&Path, u32) -> bool,
586) {
587 for finding in &mut results.unlisted_dependencies {
588 finding
589 .dep
590 .imported_from
591 .retain(|source| line_in_diff(&source.path, source.line));
592 }
593 results
594 .unlisted_dependencies
595 .retain(|finding| !finding.dep.imported_from.is_empty());
596}
597
598fn filter_dead_code_graph_findings(
599 results: &mut AnalysisResults,
600 touches_file: &dyn Fn(&Path) -> bool,
601 line_in_diff: &dyn Fn(&Path, u32) -> bool,
602) {
603 results.duplicate_exports.retain(|finding| {
604 finding
605 .export
606 .locations
607 .iter()
608 .any(|location| line_in_diff(&location.path, location.line))
609 });
610 results
611 .circular_dependencies
612 .retain(|cycle| cycle.cycle.files.iter().any(|path| touches_file(path)));
613 results
614 .re_export_cycles
615 .retain(|cycle| cycle.cycle.files.iter().any(|path| touches_file(path)));
616 results
617 .boundary_violations
618 .retain(|finding| line_in_diff(&finding.violation.from_path, finding.violation.line));
619 results
620 .stale_suppressions
621 .retain(|finding| line_in_diff(&finding.path, finding.line));
622}
623
624fn filter_dead_code_framework_findings(
625 results: &mut AnalysisResults,
626 line_in_diff: &dyn Fn(&Path, u32) -> bool,
627) {
628 results
629 .invalid_client_exports
630 .retain(|finding| line_in_diff(&finding.export.path, finding.export.line));
631 results
632 .mixed_client_server_barrels
633 .retain(|finding| line_in_diff(&finding.barrel.path, finding.barrel.line));
634 results
635 .misplaced_directives
636 .retain(|finding| line_in_diff(&finding.directive_site.path, finding.directive_site.line));
637 results
638 .route_collisions
639 .retain(|finding| line_in_diff(&finding.collision.path, finding.collision.line));
640 results
641 .dynamic_segment_name_conflicts
642 .retain(|finding| line_in_diff(&finding.conflict.path, finding.conflict.line));
643}
644
645fn apply_dead_code_file_filter(
646 options: &DeadCodeOptions,
647 root: &Path,
648 results: &mut AnalysisResults,
649) {
650 if options.files.is_empty() {
651 return;
652 }
653 let file_set = options
654 .files
655 .iter()
656 .map(|path| {
657 if is_absolute_path_any_platform(path) {
658 path.clone()
659 } else {
660 root.join(path)
661 }
662 })
663 .collect::<FxHashSet<_>>();
664 fallow_engine::dead_code::filter_by_changed_files(results, &file_set);
665 clear_dead_code_dependency_findings(results);
666}
667
668fn apply_dead_code_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
669 if !dead_code_filters_active(filters) {
670 return;
671 }
672 apply_dead_code_core_filters(filters, results);
673 apply_dead_code_component_filters(filters, results);
674 apply_dead_code_graph_filters(filters, results);
675 apply_dead_code_policy_filters(filters, results);
676 apply_dead_code_catalog_filters(filters, results);
677}
678
679fn dead_code_filters_active(filters: &DeadCodeFilters) -> bool {
680 filters.unused_files
681 || filters.unused_exports
682 || filters.unused_deps
683 || filters.unused_types
684 || filters.private_type_leaks
685 || filters.unused_enum_members
686 || filters.unused_class_members
687 || filters.unused_store_members
688 || filters.unprovided_injects
689 || filters.unrendered_components
690 || filters.unused_component_props
691 || filters.unused_component_emits
692 || filters.unused_component_inputs
693 || filters.unused_component_outputs
694 || filters.unused_svelte_events
695 || filters.unused_server_actions
696 || filters.unused_load_data_keys
697 || filters.unresolved_imports
698 || filters.unlisted_deps
699 || filters.duplicate_exports
700 || filters.circular_deps
701 || filters.re_export_cycles
702 || filters.boundary_violations
703 || filters.policy_violations
704 || filters.stale_suppressions
705 || filters.unused_catalog_entries
706 || filters.empty_catalog_groups
707 || filters.unresolved_catalog_references
708 || filters.unused_dependency_overrides
709 || filters.misconfigured_dependency_overrides
710}
711
712fn apply_dead_code_core_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
713 if !filters.unused_files {
714 results.unused_files.clear();
715 }
716 if !filters.unused_exports {
717 results.unused_exports.clear();
718 }
719 if !filters.unused_types {
720 results.unused_types.clear();
721 }
722 if !filters.private_type_leaks {
723 results.private_type_leaks.clear();
724 }
725 if !filters.unused_deps {
726 clear_dead_code_dependency_findings(results);
727 }
728 if !filters.unused_enum_members {
729 results.unused_enum_members.clear();
730 }
731 if !filters.unused_class_members {
732 results.unused_class_members.clear();
733 }
734 if !filters.unused_store_members {
735 results.unused_store_members.clear();
736 }
737 if !filters.unlisted_deps {
738 results.unlisted_dependencies.clear();
739 }
740}
741
742fn clear_dead_code_dependency_findings(results: &mut AnalysisResults) {
743 results.unused_dependencies.clear();
744 results.unused_dev_dependencies.clear();
745 results.unused_optional_dependencies.clear();
746 results.type_only_dependencies.clear();
747 results.test_only_dependencies.clear();
748 results.dev_dependencies_in_production.clear();
749}
750
751fn apply_dead_code_component_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
752 if !filters.unprovided_injects {
753 results.unprovided_injects.clear();
754 }
755 if !filters.unrendered_components {
756 results.unrendered_components.clear();
757 }
758 if !filters.unused_component_props {
759 results.unused_component_props.clear();
760 }
761 if !filters.unused_component_emits {
762 results.unused_component_emits.clear();
763 }
764 if !filters.unused_component_inputs {
765 results.unused_component_inputs.clear();
766 }
767 if !filters.unused_component_outputs {
768 results.unused_component_outputs.clear();
769 }
770 if !filters.unused_svelte_events {
771 results.unused_svelte_events.clear();
772 }
773 if !filters.unused_server_actions {
774 results.unused_server_actions.clear();
775 }
776 if !filters.unused_load_data_keys {
777 results.unused_load_data_keys.clear();
778 }
779 if !filters.unresolved_imports {
780 results.unresolved_imports.clear();
781 }
782}
783
784fn apply_dead_code_graph_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
785 if !filters.duplicate_exports {
786 results.duplicate_exports.clear();
787 }
788 if !filters.circular_deps {
789 results.circular_dependencies.clear();
790 }
791 if !filters.re_export_cycles {
792 results.re_export_cycles.clear();
793 }
794 if !filters.boundary_violations {
795 results.boundary_violations.clear();
796 results.boundary_coverage_violations.clear();
797 results.boundary_call_violations.clear();
798 }
799}
800
801fn apply_dead_code_policy_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
802 if !filters.policy_violations {
803 results.policy_violations.clear();
804 }
805 if !filters.stale_suppressions {
806 results.stale_suppressions.clear();
807 }
808}
809
810fn apply_dead_code_catalog_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
811 if !filters.unused_catalog_entries {
812 results.unused_catalog_entries.clear();
813 }
814 if !filters.empty_catalog_groups {
815 results.empty_catalog_groups.clear();
816 }
817 if !filters.unresolved_catalog_references {
818 results.unresolved_catalog_references.clear();
819 }
820 if !filters.unused_dependency_overrides {
821 results.unused_dependency_overrides.clear();
822 }
823 if !filters.misconfigured_dependency_overrides {
824 results.misconfigured_dependency_overrides.clear();
825 }
826}