1use std::sync::Arc;
7
8use mir_types::{Atomic, Union};
9
10use crate::codebase::Codebase;
11use crate::storage::{ConstantStorage, MethodStorage, PropertyStorage, Visibility};
12
13#[derive(Debug, Clone)]
15pub struct MemberInfo {
16 pub name: Arc<str>,
18 pub kind: MemberKind,
20 pub ty: Option<Union>,
22 pub visibility: Visibility,
24 pub is_static: bool,
26 pub declaring_class: Arc<str>,
28 pub deprecated: Option<Arc<str>>,
30 pub params: Vec<crate::storage::FnParam>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum MemberKind {
37 Method,
38 Property,
39 Constant,
40 EnumCase,
41}
42
43type Seen = std::collections::HashSet<(String, MemberKind)>;
48
49fn push_method(
50 name: &Arc<str>,
51 method: &MethodStorage,
52 out: &mut Vec<MemberInfo>,
53 seen: &mut Seen,
54) {
55 if seen.insert((name.to_string(), MemberKind::Method)) {
56 out.push(MemberInfo {
57 name: name.clone(),
58 kind: MemberKind::Method,
59 ty: method.effective_return_type().cloned(),
60 visibility: method.visibility,
61 is_static: method.is_static,
62 declaring_class: method.fqcn.clone(),
63 deprecated: method.deprecated.clone(),
64 params: method.params.clone(),
65 });
66 }
67}
68
69fn push_property(
70 name: &Arc<str>,
71 prop: &PropertyStorage,
72 declaring_class: Arc<str>,
73 out: &mut Vec<MemberInfo>,
74 seen: &mut Seen,
75) {
76 if seen.insert((name.to_string(), MemberKind::Property)) {
77 out.push(MemberInfo {
78 name: name.clone(),
79 kind: MemberKind::Property,
80 ty: prop.ty.clone().or_else(|| prop.inferred_ty.clone()),
81 visibility: prop.visibility,
82 is_static: prop.is_static,
83 declaring_class,
84 deprecated: None,
85 params: vec![],
86 });
87 }
88}
89
90fn push_constant(
91 name: &Arc<str>,
92 con: &ConstantStorage,
93 declaring_class: Arc<str>,
94 out: &mut Vec<MemberInfo>,
95 seen: &mut Seen,
96) {
97 if seen.insert((name.to_string(), MemberKind::Constant)) {
98 out.push(MemberInfo {
99 name: name.clone(),
100 kind: MemberKind::Constant,
101 ty: Some(con.ty.clone()),
102 visibility: con.visibility.unwrap_or(Visibility::Public),
103 is_static: true,
104 declaring_class,
105 deprecated: None,
106 params: vec![],
107 });
108 }
109}
110
111impl Codebase {
116 pub fn visible_members(&self, ty: &Union) -> Vec<MemberInfo> {
121 let mut result = Vec::new();
122 let mut seen = std::collections::HashSet::new();
123
124 for atomic in &ty.types {
125 if let Atomic::TNamedObject { fqcn, .. } = atomic {
126 self.collect_members_for_fqcn(fqcn, &mut result, &mut seen);
127 }
128 }
129
130 result
131 }
132
133 fn collect_members_for_fqcn(&self, fqcn: &str, out: &mut Vec<MemberInfo>, seen: &mut Seen) {
135 self.ensure_finalized(fqcn);
136 if let Some(cls) = self.classes.get(fqcn) {
138 let cls_fqcn = cls.fqcn.clone();
139
140 for (name, method) in &cls.own_methods {
141 push_method(name, method, out, seen);
142 }
143 for (name, prop) in &cls.own_properties {
144 push_property(name, prop, cls_fqcn.clone(), out, seen);
145 }
146 for (name, con) in &cls.own_constants {
147 push_constant(name, con, cls_fqcn.clone(), out, seen);
148 }
149
150 let own_traits = cls.traits.clone();
152 let all_parents = cls.all_parents.clone();
153 drop(cls);
154
155 for tr_fqcn in &own_traits {
156 if let Some(tr) = self.traits.get(tr_fqcn.as_ref()) {
157 let tr_fqcn = tr.fqcn.clone();
158 for (name, method) in &tr.own_methods {
159 push_method(name, method, out, seen);
160 }
161 for (name, prop) in &tr.own_properties {
162 push_property(name, prop, tr_fqcn.clone(), out, seen);
163 }
164 }
165 }
166
167 for ancestor_fqcn in &all_parents {
168 if let Some(ancestor) = self.classes.get(ancestor_fqcn.as_ref()) {
169 let anc_fqcn = ancestor.fqcn.clone();
170 for (name, method) in &ancestor.own_methods {
171 push_method(name, method, out, seen);
172 }
173 for (name, prop) in &ancestor.own_properties {
174 push_property(name, prop, anc_fqcn.clone(), out, seen);
175 }
176 for (name, con) in &ancestor.own_constants {
177 push_constant(name, con, anc_fqcn.clone(), out, seen);
178 }
179 let anc_traits = ancestor.traits.clone();
180 drop(ancestor);
181 for tr_fqcn in &anc_traits {
182 if let Some(tr) = self.traits.get(tr_fqcn.as_ref()) {
183 let tr_fqcn = tr.fqcn.clone();
184 for (name, method) in &tr.own_methods {
185 push_method(name, method, out, seen);
186 }
187 for (name, prop) in &tr.own_properties {
188 push_property(name, prop, tr_fqcn.clone(), out, seen);
189 }
190 }
191 }
192 } else if let Some(iface) = self.interfaces.get(ancestor_fqcn.as_ref()) {
193 let iface_fqcn = iface.fqcn.clone();
194 for (name, method) in &iface.own_methods {
195 push_method(name, method, out, seen);
196 }
197 for (name, con) in &iface.own_constants {
198 push_constant(name, con, iface_fqcn.clone(), out, seen);
199 }
200 }
201 }
203
204 return;
205 }
206
207 if let Some(iface) = self.interfaces.get(fqcn) {
209 let iface_fqcn = iface.fqcn.clone();
210 for (name, method) in &iface.own_methods {
211 push_method(name, method, out, seen);
212 }
213 for (name, con) in &iface.own_constants {
214 push_constant(name, con, iface_fqcn.clone(), out, seen);
215 }
216 let parents = iface.all_parents.clone();
217 drop(iface);
218 for parent_fqcn in &parents {
219 self.collect_members_for_fqcn(parent_fqcn, out, seen);
220 }
221 return;
222 }
223
224 if let Some(en) = self.enums.get(fqcn) {
226 let en_fqcn = en.fqcn.clone();
227 for (name, case) in &en.cases {
228 if seen.insert((name.to_string(), MemberKind::EnumCase)) {
229 out.push(MemberInfo {
230 name: name.clone(),
231 kind: MemberKind::EnumCase,
232 ty: case.value.clone(),
233 visibility: Visibility::Public,
234 is_static: true,
235 declaring_class: en_fqcn.clone(),
236 deprecated: None,
237 params: vec![],
238 });
239 }
240 }
241 for (name, method) in &en.own_methods {
242 push_method(name, method, out, seen);
243 }
244 for (name, con) in &en.own_constants {
245 push_constant(name, con, en_fqcn.clone(), out, seen);
246 }
247 return;
248 }
249
250 if let Some(tr) = self.traits.get(fqcn) {
252 let tr_fqcn = tr.fqcn.clone();
253 for (name, method) in &tr.own_methods {
254 push_method(name, method, out, seen);
255 }
256 for (name, prop) in &tr.own_properties {
257 push_property(name, prop, tr_fqcn.clone(), out, seen);
258 }
259 }
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266 use crate::storage::*;
267 use indexmap::IndexMap;
268
269 fn make_method(name: &str, fqcn: &str) -> MethodStorage {
270 MethodStorage {
271 name: Arc::from(name),
272 fqcn: Arc::from(fqcn),
273 params: vec![],
274 return_type: Some(Union::single(Atomic::TString)),
275 inferred_return_type: None,
276 visibility: Visibility::Public,
277 is_static: false,
278 is_abstract: false,
279 is_final: false,
280 is_constructor: false,
281 template_params: vec![],
282 assertions: vec![],
283 throws: vec![],
284 deprecated: None,
285 is_internal: false,
286 is_pure: false,
287 location: None,
288 }
289 }
290
291 #[test]
292 fn visible_members_includes_inherited() {
293 let cb = Codebase::new();
294
295 let mut parent_methods = IndexMap::new();
297 parent_methods.insert(
298 Arc::from("parentMethod"),
299 Arc::new(make_method("parentMethod", "Parent")),
300 );
301 cb.classes.insert(
302 Arc::from("Parent"),
303 ClassStorage {
304 fqcn: Arc::from("Parent"),
305 short_name: Arc::from("Parent"),
306 parent: None,
307 interfaces: vec![],
308 traits: vec![],
309 own_methods: parent_methods,
310 own_properties: IndexMap::new(),
311 own_constants: IndexMap::new(),
312 mixins: vec![],
313 template_params: vec![],
314 extends_type_args: vec![],
315 implements_type_args: vec![],
316 is_abstract: false,
317 is_final: false,
318 is_readonly: false,
319 all_parents: vec![],
320 deprecated: None,
321 is_internal: false,
322 location: None,
323 type_aliases: std::collections::HashMap::new(),
324 pending_import_types: vec![],
325 },
326 );
327
328 let mut child_methods = IndexMap::new();
330 child_methods.insert(
331 Arc::from("childMethod"),
332 Arc::new(make_method("childMethod", "Child")),
333 );
334 cb.classes.insert(
335 Arc::from("Child"),
336 ClassStorage {
337 fqcn: Arc::from("Child"),
338 short_name: Arc::from("Child"),
339 parent: Some(Arc::from("Parent")),
340 interfaces: vec![],
341 traits: vec![],
342 own_methods: child_methods,
343 own_properties: IndexMap::new(),
344 own_constants: IndexMap::new(),
345 mixins: vec![],
346 template_params: vec![],
347 extends_type_args: vec![],
348 implements_type_args: vec![],
349 is_abstract: false,
350 is_final: false,
351 is_readonly: false,
352 all_parents: vec![],
353 deprecated: None,
354 is_internal: false,
355 location: None,
356 type_aliases: std::collections::HashMap::new(),
357 pending_import_types: vec![],
358 },
359 );
360
361 cb.finalize();
362
363 let ty = Union::single(Atomic::TNamedObject {
364 fqcn: Arc::from("Child"),
365 type_params: vec![],
366 });
367 let members = cb.visible_members(&ty);
368 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
369 assert!(names.contains(&"childMethod"), "should have own method");
370 assert!(
371 names.contains(&"parentMethod"),
372 "should have inherited method"
373 );
374 }
375
376 #[test]
377 fn visible_members_union_type() {
378 let cb = Codebase::new();
379
380 let mut a_methods = IndexMap::new();
381 a_methods.insert(Arc::from("aMethod"), Arc::new(make_method("aMethod", "A")));
382 cb.classes.insert(
383 Arc::from("A"),
384 ClassStorage {
385 fqcn: Arc::from("A"),
386 short_name: Arc::from("A"),
387 parent: None,
388 interfaces: vec![],
389 traits: vec![],
390 own_methods: a_methods,
391 own_properties: IndexMap::new(),
392 own_constants: IndexMap::new(),
393 mixins: vec![],
394 template_params: vec![],
395 extends_type_args: vec![],
396 implements_type_args: vec![],
397 is_abstract: false,
398 is_final: false,
399 is_readonly: false,
400 all_parents: vec![],
401 deprecated: None,
402 is_internal: false,
403 location: None,
404 type_aliases: std::collections::HashMap::new(),
405 pending_import_types: vec![],
406 },
407 );
408
409 let mut b_methods = IndexMap::new();
410 b_methods.insert(Arc::from("bMethod"), Arc::new(make_method("bMethod", "B")));
411 cb.classes.insert(
412 Arc::from("B"),
413 ClassStorage {
414 fqcn: Arc::from("B"),
415 short_name: Arc::from("B"),
416 parent: None,
417 interfaces: vec![],
418 traits: vec![],
419 own_methods: b_methods,
420 own_properties: IndexMap::new(),
421 own_constants: IndexMap::new(),
422 mixins: vec![],
423 template_params: vec![],
424 extends_type_args: vec![],
425 implements_type_args: vec![],
426 is_abstract: false,
427 is_final: false,
428 is_readonly: false,
429 all_parents: vec![],
430 deprecated: None,
431 is_internal: false,
432 location: None,
433 type_aliases: std::collections::HashMap::new(),
434 pending_import_types: vec![],
435 },
436 );
437
438 cb.finalize();
439
440 let ty = Union::merge(
441 &Union::single(Atomic::TNamedObject {
442 fqcn: Arc::from("A"),
443 type_params: vec![],
444 }),
445 &Union::single(Atomic::TNamedObject {
446 fqcn: Arc::from("B"),
447 type_params: vec![],
448 }),
449 );
450 let members = cb.visible_members(&ty);
451 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
452 assert!(names.contains(&"aMethod"));
453 assert!(names.contains(&"bMethod"));
454 }
455}