Build the origin indicator lines for a member. Checks whether the member is actually declared on the owner class (not just inherited), then inspects the parent class and implemented interfaces (via `class_loader`) to determine whether the member overrides a parent or implements an interface contract. Also checks `is_virtual` for synthesized members. Returns a (possibly empty) string of Markdown
(
member_name: &str,
owner: &ClassInfo,
is_virtual: bool,
member_kind: MemberKindForOrigin,
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
)
| 86 | /// Returns a (possibly empty) string of Markdown lines to prepend to the |
| 87 | /// hover content. |
| 88 | fn build_origin_lines( |
| 89 | member_name: &str, |
| 90 | owner: &ClassInfo, |
| 91 | is_virtual: bool, |
| 92 | member_kind: MemberKindForOrigin, |
| 93 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 94 | ) -> String { |
| 95 | let mut origins: Vec<MemberOrigin> = Vec::new(); |
| 96 | |
| 97 | if is_virtual { |
| 98 | origins.push(MemberOrigin::Virtual); |
| 99 | } |
| 100 | |
| 101 | // Only check for override / implements when the member is actually |
| 102 | // declared on the owner class itself (not merely inherited from a |
| 103 | // parent). Without this gate, an inherited method would incorrectly |
| 104 | // show "overrides ParentClass". |
| 105 | let declared_on_owner = raw_class_has_member(owner, member_name, &member_kind, class_loader); |
| 106 | |
| 107 | if declared_on_owner { |
| 108 | // Check parent class for override. |
| 109 | if let Some(ref parent_name) = owner.parent_class |
| 110 | && let Some(parent) = class_loader(parent_name) |
| 111 | { |
| 112 | let has_member = match member_kind { |
| 113 | MemberKindForOrigin::Method => parent |
| 114 | .methods |
| 115 | .iter() |
| 116 | .any(|m| m.name.eq_ignore_ascii_case(member_name)), |
| 117 | MemberKindForOrigin::Property => { |
| 118 | parent.properties.iter().any(|p| p.name == member_name) |
| 119 | } |
| 120 | MemberKindForOrigin::Constant => { |
| 121 | parent.constants.iter().any(|c| c.name == member_name) |
| 122 | } |
| 123 | }; |
| 124 | if has_member { |
| 125 | origins.push(MemberOrigin::Override(short_name(parent_name).to_string())); |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | // Check interfaces for implements. |
| 130 | for iface_name in &owner.interfaces { |
| 131 | if let Some(iface) = class_loader(iface_name) { |
| 132 | let has_member = match member_kind { |
| 133 | MemberKindForOrigin::Method => iface |
| 134 | .methods |
| 135 | .iter() |
| 136 | .any(|m| m.name.eq_ignore_ascii_case(member_name)), |
| 137 | MemberKindForOrigin::Property => { |
| 138 | iface.properties.iter().any(|p| p.name == member_name) |
| 139 | } |
| 140 | MemberKindForOrigin::Constant => { |
| 141 | iface.constants.iter().any(|c| c.name == member_name) |
| 142 | } |
| 143 | }; |
| 144 | if has_member { |
| 145 | origins.push(MemberOrigin::Implements(short_name(iface_name).to_string())); |
no test coverage detected