Resolve go-to-implementation for a class/interface name. Resolves `name` to a fully-qualified class, checks that it is an interface or abstract class (or a non-final concrete class that may have subclasses), finds all implementors/subclasses, and returns their declaration locations.
(
&self,
uri: &str,
content: &str,
name: &str,
ctx: &FileContext,
name_offset: u32,
)
| 129 | /// may have subclasses), finds all implementors/subclasses, and |
| 130 | /// returns their declaration locations. |
| 131 | fn resolve_class_implementation( |
| 132 | &self, |
| 133 | uri: &str, |
| 134 | content: &str, |
| 135 | name: &str, |
| 136 | ctx: &FileContext, |
| 137 | name_offset: u32, |
| 138 | ) -> Option<Vec<Location>> { |
| 139 | let class_loader = self.class_loader(ctx); |
| 140 | |
| 141 | let fqn = ctx.resolve_name_at(name, name_offset); |
| 142 | let target = class_loader(&fqn) |
| 143 | .or_else(|| class_loader(name)) |
| 144 | .map(Arc::unwrap_or_clone)?; |
| 145 | |
| 146 | // Final classes cannot be extended, so there are no implementations. |
| 147 | if target.is_final { |
| 148 | return None; |
| 149 | } |
| 150 | |
| 151 | // Whether the target is a concrete (non-abstract, non-interface) |
| 152 | // class. When it is, we include abstract subclasses in the |
| 153 | // results because the user is exploring the class hierarchy |
| 154 | // rather than looking for instantiable implementations. |
| 155 | let target_is_concrete = target.kind != ClassLikeKind::Interface && !target.is_abstract; |
| 156 | |
| 157 | let target_short = target.name; |
| 158 | // Compute target FQN from the class's own namespace (most |
| 159 | // reliable), then fall back to fqn_uri_index, then to the FQN we |
| 160 | // resolved from the use-map, and finally to the short name. |
| 161 | let target_fqn = { |
| 162 | let from_class = crate::util::build_fqn(&target.name, target.file_namespace.as_deref()); |
| 163 | if from_class.contains('\\') { |
| 164 | from_class |
| 165 | } else { |
| 166 | self.class_fqn_for_short(&target_short).unwrap_or_else(|| { |
| 167 | if fqn.contains('\\') { |
| 168 | fqn.clone() |
| 169 | } else { |
| 170 | target_short.to_string() |
| 171 | } |
| 172 | }) |
| 173 | } |
| 174 | }; |
| 175 | |
| 176 | let implementors = self.find_implementors( |
| 177 | &target_short, |
| 178 | &target_fqn, |
| 179 | &class_loader, |
| 180 | target_is_concrete, |
| 181 | false, |
| 182 | ); |
| 183 | |
| 184 | if implementors.is_empty() { |
| 185 | return None; |
| 186 | } |
| 187 | |
| 188 | let mut locations = Vec::new(); |
no test coverage detected