brokk_bifrost_ruby/
graph_support.rs1use crate::mixins::RubyOwnerRelationFact;
22use brokk_bifrost_core::analyzer::capabilities::{ImportAnalysisProvider, TypeHierarchyProvider};
23use brokk_bifrost_core::analyzer::model::RubyMethodDispatchMode;
24use brokk_bifrost_core::analyzer::type_relations::{TypeRelation, TypeRelationKind};
25use brokk_bifrost_core::analyzer::{CodeUnit, CodeUnitIndex, ProjectFile};
26use brokk_bifrost_core::hash::{HashMap, HashSet};
27use std::sync::Arc;
28
29pub trait RubySource: CodeUnitIndex + TypeHierarchyProvider + ImportAnalysisProvider {
30 fn all_files(&self) -> Vec<ProjectFile>;
32
33 fn autoload_constant_files(&self) -> &HashMap<String, HashSet<ProjectFile>>;
36
37 fn has_zeitwerk_autoload_conventions(&self) -> bool;
40
41 fn zeitwerk_autoload_files(&self) -> &HashSet<ProjectFile>;
42
43 fn zeitwerk_consumer_files(&self) -> &HashSet<ProjectFile>;
44
45 fn zeitwerk_autoload_code_units(&self) -> &HashSet<CodeUnit>;
46
47 fn reverse_import_index(&self) -> Arc<HashMap<ProjectFile, Arc<HashSet<ProjectFile>>>>;
50
51 fn mixin_relations(&self) -> &[TypeRelation];
54
55 fn semantic_facts(&self) -> &RubySemanticFacts;
57
58 fn types_by_identifier(&self) -> &HashMap<String, Vec<CodeUnit>>;
61
62 fn method_dispatch_mode(&self, unit: &CodeUnit) -> RubyMethodDispatchMode;
64
65 fn forward_owner_relation_facts(&self, owner: &CodeUnit) -> Vec<RubyOwnerRelationFact>;
68}
69
70pub struct RubySemanticFacts {
74 pub ancestors: HashMap<String, HashSet<String>>,
75 pub mixin_included_owners: HashMap<String, Vec<String>>,
76 pub mixin_prepended_owners: HashMap<String, Vec<String>>,
77 pub mixin_class_owners: HashMap<String, Vec<String>>,
78}
79
80pub fn build_ruby_semantic_facts(ruby: &dyn RubySource) -> RubySemanticFacts {
81 let mut ancestors = HashMap::default();
82 let mut mixin_included_owners: HashMap<String, Vec<String>> = HashMap::default();
83 let mut mixin_prepended_owners: HashMap<String, Vec<String>> = HashMap::default();
84 let mut mixin_class_owners: HashMap<String, Vec<String>> = HashMap::default();
85
86 for unit in ruby
87 .all_declarations()
88 .filter(|unit| unit.is_class() || unit.is_module())
89 {
90 let direct = ruby
91 .get_direct_ancestors(&unit)
92 .into_iter()
93 .map(|ancestor| ancestor.fq_name())
94 .collect();
95 ancestors.insert(unit.fq_name(), direct);
96 }
97
98 for relation in ruby.mixin_relations() {
99 let entry = match relation.kind {
100 TypeRelationKind::MixinInclude => &mut mixin_included_owners,
101 TypeRelationKind::MixinPrepend => &mut mixin_prepended_owners,
102 TypeRelationKind::MixinExtend => &mut mixin_class_owners,
103 _ => continue,
104 };
105 push_ordered_mixin(entry, relation.from.fq_name(), relation.to.fq_name());
106 }
107
108 RubySemanticFacts {
109 ancestors,
110 mixin_included_owners,
111 mixin_prepended_owners,
112 mixin_class_owners,
113 }
114}
115
116fn push_ordered_mixin(index: &mut HashMap<String, Vec<String>>, from: String, to: String) {
117 let owners = index.entry(from).or_default();
118 if !owners.contains(&to) {
119 owners.push(to);
120 }
121}