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