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, 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;
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;
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 run_dead_code_with_baseline(options, None)
47}
48
49pub fn run_dead_code_with_baseline(
62 options: &DeadCodeOptions,
63 baseline: Option<&Path>,
64) -> ProgrammaticResult<DeadCodeProgrammaticOutput> {
65 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
66 resolved.install(|| {
67 let start = Instant::now();
68 resolved.ensure_not_cancelled("config load and file discovery")?;
69 let session = load_dead_code_session(options, &resolved)?;
70 let (mut results, type_aware_meta) =
71 analyze_dead_code_results(options, &resolved, &session, None, |_| {})?;
72 if let Some(baseline) = baseline {
73 apply_baseline(
74 &mut results,
75 baseline,
76 session.root(),
77 type_aware_meta.as_ref(),
78 )?;
79 }
80 Ok(build_dead_code_programmatic_output(
81 options,
82 &resolved,
83 &session,
84 results,
85 type_aware_meta,
86 start,
87 ))
88 })
89}
90
91pub(super) fn map_engine_error(
97 err: &fallow_engine::EngineError,
98 failure_message: &str,
99 failure_code: &'static str,
100 context: &'static str,
101) -> ProgrammaticError {
102 if err.is_cancelled() {
103 return crate::analysis_context::cancelled_error_message(err.message())
104 .with_context(context);
105 }
106 ProgrammaticError::new(format!("{failure_message}: {err}"), 2)
107 .with_code(failure_code)
108 .with_context(context)
109}
110
111pub fn run_circular_dependencies(
117 options: &DeadCodeOptions,
118) -> ProgrammaticResult<CircularDependenciesProgrammaticOutput> {
119 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
120 resolved.install(|| {
121 run_dead_code_inner(options, &resolved, keep_circular_dependencies).map(Into::into)
122 })
123}
124
125pub fn run_boundary_violations(
131 options: &DeadCodeOptions,
132) -> ProgrammaticResult<BoundaryViolationsProgrammaticOutput> {
133 let resolved = resolve_programmatic_analysis_context_deferred_workspace(&options.analysis)?;
134 resolved.install(|| {
135 run_dead_code_inner(options, &resolved, keep_boundary_violations).map(Into::into)
136 })
137}
138
139fn run_dead_code_inner(
140 options: &DeadCodeOptions,
141 resolved: &ProgrammaticAnalysisContext,
142 post_filter: impl FnOnce(&mut AnalysisResults),
143) -> ProgrammaticResult<DeadCodeProgrammaticOutput> {
144 let start = Instant::now();
145 resolved.ensure_not_cancelled("config load and file discovery")?;
146 let session = load_dead_code_session(options, resolved)?;
147 run_dead_code_with_session(options, resolved, &session, None, post_filter, start)
148}
149
150pub(super) fn run_dead_code_with_session(
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<DeadCodeProgrammaticOutput> {
158 let (results, type_aware_meta) =
159 analyze_dead_code_results(options, resolved, session, changed_files, post_filter)?;
160 Ok(build_dead_code_programmatic_output(
161 options,
162 resolved,
163 session,
164 results,
165 type_aware_meta,
166 start,
167 ))
168}
169
170fn analyze_dead_code_results(
173 options: &DeadCodeOptions,
174 resolved: &ProgrammaticAnalysisContext,
175 session: &AnalysisSession,
176 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
177 post_filter: impl FnOnce(&mut AnalysisResults),
178) -> ProgrammaticResult<(
179 AnalysisResults,
180 Option<fallow_types::envelope::TypeAwareMeta>,
181)> {
182 resolved.ensure_not_cancelled("dead-code analysis")?;
183 let analysis = session.analyze_dead_code().map_err(|err| {
184 map_engine_error(
185 &err,
186 "dead-code analysis failed",
187 "FALLOW_DEAD_CODE_FAILED",
188 "dead-code",
189 )
190 })?;
191 let mut results = analysis.results;
192 let unfiltered_unused_files = results.unused_files.clone();
193
194 let type_aware_meta = finish_dead_code_results(
195 DeadCodeReportInputs {
196 options,
197 resolved,
198 session,
199 changed_files,
200 unfiltered_unused_files,
201 },
202 &mut results,
203 post_filter,
204 )?;
205 Ok((results, type_aware_meta))
206}
207
208fn apply_baseline(
210 results: &mut AnalysisResults,
211 baseline: &Path,
212 root: &Path,
213 type_aware_meta: Option<&fallow_types::envelope::TypeAwareMeta>,
214) -> ProgrammaticResult<()> {
215 use fallow_engine::baseline::{DeadCodeBaselineError, apply_dead_code_baseline};
216
217 let path = if is_absolute_path_any_platform(baseline) {
218 baseline.to_path_buf()
219 } else {
220 root.join(baseline)
221 };
222 let content = std::fs::read_to_string(&path).map_err(|err| {
223 ProgrammaticError::new(
224 format!("failed to read baseline {}: {err}", path.display()),
225 2,
226 )
227 .with_code("FALLOW_BASELINE_READ_FAILED")
228 .with_context("baseline")
229 })?;
230 let identity = type_aware_meta
231 .and_then(|meta| meta.identity.clone())
232 .unwrap_or_default();
233 apply_dead_code_baseline(results, &content, root, &identity, false)
234 .map(|_| ())
235 .map_err(|err| match err {
236 DeadCodeBaselineError::Parse(message) => ProgrammaticError::new(
237 format!("failed to parse baseline {}: {message}", path.display()),
238 2,
239 )
240 .with_code("FALLOW_BASELINE_INVALID")
241 .with_context("baseline"),
242 DeadCodeBaselineError::IncompatibleIdentity(fields) => ProgrammaticError::new(
243 format!(
244 "baseline analysis identity is incompatible in: {}. Save it again with the same analysis options",
245 fields.join(", ")
246 ),
247 2,
248 )
249 .with_code("FALLOW_BASELINE_IDENTITY_INCOMPATIBLE")
250 .with_context("baseline"),
251 })
252}
253
254pub(super) fn run_dead_code_with_session_artifacts(
255 options: &DeadCodeOptions,
256 resolved: &ProgrammaticAnalysisContext,
257 session: &AnalysisSession,
258 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
259 post_filter: impl FnOnce(&mut AnalysisResults),
260 start: Instant,
261) -> ProgrammaticResult<DeadCodeProgrammaticRunWithArtifacts> {
262 resolved.ensure_not_cancelled("dead-code analysis")?;
263 let mut artifacts = session
264 .analyze_dead_code_with_artifacts(true, true)
265 .map_err(|err| {
266 map_engine_error(
267 &err,
268 "dead-code analysis failed",
269 "FALLOW_DEAD_CODE_FAILED",
270 "dead-code",
271 )
272 })?;
273 let unfiltered_unused_files = artifacts.results.unused_files.clone();
274
275 let type_aware_meta = finish_dead_code_results(
276 DeadCodeReportInputs {
277 options,
278 resolved,
279 session,
280 changed_files,
281 unfiltered_unused_files,
282 },
283 &mut artifacts.results,
284 post_filter,
285 )?;
286
287 Ok(build_dead_code_run_with_artifacts(
288 options,
289 resolved,
290 session,
291 artifacts,
292 type_aware_meta,
293 start,
294 ))
295}
296
297pub(super) fn run_dead_code_from_artifacts(
298 options: &DeadCodeOptions,
299 resolved: &ProgrammaticAnalysisContext,
300 session: &AnalysisSession,
301 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
302 mut artifacts: DeadCodeAnalysisArtifacts,
303 start: Instant,
304) -> ProgrammaticResult<DeadCodeProgrammaticRunWithArtifacts> {
305 let unfiltered_unused_files = artifacts.results.unused_files.clone();
306 let type_aware_meta = finish_dead_code_results(
307 DeadCodeReportInputs {
308 options,
309 resolved,
310 session,
311 changed_files,
312 unfiltered_unused_files,
313 },
314 &mut artifacts.results,
315 |_| {},
316 )?;
317
318 Ok(build_dead_code_run_with_artifacts(
319 options,
320 resolved,
321 session,
322 artifacts,
323 type_aware_meta,
324 start,
325 ))
326}
327
328struct DeadCodeReportInputs<'a> {
331 options: &'a DeadCodeOptions,
332 resolved: &'a ProgrammaticAnalysisContext,
333 session: &'a AnalysisSession,
334 changed_files: Option<&'a FxHashSet<std::path::PathBuf>>,
335 unfiltered_unused_files: Vec<fallow_types::output_dead_code::UnusedFileFinding>,
338}
339
340fn finish_dead_code_results(
349 inputs: DeadCodeReportInputs<'_>,
350 results: &mut AnalysisResults,
351 post_filter: impl FnOnce(&mut AnalysisResults),
352) -> ProgrammaticResult<Option<fallow_types::envelope::TypeAwareMeta>> {
353 let DeadCodeReportInputs {
354 options,
355 resolved,
356 session,
357 changed_files,
358 unfiltered_unused_files,
359 } = inputs;
360 apply_dead_code_scope(options, resolved, session, changed_files, results)?;
361 apply_dead_code_filters(&options.filters, results);
362 fallow_engine::dead_code::apply_rule_severities(results, session.config());
363 post_filter(results);
364 refine_with_unfiltered_unused_files(
365 &options.analysis.type_aware,
366 &options.filters,
367 session,
368 results,
369 unfiltered_unused_files,
370 )
371}
372
373fn refine_with_unfiltered_unused_files(
374 options: &crate::TypeAwareOptions,
375 filters: &DeadCodeFilters,
376 session: &AnalysisSession,
377 results: &mut AnalysisResults,
378 unfiltered_unused_files: Vec<fallow_types::output_dead_code::UnusedFileFinding>,
379) -> ProgrammaticResult<Option<fallow_types::envelope::TypeAwareMeta>> {
380 let reported_unused_files =
381 std::mem::replace(&mut results.unused_files, unfiltered_unused_files);
382 let outcome =
383 crate::type_aware::refine_programmatic_dead_code(options, filters, session, results);
384 results.unused_files = reported_unused_files;
385 if options.enabled {
386 fallow_engine::dead_code::apply_rule_severities(results, session.config());
391 }
392 outcome
393}
394
395fn build_dead_code_run_with_artifacts(
396 options: &DeadCodeOptions,
397 resolved: &ProgrammaticAnalysisContext,
398 session: &AnalysisSession,
399 artifacts: DeadCodeAnalysisArtifacts,
400 type_aware_meta: Option<fallow_types::envelope::TypeAwareMeta>,
401 start: Instant,
402) -> DeadCodeProgrammaticRunWithArtifacts {
403 let output = build_dead_code_programmatic_output(
404 options,
405 resolved,
406 session,
407 artifacts.results.clone(),
408 type_aware_meta,
409 start,
410 );
411 DeadCodeProgrammaticRunWithArtifacts { output, artifacts }
412}
413
414fn build_dead_code_programmatic_output(
415 options: &DeadCodeOptions,
416 resolved: &ProgrammaticAnalysisContext,
417 session: &AnalysisSession,
418 results: AnalysisResults,
419 type_aware_meta: Option<fallow_types::envelope::TypeAwareMeta>,
420 start: Instant,
421) -> DeadCodeProgrammaticOutput {
422 let root = session.root();
423 let next_steps = build_dead_code_next_steps(DeadCodeNextStepsInput {
424 suggestions_enabled: suggestions_enabled(),
425 results: &results,
426 root,
427 offer_setup: setup_pointer_applicable(root),
428 impact_digest: None,
429 workspace_ref: default_workspace_ref_for_workspaces(root, session.workspaces()).as_deref(),
430 audit_changed: fallow_engine::churn::is_git_repo(root),
431 has_external_plugins: !fallow_config::discover_external_plugins(root, &[]).is_empty(),
432 baseline_recheck: None,
435 });
436 let config_fixable =
437 fallow_config::is_config_fixable(&resolved.root, resolved.config_path.as_ref());
438 let mut meta = options.analysis.explain.then(check_meta);
439 if let Some(type_aware) = type_aware_meta {
440 meta.get_or_insert_with(Default::default).type_aware = Some(type_aware);
441 }
442 let mut output = build_check_output(CheckOutputInput {
443 schema_version: CHECK_SCHEMA_VERSION,
444 version: env!("CARGO_PKG_VERSION").to_string(),
445 elapsed: start.elapsed(),
446 results,
447 config_fixable,
448 meta,
449 workspace_diagnostics: session.current_workspace_diagnostics(),
450 next_steps,
451 });
452 output.request_outcomes = resolved.request_outcomes();
453 DeadCodeProgrammaticOutput {
454 output,
455 root: session.root().to_path_buf(),
456 config_fixable,
457 telemetry_analysis_run_id: None,
458 }
459}
460
461fn keep_circular_dependencies(results: &mut AnalysisResults) {
462 let entry_point_summary = results.entry_point_summary.take();
463 let circular_dependencies = std::mem::take(&mut results.circular_dependencies);
464 *results = AnalysisResults::default();
465 results.entry_point_summary = entry_point_summary;
466 results.circular_dependencies = circular_dependencies;
467}
468
469fn keep_boundary_violations(results: &mut AnalysisResults) {
470 let entry_point_summary = results.entry_point_summary.take();
471 let boundary_violations = std::mem::take(&mut results.boundary_violations);
472 let boundary_coverage_violations = std::mem::take(&mut results.boundary_coverage_violations);
473 let boundary_call_violations = std::mem::take(&mut results.boundary_call_violations);
474 *results = AnalysisResults::default();
475 results.entry_point_summary = entry_point_summary;
476 results.boundary_violations = boundary_violations;
477 results.boundary_coverage_violations = boundary_coverage_violations;
478 results.boundary_call_violations = boundary_call_violations;
479}
480
481pub(super) fn load_dead_code_session(
482 options: &DeadCodeOptions,
483 resolved: &ProgrammaticAnalysisContext,
484) -> ProgrammaticResult<AnalysisSession> {
485 let project_config = fallow_engine::project_config::config_for_project_analysis(
486 &resolved.root,
487 resolved.config_path.as_deref(),
488 ProjectConfigOptions {
489 output: OutputFormat::Json,
490 no_cache: resolved.no_cache,
491 threads: resolved.threads,
492 production_override: resolved.production_override,
493 quiet: true,
494 analysis: ProductionAnalysis::DeadCode,
495 allow_remote_extends: resolved.allow_remote_extends,
496 },
497 )
498 .map_err(|err| {
499 ProgrammaticError::new(format!("failed to load config: {err}"), 2)
500 .with_code("FALLOW_CONFIG_LOAD_FAILED")
501 .with_context("analysis.configPath")
502 })?;
503 let project_config = configure_project_for_dead_code(project_config, options);
504 Ok(attach_cancellation(
505 AnalysisSession::from_config(project_config),
506 resolved,
507 ))
508}
509
510pub(super) fn attach_cancellation(
515 session: AnalysisSession,
516 resolved: &ProgrammaticAnalysisContext,
517) -> AnalysisSession {
518 match resolved.cancellation() {
519 Some(cancellation) => session.with_cancellation(std::sync::Arc::clone(cancellation)),
520 None => session,
521 }
522}
523
524pub(super) fn default_dead_code_options_for_context(
525 resolved: &ProgrammaticAnalysisContext,
526) -> DeadCodeOptions {
527 DeadCodeOptions {
528 analysis: AnalysisOptions {
529 root: Some(resolved.root().to_path_buf()),
530 config_path: resolved.config_path().clone(),
531 no_cache: resolved.no_cache(),
532 threads: Some(resolved.threads()),
533 production_override: resolved.production_override(),
534 changed_since: resolved.changed_since().map(str::to_owned),
535 workspace: resolved.workspace().map(<[String]>::to_vec),
536 changed_workspaces: resolved.changed_workspaces().map(str::to_owned),
537 explain: resolved.explain_enabled(),
538 ..AnalysisOptions::default()
539 },
540 filters: DeadCodeFilters::default(),
541 files: Vec::new(),
542 include_entry_exports: false,
543 }
544}
545
546fn configure_project_for_dead_code(
547 mut project_config: ProjectConfig,
548 options: &DeadCodeOptions,
549) -> ProjectConfig {
550 if options.include_entry_exports {
551 project_config.config.include_entry_exports = true;
552 }
553 activate_explicit_dead_code_opt_ins(&options.filters, &mut project_config.config.rules);
554 project_config
555}
556
557fn activate_explicit_dead_code_opt_ins(
558 filters: &DeadCodeFilters,
559 rules: &mut fallow_config::RulesConfig,
560) {
561 if filters.private_type_leaks && rules.private_type_leaks == fallow_config::Severity::Off {
562 rules.private_type_leaks = fallow_config::Severity::Warn;
563 }
564 if filters.deprecated_exports_in_use
565 && rules.deprecated_exports_in_use == fallow_config::Severity::Off
566 {
567 rules.deprecated_exports_in_use = fallow_config::Severity::Warn;
568 }
569}
570
571fn apply_dead_code_scope(
572 options: &DeadCodeOptions,
573 resolved: &ProgrammaticAnalysisContext,
574 session: &AnalysisSession,
575 changed_files: Option<&FxHashSet<std::path::PathBuf>>,
576 results: &mut AnalysisResults,
577) -> ProgrammaticResult<()> {
578 let workspace_roots = workspace_roots_for_session(resolved, session.workspaces())?;
579 let resolved_changed_files = if changed_files.is_some() {
580 None
581 } else {
582 changed_files_for_run(resolved)?
583 };
584 if changed_files.or(resolved_changed_files.as_ref()).is_some() {
585 resolved
586 .measure_changed_since_scope(session.files().iter().map(|file| file.path.as_path()));
587 }
588 let files = file_scope(options, session.root());
589 fallow_engine::dead_code::apply_scope(
590 results,
591 &fallow_engine::dead_code::DeadCodeScope {
592 workspace_roots: workspace_roots.as_deref(),
593 changed_files: changed_files.or(resolved_changed_files.as_ref()),
594 diff: resolved.diff.as_ref().map(|diff| (diff, session.root())),
595 files: files.as_ref(),
596 },
597 session.config(),
598 );
599 Ok(())
600}
601
602fn file_scope(options: &DeadCodeOptions, root: &Path) -> Option<FxHashSet<std::path::PathBuf>> {
604 if options.files.is_empty() {
605 return None;
606 }
607 Some(
608 options
609 .files
610 .iter()
611 .map(|path| {
612 if is_absolute_path_any_platform(path) {
613 path.clone()
614 } else {
615 root.join(path)
616 }
617 })
618 .collect(),
619 )
620}
621
622fn apply_dead_code_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
623 if !dead_code_filters_active(filters) {
624 return;
625 }
626 apply_dead_code_core_filters(filters, results);
627 apply_dead_code_component_filters(filters, results);
628 apply_dead_code_graph_filters(filters, results);
629 apply_dead_code_policy_filters(filters, results);
630 apply_dead_code_catalog_filters(filters, results);
631}
632
633fn dead_code_filters_active(filters: &DeadCodeFilters) -> bool {
634 filters.unused_files
635 || filters.unused_exports
636 || filters.unused_deps
637 || filters.unused_types
638 || filters.private_type_leaks
639 || filters.deprecated_exports_in_use
640 || filters.unused_enum_members
641 || filters.unused_class_members
642 || filters.unused_store_members
643 || filters.unprovided_injects
644 || filters.unrendered_components
645 || filters.unused_component_props
646 || filters.unused_component_emits
647 || filters.unused_component_inputs
648 || filters.unused_component_outputs
649 || filters.unused_svelte_events
650 || filters.unused_server_actions
651 || filters.unused_load_data_keys
652 || filters.unresolved_imports
653 || filters.unlisted_deps
654 || filters.duplicate_exports
655 || filters.circular_deps
656 || filters.re_export_cycles
657 || filters.boundary_violations
658 || filters.policy_violations
659 || filters.stale_suppressions
660 || filters.unused_catalog_entries
661 || filters.empty_catalog_groups
662 || filters.unresolved_catalog_references
663 || filters.unused_dependency_overrides
664 || filters.misconfigured_dependency_overrides
665}
666
667fn apply_dead_code_core_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
668 if !filters.unused_files {
669 results.unused_files.clear();
670 }
671 if !filters.unused_exports {
672 results.unused_exports.clear();
673 }
674 if !filters.unused_types {
675 results.unused_types.clear();
676 }
677 if !filters.private_type_leaks {
678 results.private_type_leaks.clear();
679 }
680 if !filters.deprecated_exports_in_use {
681 results.deprecated_exports_in_use.clear();
682 }
683 if !filters.unused_deps {
684 clear_dead_code_dependency_findings(results);
685 }
686 if !filters.unused_enum_members {
687 results.unused_enum_members.clear();
688 }
689 if !filters.unused_class_members {
690 results.unused_class_members.clear();
691 }
692 if !filters.unused_store_members {
693 results.unused_store_members.clear();
694 }
695 if !filters.unlisted_deps {
696 results.unlisted_dependencies.clear();
697 }
698}
699
700fn clear_dead_code_dependency_findings(results: &mut AnalysisResults) {
701 results.unused_dependencies.clear();
702 results.unused_dev_dependencies.clear();
703 results.unused_optional_dependencies.clear();
704 results.type_only_dependencies.clear();
705 results.test_only_dependencies.clear();
706 results.dev_dependencies_in_production.clear();
707}
708
709fn apply_dead_code_component_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
710 if !filters.unprovided_injects {
711 results.unprovided_injects.clear();
712 }
713 if !filters.unrendered_components {
714 results.unrendered_components.clear();
715 }
716 if !filters.unused_component_props {
717 results.unused_component_props.clear();
718 }
719 if !filters.unused_component_emits {
720 results.unused_component_emits.clear();
721 }
722 if !filters.unused_component_inputs {
723 results.unused_component_inputs.clear();
724 }
725 if !filters.unused_component_outputs {
726 results.unused_component_outputs.clear();
727 }
728 if !filters.unused_svelte_events {
729 results.unused_svelte_events.clear();
730 }
731 if !filters.unused_server_actions {
732 results.unused_server_actions.clear();
733 }
734 if !filters.unused_load_data_keys {
735 results.unused_load_data_keys.clear();
736 }
737 if !filters.unresolved_imports {
738 results.unresolved_imports.clear();
739 }
740}
741
742fn apply_dead_code_graph_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
743 if !filters.duplicate_exports {
744 results.duplicate_exports.clear();
745 }
746 if !filters.circular_deps {
747 results.circular_dependencies.clear();
748 }
749 if !filters.re_export_cycles {
750 results.re_export_cycles.clear();
751 }
752 if !filters.boundary_violations {
753 results.boundary_violations.clear();
754 results.boundary_coverage_violations.clear();
755 results.boundary_call_violations.clear();
756 }
757}
758
759fn apply_dead_code_policy_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
760 if !filters.policy_violations {
761 results.policy_violations.clear();
762 }
763 if !filters.stale_suppressions {
764 results.stale_suppressions.clear();
765 }
766}
767
768fn apply_dead_code_catalog_filters(filters: &DeadCodeFilters, results: &mut AnalysisResults) {
769 if !filters.unused_catalog_entries {
770 results.unused_catalog_entries.clear();
771 }
772 if !filters.empty_catalog_groups {
773 results.empty_catalog_groups.clear();
774 }
775 if !filters.unresolved_catalog_references {
776 results.unresolved_catalog_references.clear();
777 }
778 if !filters.unused_dependency_overrides {
779 results.unused_dependency_overrides.clear();
780 }
781 if !filters.misconfigured_dependency_overrides {
782 results.misconfigured_dependency_overrides.clear();
783 }
784}