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 if let Some(cls) = self.classes.get(fqcn) {
137 let cls_fqcn = cls.fqcn.clone();
138
139 for (name, method) in &cls.own_methods {
140 push_method(name, method, out, seen);
141 }
142 for (name, prop) in &cls.own_properties {
143 push_property(name, prop, cls_fqcn.clone(), out, seen);
144 }
145 for (name, con) in &cls.own_constants {
146 push_constant(name, con, cls_fqcn.clone(), out, seen);
147 }
148
149 let own_traits = cls.traits.clone();
151 let all_parents = cls.all_parents.clone();
152 drop(cls);
153
154 for tr_fqcn in &own_traits {
155 if let Some(tr) = self.traits.get(tr_fqcn.as_ref()) {
156 let tr_fqcn = tr.fqcn.clone();
157 for (name, method) in &tr.own_methods {
158 push_method(name, method, out, seen);
159 }
160 for (name, prop) in &tr.own_properties {
161 push_property(name, prop, tr_fqcn.clone(), out, seen);
162 }
163 }
164 }
165
166 for ancestor_fqcn in &all_parents {
167 if let Some(ancestor) = self.classes.get(ancestor_fqcn.as_ref()) {
168 let anc_fqcn = ancestor.fqcn.clone();
169 for (name, method) in &ancestor.own_methods {
170 push_method(name, method, out, seen);
171 }
172 for (name, prop) in &ancestor.own_properties {
173 push_property(name, prop, anc_fqcn.clone(), out, seen);
174 }
175 for (name, con) in &ancestor.own_constants {
176 push_constant(name, con, anc_fqcn.clone(), out, seen);
177 }
178 let anc_traits = ancestor.traits.clone();
179 drop(ancestor);
180 for tr_fqcn in &anc_traits {
181 if let Some(tr) = self.traits.get(tr_fqcn.as_ref()) {
182 let tr_fqcn = tr.fqcn.clone();
183 for (name, method) in &tr.own_methods {
184 push_method(name, method, out, seen);
185 }
186 for (name, prop) in &tr.own_properties {
187 push_property(name, prop, tr_fqcn.clone(), out, seen);
188 }
189 }
190 }
191 } else if let Some(iface) = self.interfaces.get(ancestor_fqcn.as_ref()) {
192 let iface_fqcn = iface.fqcn.clone();
193 for (name, method) in &iface.own_methods {
194 push_method(name, method, out, seen);
195 }
196 for (name, con) in &iface.own_constants {
197 push_constant(name, con, iface_fqcn.clone(), out, seen);
198 }
199 }
200 }
202
203 return;
204 }
205
206 if let Some(iface) = self.interfaces.get(fqcn) {
208 let iface_fqcn = iface.fqcn.clone();
209 for (name, method) in &iface.own_methods {
210 push_method(name, method, out, seen);
211 }
212 for (name, con) in &iface.own_constants {
213 push_constant(name, con, iface_fqcn.clone(), out, seen);
214 }
215 let parents = iface.all_parents.clone();
216 drop(iface);
217 for parent_fqcn in &parents {
218 self.collect_members_for_fqcn(parent_fqcn, out, seen);
219 }
220 return;
221 }
222
223 if let Some(en) = self.enums.get(fqcn) {
225 let en_fqcn = en.fqcn.clone();
226 for (name, case) in &en.cases {
227 if seen.insert((name.to_string(), MemberKind::EnumCase)) {
228 out.push(MemberInfo {
229 name: name.clone(),
230 kind: MemberKind::EnumCase,
231 ty: case.value.clone(),
232 visibility: Visibility::Public,
233 is_static: true,
234 declaring_class: en_fqcn.clone(),
235 deprecated: None,
236 params: vec![],
237 });
238 }
239 }
240 for (name, method) in &en.own_methods {
241 push_method(name, method, out, seen);
242 }
243 for (name, con) in &en.own_constants {
244 push_constant(name, con, en_fqcn.clone(), out, seen);
245 }
246 return;
247 }
248
249 if let Some(tr) = self.traits.get(fqcn) {
251 let tr_fqcn = tr.fqcn.clone();
252 for (name, method) in &tr.own_methods {
253 push_method(name, method, out, seen);
254 }
255 for (name, prop) in &tr.own_properties {
256 push_property(name, prop, tr_fqcn.clone(), out, seen);
257 }
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use crate::storage::*;
266 use indexmap::IndexMap;
267
268 fn make_method(name: &str, fqcn: &str) -> MethodStorage {
269 MethodStorage {
270 name: Arc::from(name),
271 fqcn: Arc::from(fqcn),
272 params: vec![],
273 return_type: Some(Union::single(Atomic::TString)),
274 inferred_return_type: None,
275 visibility: Visibility::Public,
276 is_static: false,
277 is_abstract: false,
278 is_final: false,
279 is_constructor: false,
280 template_params: vec![],
281 assertions: vec![],
282 throws: vec![],
283 deprecated: None,
284 is_internal: false,
285 is_pure: false,
286 location: None,
287 }
288 }
289
290 #[test]
291 fn visible_members_includes_inherited() {
292 let cb = Codebase::new();
293
294 let mut parent_methods = IndexMap::new();
296 parent_methods.insert(
297 Arc::from("parentMethod"),
298 Arc::new(make_method("parentMethod", "Parent")),
299 );
300 cb.classes.insert(
301 Arc::from("Parent"),
302 ClassStorage {
303 fqcn: Arc::from("Parent"),
304 short_name: Arc::from("Parent"),
305 parent: None,
306 interfaces: vec![],
307 traits: vec![],
308 own_methods: parent_methods,
309 own_properties: IndexMap::new(),
310 own_constants: IndexMap::new(),
311 mixins: vec![],
312 template_params: vec![],
313 extends_type_args: vec![],
314 implements_type_args: vec![],
315 is_abstract: false,
316 is_final: false,
317 is_readonly: false,
318 all_parents: vec![],
319 deprecated: None,
320 is_internal: false,
321 location: None,
322 type_aliases: std::collections::HashMap::new(),
323 pending_import_types: vec![],
324 },
325 );
326
327 let mut child_methods = IndexMap::new();
329 child_methods.insert(
330 Arc::from("childMethod"),
331 Arc::new(make_method("childMethod", "Child")),
332 );
333 cb.classes.insert(
334 Arc::from("Child"),
335 ClassStorage {
336 fqcn: Arc::from("Child"),
337 short_name: Arc::from("Child"),
338 parent: Some(Arc::from("Parent")),
339 interfaces: vec![],
340 traits: vec![],
341 own_methods: child_methods,
342 own_properties: IndexMap::new(),
343 own_constants: IndexMap::new(),
344 mixins: vec![],
345 template_params: vec![],
346 extends_type_args: vec![],
347 implements_type_args: vec![],
348 is_abstract: false,
349 is_final: false,
350 is_readonly: false,
351 all_parents: vec![],
352 deprecated: None,
353 is_internal: false,
354 location: None,
355 type_aliases: std::collections::HashMap::new(),
356 pending_import_types: vec![],
357 },
358 );
359
360 cb.finalize();
361
362 let ty = Union::single(Atomic::TNamedObject {
363 fqcn: Arc::from("Child"),
364 type_params: vec![],
365 });
366 let members = cb.visible_members(&ty);
367 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
368 assert!(names.contains(&"childMethod"), "should have own method");
369 assert!(
370 names.contains(&"parentMethod"),
371 "should have inherited method"
372 );
373 }
374
375 #[test]
376 fn visible_members_union_type() {
377 let cb = Codebase::new();
378
379 let mut a_methods = IndexMap::new();
380 a_methods.insert(Arc::from("aMethod"), Arc::new(make_method("aMethod", "A")));
381 cb.classes.insert(
382 Arc::from("A"),
383 ClassStorage {
384 fqcn: Arc::from("A"),
385 short_name: Arc::from("A"),
386 parent: None,
387 interfaces: vec![],
388 traits: vec![],
389 own_methods: a_methods,
390 own_properties: IndexMap::new(),
391 own_constants: IndexMap::new(),
392 mixins: vec![],
393 template_params: vec![],
394 extends_type_args: vec![],
395 implements_type_args: vec![],
396 is_abstract: false,
397 is_final: false,
398 is_readonly: false,
399 all_parents: vec![],
400 deprecated: None,
401 is_internal: false,
402 location: None,
403 type_aliases: std::collections::HashMap::new(),
404 pending_import_types: vec![],
405 },
406 );
407
408 let mut b_methods = IndexMap::new();
409 b_methods.insert(Arc::from("bMethod"), Arc::new(make_method("bMethod", "B")));
410 cb.classes.insert(
411 Arc::from("B"),
412 ClassStorage {
413 fqcn: Arc::from("B"),
414 short_name: Arc::from("B"),
415 parent: None,
416 interfaces: vec![],
417 traits: vec![],
418 own_methods: b_methods,
419 own_properties: IndexMap::new(),
420 own_constants: IndexMap::new(),
421 mixins: vec![],
422 template_params: vec![],
423 extends_type_args: vec![],
424 implements_type_args: vec![],
425 is_abstract: false,
426 is_final: false,
427 is_readonly: false,
428 all_parents: vec![],
429 deprecated: None,
430 is_internal: false,
431 location: None,
432 type_aliases: std::collections::HashMap::new(),
433 pending_import_types: vec![],
434 },
435 );
436
437 cb.finalize();
438
439 let ty = Union::merge(
440 &Union::single(Atomic::TNamedObject {
441 fqcn: Arc::from("A"),
442 type_params: vec![],
443 }),
444 &Union::single(Atomic::TNamedObject {
445 fqcn: Arc::from("B"),
446 type_params: vec![],
447 }),
448 );
449 let members = cb.visible_members(&ty);
450 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
451 assert!(names.contains(&"aMethod"));
452 assert!(names.contains(&"bMethod"));
453 }
454}