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