Find the class that originally declares a member. When a member is inherited (not declared on `owner` itself), this walks up the parent chain and checks traits and mixins to find the class that actually declares the member. Returns a fully-resolved `ClassInfo` for the declaring class, or falls back to `owner` when the declaring class cannot be determined. This is used by hover and completion-re
(
owner: &ClassInfo,
member_name: &str,
member_kind: &MemberKindForOrigin,
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
)
| 187 | /// shows `class Model { public static function find(...) }` rather than |
| 188 | /// `class User { ... }` when `find()` is inherited from `Model`. |
| 189 | pub(crate) fn find_declaring_class( |
| 190 | owner: &ClassInfo, |
| 191 | member_name: &str, |
| 192 | member_kind: &MemberKindForOrigin, |
| 193 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 194 | ) -> Arc<ClassInfo> { |
| 195 | // If the member is declared directly on the owner, no need to search. |
| 196 | if raw_class_has_member(owner, member_name, member_kind, class_loader) { |
| 197 | return Arc::new(owner.clone()); |
| 198 | } |
| 199 | |
| 200 | // Check traits used by the owner. |
| 201 | for trait_name in &owner.used_traits { |
| 202 | if let Some(trait_class) = class_loader(trait_name) { |
| 203 | let has = match member_kind { |
| 204 | MemberKindForOrigin::Method => trait_class |
| 205 | .methods |
| 206 | .iter() |
| 207 | .any(|m| m.name.eq_ignore_ascii_case(member_name)), |
| 208 | MemberKindForOrigin::Property => { |
| 209 | trait_class.properties.iter().any(|p| p.name == member_name) |
| 210 | } |
| 211 | MemberKindForOrigin::Constant => { |
| 212 | trait_class.constants.iter().any(|c| c.name == member_name) |
| 213 | } |
| 214 | }; |
| 215 | if has { |
| 216 | return trait_class; |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | // Walk the parent chain. |
| 222 | let mut ancestor_name = owner.parent_class; |
| 223 | let mut depth = 0u32; |
| 224 | while let Some(ref name) = ancestor_name { |
| 225 | depth += 1; |
| 226 | if depth > 20 { |
| 227 | break; |
| 228 | } |
| 229 | if let Some(ancestor) = class_loader(name) { |
| 230 | // Check traits on the ancestor first. |
| 231 | for trait_name in &ancestor.used_traits { |
| 232 | if let Some(trait_class) = class_loader(trait_name) { |
| 233 | let has = match member_kind { |
| 234 | MemberKindForOrigin::Method => trait_class |
| 235 | .methods |
| 236 | .iter() |
| 237 | .any(|m| m.name.eq_ignore_ascii_case(member_name)), |
| 238 | MemberKindForOrigin::Property => { |
| 239 | trait_class.properties.iter().any(|p| p.name == member_name) |
| 240 | } |
| 241 | MemberKindForOrigin::Constant => { |
| 242 | trait_class.constants.iter().any(|c| c.name == member_name) |
| 243 | } |
| 244 | }; |
| 245 | if has { |
| 246 | return trait_class; |
no test coverage detected