@@ -2512,110 +2512,17 @@ impl Backend {
25122512
25132513/// Resolve an arbitrary expression to a [`PhpType`].
25142514///
2515- /// Unlike [`super::resolver::resolve_target_classes`] (which returns
2516- /// class-bearing `ResolvedType` values and drops scalars), this function
2517- /// returns the *type* of the expression — including scalars like `string`,
2518- /// `int`, `bool`, etc.
2519- ///
2520- /// Strategy:
2521- /// 1. Parse the expression with [`SubjectExpr::parse`].
2522- /// 2. For property chains (`$var->prop`), resolve the base to classes
2523- /// and look up the property's type hint directly.
2524- /// 3. For method calls (`$var->method()`), resolve the base and look
2525- /// up the method's return type.
2526- /// 4. For everything else, fall back to [`super::resolver::resolve_target_classes`]
2527- /// which handles variables, `$this`, `new …`, etc.
2515+ /// Delegates to [`super::resolver::resolve_target_classes`] which
2516+ /// handles all expression patterns (variables, property chains,
2517+ /// method calls, static accesses, etc.) and preserves scalar types
2518+ /// through the `type_string` field of [`ResolvedType`].
25282519fn resolve_expression_to_type ( text : & str , ctx : & ResolutionCtx < ' _ > ) -> Option < PhpType > {
2529- let expr = SubjectExpr :: parse ( text) ;
2530-
2531- match & expr {
2532- // $var->prop or $this->prop — resolve base to classes, then
2533- // look up the property's type hint (which preserves scalars).
2534- SubjectExpr :: PropertyChain { base, property } => {
2535- let base_text = base. to_subject_text ( ) ;
2536- let base_classes = ResolvedType :: into_arced_classes (
2537- super :: resolver:: resolve_target_classes ( & base_text, crate :: AccessKind :: Arrow , ctx) ,
2538- ) ;
2539- for cls in & base_classes {
2540- if let Some ( hint) =
2541- crate :: inheritance:: resolve_property_type_hint ( cls, property, ctx. class_loader )
2542- {
2543- return Some ( hint) ;
2544- }
2545- }
2546- None
2547- }
2548-
2549- // $var->method() or $this->helper->getText() — resolve the
2550- // full call expression to return-type classes first; if that
2551- // yields nothing class-like, resolve the raw return type
2552- // (which preserves scalars).
2553- SubjectExpr :: CallExpr { callee, args_text } => {
2554- // Try class-bearing resolution first (handles non-scalar returns).
2555- // Use the _with_hint variant to capture the raw return type
2556- // (which preserves generic parameters like `ASTArtifactList<ASTClass>`).
2557- // Without this, generic args are lost and downstream template
2558- // substitution resolves to unsubstituted template params.
2559- let mut return_hint: Option < PhpType > = None ;
2560- let classes = Backend :: resolve_call_return_types_expr_with_hint (
2561- callee,
2562- args_text,
2563- ctx,
2564- Some ( & mut return_hint) ,
2565- ) ;
2566- if let Some ( first) = classes. first ( ) {
2567- // Prefer the return type hint (preserves generics) over
2568- // the bare class FQN. Fall back to FQN when no hint
2569- // was captured or when the hint is a raw template name.
2570- if let Some ( ref hint) = return_hint
2571- && !hint. is_untyped ( )
2572- && !hint. is_mixed ( )
2573- {
2574- return Some ( hint. clone ( ) ) ;
2575- }
2576- return Some ( PhpType :: Named ( first. fqn ( ) . to_string ( ) ) ) ;
2577- }
2578-
2579- // Fall back to raw return type for scalar returns.
2580- if let SubjectExpr :: MethodCall { base, method } = callee. as_ref ( ) {
2581- let base_text = base. to_subject_text ( ) ;
2582- let base_classes =
2583- ResolvedType :: into_arced_classes ( super :: resolver:: resolve_target_classes (
2584- & base_text,
2585- crate :: AccessKind :: Arrow ,
2586- ctx,
2587- ) ) ;
2588- for cls in & base_classes {
2589- if let Some ( rt) = crate :: inheritance:: resolve_method_return_type (
2590- cls,
2591- method,
2592- ctx. class_loader ,
2593- ) {
2594- return Some ( rt) ;
2595- }
2596- }
2597- }
2598- if let SubjectExpr :: StaticMethodCall { class, method } = callee. as_ref ( )
2599- && let Some ( owner) = super :: resolver:: resolve_static_owner_class ( class, ctx)
2600- && let Some ( rt) =
2601- crate :: inheritance:: resolve_method_return_type ( & owner, method, ctx. class_loader )
2602- {
2603- return Some ( rt) ;
2604- }
2605- None
2606- }
2607-
2608- // Everything else ($var, $this, ClassName, new X, etc.) —
2609- // use the standard resolver which returns class-bearing types.
2610- _ => {
2611- let resolved =
2612- super :: resolver:: resolve_target_classes ( text, crate :: types:: AccessKind :: Arrow , ctx) ;
2613- if let Some ( first) = resolved. first ( ) {
2614- return Some ( first. type_string . clone ( ) ) ;
2615- }
2616- None
2617- }
2520+ let results =
2521+ super :: resolver:: resolve_target_classes ( text, crate :: types:: AccessKind :: Arrow , ctx) ;
2522+ if let Some ( first) = results. first ( ) {
2523+ return Some ( first. type_string . clone ( ) ) ;
26182524 }
2525+ None
26192526}
26202527
26212528/// Resolve a `ClassName::Member` expression to a type.
0 commit comments