MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / bind_foreach_value

Function bind_foreach_value

src/completion/variable/forward_walk.rs:6528–6735  ·  view source on GitHub ↗

Bind a foreach value variable from the iterable's element type. Resolution strategy: 1. Try `PhpType::extract_value_type` — works for types that already carry generic parameters (e.g. `list `, `array `, `Collection `). 2. Class-based fallback — when the type is a bare class name (e.g. `OrderProductCollection`), resolve it to `ClassInfo`, merge inheritance, and extract

(
    value_expr: &'b Expression<'b>,
    iter_type: &Option<PhpType>,
    scope: &mut ScopeState,
    ctx: &ForwardWalkCtx<'_>,
)

Source from the content-addressed store, hash-verified

6526/// `@implements` generics. This mirrors what
6527/// `try_resolve_foreach_value_type` does in the backward scanner.
6528fn bind_foreach_value<'b>(
6529 value_expr: &'b Expression<'b>,
6530 iter_type: &Option<PhpType>,
6531 scope: &mut ScopeState,
6532 ctx: &ForwardWalkCtx<'_>,
6533) {
6534 // Unwrap `&$value` (by-reference foreach) to get the inner variable.
6535 let value_expr = if let Expression::UnaryPrefix(up) = value_expr
6536 && matches!(up.operator, UnaryPrefixOperator::Reference(_))
6537 {
6538 up.operand
6539 } else {
6540 value_expr
6541 };
6542 if let Expression::Variable(Variable::Direct(dv)) = value_expr {
6543 let var_name = dv.name.to_string();
6544 if let Some(it) = iter_type {
6545 // Strategy 1: extract from the type's own generic parameters.
6546 let value_php_type = it.extract_value_type(false);
6547 if let Some(vt) = value_php_type {
6548 let resolved = crate::completion::type_resolution::type_hint_to_classes_typed(
6549 vt,
6550 &ctx.current_class.name,
6551 ctx.all_classes,
6552 ctx.class_loader,
6553 );
6554 if !resolved.is_empty() {
6555 scope.set(
6556 &var_name,
6557 ResolvedType::from_classes_with_hint(resolved, vt.clone()),
6558 );
6559 } else {
6560 scope.set(&var_name, vec![ResolvedType::from_type_string(vt.clone())]);
6561 }
6562 return;
6563 }
6564
6565 // Strategy 2: class-based fallback for bare collection names.
6566 let element_via_class = resolve_iterable_element_via_class(it, ctx);
6567 if let Some(element_type) = element_via_class
6568 && !is_unsubstituted_template_param(&element_type)
6569 {
6570 let resolved = crate::completion::type_resolution::type_hint_to_classes_typed(
6571 &element_type,
6572 &ctx.current_class.name,
6573 ctx.all_classes,
6574 ctx.class_loader,
6575 );
6576 if !resolved.is_empty() {
6577 scope.set(
6578 &var_name,
6579 ResolvedType::from_classes_with_hint(resolved, element_type),
6580 );
6581 } else {
6582 scope.set(
6583 &var_name,
6584 vec![ResolvedType::from_type_string(element_type)],
6585 );

Callers 1

process_foreachFunction · 0.85

Calls 12

extract_value_typeMethod · 0.80
setMethod · 0.80
cloneMethod · 0.80
containsMethod · 0.80
set_emptyMethod · 0.80
iterMethod · 0.80
shape_value_typeMethod · 0.80
is_emptyMethod · 0.45

Tested by

no test coverage detected