Skip to content

Commit 5d4f2a0

Browse files
committed
Substituted return types as PhpType
1 parent 5dfc456 commit 5d4f2a0

3 files changed

Lines changed: 15 additions & 51 deletions

File tree

docs/todo.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ within the same impact tier.
2323

2424
| # | Item | Impact | Effort |
2525
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- |
26-
| T27 | [Keep substituted return types as `PhpType` in RHS resolution](todo/type-inference.md#t27-keep-substituted-return-types-as-phptype-in-rhs-resolution) | High | Low |
2726
| T29 | [Migrate `type_strings_joined` call sites to `types_joined`](todo/type-inference.md#t29-migrate-type_strings_joined-call-sites-to-types_joined) | Medium | Low |
2827
| T30 | [Thread `PhpType` through remaining string-based API boundaries](todo/type-inference.md#t30-thread-phptype-through-remaining-string-based-api-boundaries) | Medium | Medium |
2928
| | **Release 0.7.0** | | |
@@ -98,7 +97,7 @@ unlikely to move the needle for most users.
9897
| C10 | [Deprecation markers on class-name completions from all sources](todo/completion.md#c10-deprecation-markers-on-class-name-completions-from-all-sources) | Low | Low |
9998
| | **[Type Inference](todo/type-inference.md)** | | |
10099
| T25 | [Forward-walking scope model](todo/type-inference.md#t25-forward-walking-scope-model-for-variable-type-resolution) (eliminate backward-scanning depth limit) | High | Very High |
101-
| T19 | [Structured type representation](todo/type-inference.md#t19-structured-type-representation) (partially complete; remaining API boundary cleanup tracked as T27–T30) | High | Very High |
100+
| T19 | [Structured type representation](todo/type-inference.md#t19-structured-type-representation) (partially complete; remaining API boundary cleanup tracked as T29–T30) | High | Very High |
102101
| T20 | [Type narrowing reconciliation engine](todo/type-inference.md#t20-type-narrowing-reconciliation-engine) (sure/sureNot tracking, AND/OR algebra) | Medium-High | High |
103102
| T6 | `Closure::bind()` / `Closure::fromCallable()` return type preservation | Low-Medium | Low-Medium |
104103
| T12 | [Intersection types flattened to unions by `type_strings_joined`](todo/type-inference.md#t12-intersection-types-flattened-to-unions-by-type_strings_joined) (subsumed by T29) | Low-Medium | Low |

docs/todo/type-inference.md

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ The remaining work is eliminating `PhpType -> String -> PhpType`
428428
round-trips at internal API boundaries. Many functions still accept
429429
`&str` or return `Option<String>` for types, forcing callers to
430430
stringify and downstream consumers to re-parse. The concrete migration
431-
tasks are tracked as T27 through T30.
431+
tasks are tracked as T29 and T30.
432432

433433
Subtype checking (`is Cat a subtype of Animal?`), union simplification
434434
(`string|string`, `true|false -> bool`), and intersection distribution
@@ -594,39 +594,6 @@ progresses.
594594

595595
---
596596

597-
## T27. Keep substituted return types as `PhpType` in RHS resolution
598-
**Impact: High · Effort: Low**
599-
600-
In `rhs_resolution.rs`, `resolve_rhs_method_call_inner` (~L1699-1726)
601-
and `resolve_rhs_static_call` (~L1895-1916) produce a `PhpType` via
602-
`substitute() -> replace_self()`, then stringify it, only for the
603-
caller to immediately re-parse with `PhpType::parse(hint)`:
604-
605-
```
606-
let substituted = ret.substitute(&subs).replace_self(&owner.name);
607-
let ret_type_string = substituted.to_string(); // stringify
608-
// ...
609-
ResolvedType::from_classes_with_hint(classes, PhpType::parse(hint)) // re-parse
610-
```
611-
612-
This runs for every method/static call in an assignment RHS.
613-
614-
**What to change:**
615-
616-
1. Keep `ret_type_string` as `Option<PhpType>` instead of
617-
`Option<String>` in both functions.
618-
619-
2. Pass the `PhpType` directly to `from_classes_with_hint` (which
620-
already accepts `PhpType`).
621-
622-
3. Apply the same fix in `resolve_rhs_static_call`.
623-
624-
**Files:** `src/completion/variable/rhs_resolution.rs`.
625-
626-
**Part of:** T19.
627-
628-
---
629-
630597
## T29. Migrate `type_strings_joined` call sites to `types_joined`
631598
**Impact: Medium · Effort: Low**
632599

src/completion/variable/rhs_resolution.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,8 +1708,8 @@ fn resolve_rhs_method_call_inner<'b>(
17081708
None
17091709
};
17101710
match receiver_type {
1711-
Some(rt) => substituted.replace_self_with_type(&rt).to_string(),
1712-
None => substituted.replace_self(&owner.name).to_string(),
1711+
Some(rt) => substituted.replace_self_with_type(&rt),
1712+
None => substituted.replace_self(&owner.name),
17131713
}
17141714
});
17151715

@@ -1722,9 +1722,7 @@ fn resolve_rhs_method_call_inner<'b>(
17221722
if !results.is_empty() {
17231723
let classes: Vec<ClassInfo> = results.into_iter().map(Arc::unwrap_or_clone).collect();
17241724
return match ret_type_string {
1725-
Some(ref hint) => {
1726-
ResolvedType::from_classes_with_hint(classes, PhpType::parse(hint))
1727-
}
1725+
Some(hint) => ResolvedType::from_classes_with_hint(classes, hint),
17281726
None => ResolvedType::from_classes(classes),
17291727
};
17301728
}
@@ -1743,14 +1741,17 @@ fn resolve_rhs_method_call_inner<'b>(
17431741
// `@phpstan-type UserList array<int, User>` with
17441742
// `@return UserList` is expanded to its concrete type.
17451743
if let Some(ref hint) = ret_type_string {
1744+
let hint_str = hint.to_string();
17461745
let expanded = crate::completion::type_resolution::resolve_type_alias(
1747-
hint,
1746+
&hint_str,
17481747
&owner.name,
17491748
ctx.all_classes,
17501749
ctx.class_loader,
17511750
);
1752-
let effective = expanded.as_deref().unwrap_or(hint);
1753-
let parsed_effective = PhpType::parse(effective);
1751+
let parsed_effective = match expanded {
1752+
Some(ref e) => PhpType::parse(e),
1753+
None => hint.clone(),
1754+
};
17541755
if parsed_effective == PhpType::Named("void".into()) {
17551756
return vec![ResolvedType::from_type_string(PhpType::Named(
17561757
"null".into(),
@@ -1893,7 +1894,7 @@ fn resolve_rhs_static_call(
18931894
} else {
18941895
ret.clone()
18951896
};
1896-
substituted.replace_self(&owner.name).to_string()
1897+
substituted.replace_self(&owner.name)
18971898
});
18981899

18991900
let results = Backend::resolve_method_return_types_with_args(
@@ -1906,9 +1907,7 @@ fn resolve_rhs_static_call(
19061907
let classes: Vec<ClassInfo> =
19071908
results.into_iter().map(Arc::unwrap_or_clone).collect();
19081909
return match ret_type_string {
1909-
Some(ref hint) => {
1910-
ResolvedType::from_classes_with_hint(classes, PhpType::parse(hint))
1911-
}
1910+
Some(hint) => ResolvedType::from_classes_with_hint(classes, hint),
19121911
None => ResolvedType::from_classes(classes),
19131912
};
19141913
}
@@ -1919,13 +1918,12 @@ fn resolve_rhs_static_call(
19191918
// that consumers reading `.type_string` (hover, raw-type
19201919
// pipeline, null-coalesce stripping) still get the information.
19211920
if let Some(ref hint) = ret_type_string {
1922-
let parsed_hint = PhpType::parse(hint);
1923-
if parsed_hint == PhpType::Named("void".into()) {
1921+
if *hint == PhpType::Named("void".into()) {
19241922
return vec![ResolvedType::from_type_string(PhpType::Named(
19251923
"null".into(),
19261924
))];
19271925
}
1928-
return vec![ResolvedType::from_type_string(parsed_hint)];
1926+
return vec![ResolvedType::from_type_string(hint.clone())];
19291927
}
19301928
}
19311929
}

0 commit comments

Comments
 (0)