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