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 },
323 );
324
325 let mut child_methods = IndexMap::new();
327 child_methods.insert(
328 Arc::from("childMethod"),
329 Arc::new(make_method("childMethod", "Child")),
330 );
331 cb.classes.insert(
332 Arc::from("Child"),
333 ClassStorage {
334 fqcn: Arc::from("Child"),
335 short_name: Arc::from("Child"),
336 parent: Some(Arc::from("Parent")),
337 interfaces: vec![],
338 traits: vec![],
339 own_methods: child_methods,
340 own_properties: IndexMap::new(),
341 own_constants: IndexMap::new(),
342 mixins: vec![],
343 template_params: vec![],
344 extends_type_args: vec![],
345 implements_type_args: vec![],
346 is_abstract: false,
347 is_final: false,
348 is_readonly: false,
349 all_parents: vec![],
350 deprecated: None,
351 is_internal: false,
352 location: None,
353 },
354 );
355
356 cb.finalize();
357
358 let ty = Union::single(Atomic::TNamedObject {
359 fqcn: Arc::from("Child"),
360 type_params: vec![],
361 });
362 let members = cb.visible_members(&ty);
363 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
364 assert!(names.contains(&"childMethod"), "should have own method");
365 assert!(
366 names.contains(&"parentMethod"),
367 "should have inherited method"
368 );
369 }
370
371 #[test]
372 fn visible_members_union_type() {
373 let cb = Codebase::new();
374
375 let mut a_methods = IndexMap::new();
376 a_methods.insert(Arc::from("aMethod"), Arc::new(make_method("aMethod", "A")));
377 cb.classes.insert(
378 Arc::from("A"),
379 ClassStorage {
380 fqcn: Arc::from("A"),
381 short_name: Arc::from("A"),
382 parent: None,
383 interfaces: vec![],
384 traits: vec![],
385 own_methods: a_methods,
386 own_properties: IndexMap::new(),
387 own_constants: IndexMap::new(),
388 mixins: vec![],
389 template_params: vec![],
390 extends_type_args: vec![],
391 implements_type_args: vec![],
392 is_abstract: false,
393 is_final: false,
394 is_readonly: false,
395 all_parents: vec![],
396 deprecated: None,
397 is_internal: false,
398 location: None,
399 },
400 );
401
402 let mut b_methods = IndexMap::new();
403 b_methods.insert(Arc::from("bMethod"), Arc::new(make_method("bMethod", "B")));
404 cb.classes.insert(
405 Arc::from("B"),
406 ClassStorage {
407 fqcn: Arc::from("B"),
408 short_name: Arc::from("B"),
409 parent: None,
410 interfaces: vec![],
411 traits: vec![],
412 own_methods: b_methods,
413 own_properties: IndexMap::new(),
414 own_constants: IndexMap::new(),
415 mixins: vec![],
416 template_params: vec![],
417 extends_type_args: vec![],
418 implements_type_args: vec![],
419 is_abstract: false,
420 is_final: false,
421 is_readonly: false,
422 all_parents: vec![],
423 deprecated: None,
424 is_internal: false,
425 location: None,
426 },
427 );
428
429 cb.finalize();
430
431 let ty = Union::merge(
432 &Union::single(Atomic::TNamedObject {
433 fqcn: Arc::from("A"),
434 type_params: vec![],
435 }),
436 &Union::single(Atomic::TNamedObject {
437 fqcn: Arc::from("B"),
438 type_params: vec![],
439 }),
440 );
441 let members = cb.visible_members(&ty);
442 let names: Vec<&str> = members.iter().map(|m| m.name.as_ref()).collect();
443 assert!(names.contains(&"aMethod"));
444 assert!(names.contains(&"bMethod"));
445 }
446}