Build hover content for a method.
(
&self,
method: &MethodInfo,
owner: &ClassInfo,
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
uri: &str,
content: &str,
)
| 1104 | |
| 1105 | /// Build hover content for a method. |
| 1106 | pub(crate) fn hover_for_method( |
| 1107 | &self, |
| 1108 | method: &MethodInfo, |
| 1109 | owner: &ClassInfo, |
| 1110 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 1111 | uri: &str, |
| 1112 | content: &str, |
| 1113 | ) -> Hover { |
| 1114 | // When the method has no declared return type and no @return |
| 1115 | // docblock, try to infer the return type from the method body. |
| 1116 | // This mirrors what completion/hover resolution does, so the |
| 1117 | // hover display matches the resolved type the user sees. |
| 1118 | let inferred_return_type: Option<crate::php_type::PhpType> = |
| 1119 | if method.return_type.is_none() && method.name_offset != 0 && !method.is_virtual { |
| 1120 | crate::completion::call_resolution::try_infer_body_return_type(&owner.fqn(), method) |
| 1121 | .filter(|t| !t.is_mixed() && !t.is_void()) |
| 1122 | } else { |
| 1123 | None |
| 1124 | }; |
| 1125 | |
| 1126 | // Use the inferred type as the effective return type when |
| 1127 | // the method has no declared one. |
| 1128 | let effective_return = method |
| 1129 | .return_type |
| 1130 | .as_ref() |
| 1131 | .or(inferred_return_type.as_ref()); |
| 1132 | |
| 1133 | let visibility = format_visibility(method.visibility); |
| 1134 | let static_kw = if method.is_static { "static " } else { "" }; |
| 1135 | let native_params = format_native_params(&method.parameters); |
| 1136 | |
| 1137 | // Use native return type in the code block, effective type as docblock annotation. |
| 1138 | let native_ret = method |
| 1139 | .native_return_type |
| 1140 | .as_ref() |
| 1141 | .map(|r| format!(": {}", r)) |
| 1142 | .unwrap_or_default(); |
| 1143 | |
| 1144 | let member_line = format!( |
| 1145 | "{}{}function {}({}){};", |
| 1146 | visibility, static_kw, method.name, native_params, native_ret |
| 1147 | ); |
| 1148 | |
| 1149 | let mut lines = Vec::new(); |
| 1150 | |
| 1151 | // When the return type or a parameter type is a template |
| 1152 | // parameter on the method or owning class, show the template's |
| 1153 | // variance and bound so the user understands the constraint. |
| 1154 | // Method-level templates take priority over class-level ones. |
| 1155 | let mut seen_templates: Vec<PhpType> = Vec::new(); |
| 1156 | if let Some(ret) = effective_return |
| 1157 | && let Some(tpl_line) = find_template_info_in_method_or_class(ret, method, owner) |
| 1158 | { |
| 1159 | seen_templates.push(ret.clone()); |
| 1160 | lines.push(tpl_line); |
| 1161 | } |
| 1162 | for param in &method.parameters { |
| 1163 | if let Some(ref hint) = param.type_hint |
no test coverage detected