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