Skip to content

Commit d828958

Browse files
committed
Switch to type_hint_to_classes_typed with pre-parsed PhpType
1 parent 7e30105 commit d828958

20 files changed

Lines changed: 202 additions & 225 deletions

File tree

docs/todo.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ within the same impact tier.
2525
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- |
2626
| T19 | [Structured type representation](todo/type-inference.md#t19-structured-type-representation) (migrate remaining callers; already in progress) | High | Very High |
2727
| T26 | [Migrate `resolve_effective_type` / `should_override_type` to accept `PhpType`](todo/type-inference.md#t26-typed-resolve_effective_type-and-should_override_type) | Medium | Medium |
28-
| T27 | [Migrate `type_hint_to_classes` callers to `type_hint_to_classes_typed`](todo/type-inference.md#t27-migrate-type_hint_to_classes-callers-to-typed-variant) | Medium | Low |
2928
| T28 | [Migrate `enrichment_snippet` / `enrichment_plain` to accept `PhpType`](todo/type-inference.md#t28-migrate-enrichment-functions-to-accept-phptype) | Low | Low |
3029
| T29 | [Unify `ArrayShapeEntry` with `ShapeEntry`](todo/type-inference.md#t29-unify-arrayshapeentry-with-shapeentry) | Low | Low |
3130
| T30 | [Migrate `inline_use_generics` to `Vec<(String, Vec<PhpType>)>`](todo/type-inference.md#t30-migrate-inline_use_generics-to-phptype) | Low | Low |

docs/todo/type-inference.md

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -470,25 +470,6 @@ delegate to the typed variants for backward compatibility.
470470

471471
---
472472

473-
## T27. Migrate `type_hint_to_classes` callers to typed variant
474-
**Impact: Medium · Effort: Low**
475-
476-
`type_hint_to_classes_typed()` in `completion/types/resolution.rs`
477-
already exists and accepts a `&PhpType`. The string-based
478-
`type_hint_to_classes()` entry point just parses and delegates.
479-
Several callers still use the string version even when they already
480-
have a parsed `PhpType` available, causing redundant parse round-trips.
481-
482-
**Task:** Audit all call sites of `type_hint_to_classes()`, migrate
483-
those that already hold a `PhpType` to use `type_hint_to_classes_typed()`,
484-
and deprecate or remove the string entry point once all callers are
485-
migrated.
486-
487-
**Files:** `src/completion/types/resolution.rs` and all call sites
488-
(grep for `type_hint_to_classes(`)
489-
490-
---
491-
492473
## T28. Migrate enrichment functions to accept `PhpType`
493474
**Impact: Low · Effort: Low**
494475

src/completion/call_resolution.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -528,9 +528,10 @@ impl Backend {
528528
resolve_conditional_without_args(cond, &func_info.parameters)
529529
};
530530
if let Some(ref ty) = resolved_type {
531+
let parsed_ty = PhpType::parse(ty);
531532
let classes: Vec<Arc<ClassInfo>> =
532-
super::type_resolution::type_hint_to_classes(
533-
ty,
533+
super::type_resolution::type_hint_to_classes_typed(
534+
&parsed_ty,
534535
"",
535536
ctx.all_classes,
536537
ctx.class_loader,
@@ -631,9 +632,10 @@ impl Backend {
631632
cursor_offset,
632633
)
633634
{
635+
let parsed_ret = PhpType::parse(&ret);
634636
let classes: Vec<Arc<ClassInfo>> =
635-
super::type_resolution::type_hint_to_classes(
636-
&ret,
637+
super::type_resolution::type_hint_to_classes_typed(
638+
&parsed_ret,
637639
"",
638640
ctx.all_classes,
639641
ctx.class_loader,
@@ -650,9 +652,10 @@ impl Backend {
650652
if let Some(ret) =
651653
super::source::helpers::extract_first_class_callable_return_type(var_name, ctx)
652654
{
655+
let parsed_ret = PhpType::parse(&ret);
653656
let classes: Vec<Arc<ClassInfo>> =
654-
super::type_resolution::type_hint_to_classes(
655-
&ret,
657+
super::type_resolution::type_hint_to_classes_typed(
658+
&parsed_ret,
656659
"",
657660
ctx.all_classes,
658661
ctx.class_loader,

src/completion/handler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@ impl Backend {
852852
// `collect($x)->`) doesn't crash the LSP server process.
853853
// The variable-resolution path already has its own
854854
// catch_unwind, but the direct call-expression path
855-
// (resolve_call_return_types_expr → type_hint_to_classes
855+
// (resolve_call_return_types_expr → type_hint_to_classes_typed
856856
// class_loader → find_or_load_class → parse_php →
857857
// resolve_class_with_inheritance) does not.
858858
let member_items = crate::util::catch_panic_unwind_safe(

src/completion/resolver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,7 @@ pub(crate) fn resolve_target_classes_expr(
833833
// the property's raw type hint. This preserves generic
834834
// parameters like `array<string, IntCollection>` or
835835
// `Collection<int, Translation>` that would be lost if
836-
// we resolved through `type_hint_to_classes` first.
836+
// we resolved through `type_hint_to_classes_typed` first.
837837
let property_raw_type: Option<PhpType> = if let SubjectExpr::PropertyChain {
838838
base: prop_base,
839839
property,

src/completion/source/helpers.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -565,9 +565,8 @@ fn walk_array_segments_and_resolve(
565565
// its iterable generics (`@extends`, `@implements`) for the
566566
// element type. This handles bracket access on classes that
567567
// implement `ArrayAccess` with generic type parameters.
568-
let type_str = current.to_string();
569-
let class_element = crate::completion::type_resolution::type_hint_to_classes(
570-
&type_str,
568+
let class_element = crate::completion::type_resolution::type_hint_to_classes_typed(
569+
&current,
571570
current_class_name,
572571
all_classes,
573572
class_loader,

src/completion/types/narrowing.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,9 @@ pub(in crate::completion) fn try_apply_instanceof_narrowing(
9696
{
9797
let mut union = Vec::new();
9898
for cls_name in &classes {
99-
let resolved = super::resolution::type_hint_to_classes(
100-
cls_name,
99+
let parsed = PhpType::parse(cls_name);
100+
let resolved = super::resolution::type_hint_to_classes_typed(
101+
&parsed,
101102
&ctx.current_class.name,
102103
ctx.all_classes,
103104
ctx.class_loader,
@@ -124,8 +125,9 @@ pub(in crate::completion) fn try_apply_instanceof_narrowing(
124125
{
125126
let mut union = Vec::new();
126127
for cls_name in &classes {
127-
let resolved = super::resolution::type_hint_to_classes(
128-
cls_name,
128+
let parsed = PhpType::parse(cls_name);
129+
let resolved = super::resolution::type_hint_to_classes_typed(
130+
&parsed,
129131
&ctx.current_class.name,
130132
ctx.all_classes,
131133
ctx.class_loader,
@@ -213,8 +215,9 @@ pub(in crate::completion) fn apply_instanceof_inclusion(
213215
ctx: &VarResolutionCtx<'_>,
214216
results: &mut Vec<ClassInfo>,
215217
) {
216-
let narrowed = super::resolution::type_hint_to_classes(
217-
cls_name,
218+
let parsed = PhpType::parse(cls_name);
219+
let narrowed = super::resolution::type_hint_to_classes_typed(
220+
&parsed,
218221
&ctx.current_class.name,
219222
ctx.all_classes,
220223
ctx.class_loader,
@@ -402,8 +405,9 @@ pub(in crate::completion) fn apply_instanceof_exclusion(
402405
ctx: &VarResolutionCtx<'_>,
403406
results: &mut Vec<ClassInfo>,
404407
) {
405-
let excluded = super::resolution::type_hint_to_classes(
406-
cls_name,
408+
let parsed = PhpType::parse(cls_name);
409+
let excluded = super::resolution::type_hint_to_classes_typed(
410+
&parsed,
407411
&ctx.current_class.name,
408412
ctx.all_classes,
409413
ctx.class_loader,
@@ -1094,8 +1098,9 @@ pub(in crate::completion) fn try_apply_assert_instanceof_narrowing(
10941098
{
10951099
let mut union = Vec::new();
10961100
for cls_name in &classes {
1097-
let resolved = super::resolution::type_hint_to_classes(
1098-
cls_name,
1101+
let parsed = PhpType::parse(cls_name);
1102+
let resolved = super::resolution::type_hint_to_classes_typed(
1103+
&parsed,
10991104
&ctx.current_class.name,
11001105
ctx.all_classes,
11011106
ctx.class_loader,
@@ -1798,8 +1803,9 @@ pub(in crate::completion) fn apply_guard_clause_narrowing(
17981803
{
17991804
let mut union = Vec::new();
18001805
for cls_name in &classes {
1801-
let resolved = super::resolution::type_hint_to_classes(
1802-
cls_name,
1806+
let parsed = PhpType::parse(cls_name);
1807+
let resolved = super::resolution::type_hint_to_classes_typed(
1808+
&parsed,
18031809
&ctx.current_class.name,
18041810
ctx.all_classes,
18051811
ctx.class_loader,
@@ -2229,8 +2235,9 @@ fn would_exclude_all_results(
22292235
results: &[ClassInfo],
22302236
ctx: &VarResolutionCtx<'_>,
22312237
) -> bool {
2232-
let excluded = super::resolution::type_hint_to_classes(
2233-
element_type,
2238+
let parsed = PhpType::parse(element_type);
2239+
let excluded = super::resolution::type_hint_to_classes_typed(
2240+
&parsed,
22342241
&ctx.current_class.name,
22352242
ctx.all_classes,
22362243
ctx.class_loader,

src/completion/types/resolution.rs

Lines changed: 14 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/// Type-hint resolution to concrete `ClassInfo` values.
22
///
3-
/// This module contains the logic for mapping type-hint strings (as they
4-
/// appear in return types, property annotations, and PHPDoc tags) to
5-
/// resolved `ClassInfo` values that the completion, hover, and definition
6-
/// engines can work with.
3+
/// This module contains the logic for mapping parsed [`PhpType`] values
4+
/// (as they appear in return types, property annotations, and PHPDoc
5+
/// tags) to resolved `ClassInfo` values that the completion, hover, and
6+
/// definition engines can work with.
77
///
88
/// Split from [`super::resolver`] for navigability. The entry points are:
99
///
10-
/// - [`type_hint_to_classes`]: the public facade that maps a
11-
/// type-hint string to all matching `ClassInfo` values (handles unions,
12-
/// intersections, generics, `self`/`static`/`$this`, nullable types,
13-
/// object shapes, and type alias expansion).
10+
/// - [`type_hint_to_classes_typed`]: maps a parsed [`PhpType`] to all
11+
/// matching `ClassInfo` values (handles unions, intersections, generics,
12+
/// `self`/`static`/`$this`, nullable types, object shapes, and type
13+
/// alias expansion).
1414
/// - [`resolve_type_alias`]: fully expands a type alias defined
1515
/// via `@phpstan-type` / `@psalm-type` / `@phpstan-import-type`.
1616
/// - [`resolve_property_types`]: resolves a property's type hint
@@ -47,12 +47,11 @@ pub(crate) fn resolve_property_types(
4747
type_hint_to_classes_typed(&type_hint, &class_info.name, all_classes, class_loader)
4848
}
4949

50-
/// Map a type-hint string to all matching `ClassInfo` values.
50+
/// Map a parsed [`PhpType`] to all matching `ClassInfo` values.
5151
///
5252
/// Handles:
5353
/// - Nullable types: `?Foo` → strips `?`, resolves `Foo`
5454
/// - Union types: `A|B|C` → resolves each part independently
55-
/// (respects `<…>` nesting so `Collection<int|string>` is not split)
5655
/// - Intersection types: `A&B` → resolves each part independently
5756
/// - Generic types: `Collection<int, User>` → resolves `Collection`,
5857
/// then applies generic substitution (`TKey→int`, `TValue→User`)
@@ -62,18 +61,9 @@ pub(crate) fn resolve_property_types(
6261
/// `false`, `true`) → skipped (not class types)
6362
///
6463
/// Each resolvable class-like part is returned as a separate entry.
65-
pub(crate) fn type_hint_to_classes(
66-
type_hint: &str,
67-
owning_class_name: &str,
68-
all_classes: &[Arc<ClassInfo>],
69-
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
70-
) -> Vec<ClassInfo> {
71-
type_hint_to_classes_depth(type_hint, owning_class_name, all_classes, class_loader, 0)
72-
}
73-
74-
/// Like [`type_hint_to_classes`], but accepts a pre-parsed [`PhpType`]
75-
/// to avoid a parse→stringify→reparse round-trip when the caller already
76-
/// has a structured type.
64+
///
65+
/// Callers that start with a raw type string should parse it with
66+
/// `PhpType::parse()` first.
7767
pub(crate) fn type_hint_to_classes_typed(
7868
ty: &PhpType,
7969
owning_class_name: &str,
@@ -83,32 +73,8 @@ pub(crate) fn type_hint_to_classes_typed(
8373
type_hint_to_classes_typed_depth(ty, owning_class_name, all_classes, class_loader, 0)
8474
}
8575

86-
/// Inner implementation of [`type_hint_to_classes`] with a recursion
87-
/// depth guard to prevent infinite loops from circular type aliases.
88-
fn type_hint_to_classes_depth(
89-
type_hint: &str,
90-
owning_class_name: &str,
91-
all_classes: &[Arc<ClassInfo>],
92-
class_loader: &dyn Fn(&str) -> Option<Arc<ClassInfo>>,
93-
depth: u8,
94-
) -> Vec<ClassInfo> {
95-
if depth > MAX_ALIAS_DEPTH {
96-
return vec![];
97-
}
98-
99-
// Parse first — PhpType::parse handles `?Foo`, `(A&B)|C`, and all
100-
// other syntax natively, making manual string preprocessing redundant.
101-
// Alias resolution is handled by `resolve_named_type` when the parsed
102-
// type turns out to be a `Named` variant.
103-
let parsed = PhpType::parse(type_hint);
104-
type_hint_to_classes_typed_depth(&parsed, owning_class_name, all_classes, class_loader, depth)
105-
}
106-
107-
/// Inner implementation that operates on a pre-parsed [`PhpType`].
108-
///
109-
/// Union and intersection members are recursed directly without
110-
/// stringifying and re-parsing. Named types still go through
111-
/// string-based alias resolution when necessary.
76+
/// Inner implementation with a recursion depth guard to prevent
77+
/// infinite loops from circular type aliases.
11278
fn type_hint_to_classes_typed_depth(
11379
ty: &PhpType,
11480
owning_class_name: &str,

0 commit comments

Comments
 (0)