(
sym: &SymbolInfo,
indent: &str,
content: &str,
position: Position,
use_map: &HashMap<String, String>,
file_namespace: &Option<String>,
local_classes: &[Arc<ClassInfo>],
| 1437 | /// Same enrichment logic as the snippet builder, but without tab stops. |
| 1438 | #[allow(clippy::too_many_arguments)] |
| 1439 | fn build_function_plain( |
| 1440 | sym: &SymbolInfo, |
| 1441 | indent: &str, |
| 1442 | content: &str, |
| 1443 | position: Position, |
| 1444 | use_map: &HashMap<String, String>, |
| 1445 | file_namespace: &Option<String>, |
| 1446 | local_classes: &[Arc<ClassInfo>], |
| 1447 | class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>, |
| 1448 | function_loader: FunctionLoader<'_>, |
| 1449 | ) -> String { |
| 1450 | let throws_ctx = ThrowsContext { |
| 1451 | class_loader, |
| 1452 | function_loader, |
| 1453 | use_map, |
| 1454 | file_namespace, |
| 1455 | }; |
| 1456 | let uncaught = throws_analysis::find_uncaught_throw_types_with_context( |
| 1457 | content, |
| 1458 | position, |
| 1459 | Some(&throws_ctx), |
| 1460 | ); |
| 1461 | |
| 1462 | // Collect @param tags that need enrichment. |
| 1463 | let mut param_tags: Vec<(String, String)> = Vec::new(); |
| 1464 | for (type_hint, name) in &sym.params { |
| 1465 | if let Some(enriched) = enrichment_plain(type_hint.as_ref(), class_loader) { |
| 1466 | param_tags.push((enriched, name.clone())); |
| 1467 | } |
| 1468 | } |
| 1469 | |
| 1470 | // Constructors never get @return. |
| 1471 | let is_constructor = sym |
| 1472 | .method_name |
| 1473 | .as_ref() |
| 1474 | .is_some_and(|n| n.eq_ignore_ascii_case("__construct")); |
| 1475 | let is_void = sym.return_type.as_ref().is_some_and(|r| r.is_void()); |
| 1476 | let return_tag = if is_void || is_constructor { |
| 1477 | None |
| 1478 | } else { |
| 1479 | // Try body-based inference first (produces richer types like |
| 1480 | // `list<string>` instead of `array<mixed>`). |
| 1481 | let body_inferred = crate::code_actions::phpstan::fix_return_type::enrichment_return_type( |
| 1482 | content, |
| 1483 | position, |
| 1484 | local_classes, |
| 1485 | class_loader, |
| 1486 | function_loader, |
| 1487 | ); |
| 1488 | // Filter out types that don't need a @return tag (void, scalars |
| 1489 | // that match the native hint exactly). |
| 1490 | let inferred = body_inferred.filter(|t| { |
| 1491 | !t.is_void() |
| 1492 | && !t.is_mixed() |
| 1493 | && sym.return_type.as_ref().is_none_or(|s| !t.equivalent(s)) |
| 1494 | }); |
| 1495 | // Fall back to signature-based enrichment when body inference |
| 1496 | // doesn't produce anything useful. |
no test coverage detected