Skip to content

Preserve literal types in the expression resolver (PHPStan-style) #293

Description

@calebdw

Summary

The expression resolver (resolve_rhs_expression) currently widens
scalar literals to their base types (string, int, float) at
the point of resolution. This means compound expressions like
ternaries, match, and null-coalesce lose literal precision and require
a separate post-hoc narrowing pass in the diagnostic path
(narrow_literal_type) to recover it.

PHPStan preserves literal types from the start and only widens where
specifically needed. Adopting this approach would make the type system
more precise across the board — diagnostics, hover, completion — and
eliminate the need for the narrowing workaround.

Current state

  • resolve_rhs_expression returns PhpType::string() for
    Expression::Literal(Literal::String(...)), etc.
  • The diagnostic path uses narrow_literal_type() to recover
    precise literal types for argument type checking.
  • Array shape and list element tracking depend on the widened types
    (array{name: string} vs array{name: 'John'}).

What needs to change

  1. Make resolve_rhs_expression return literal types for scalar
    literals (PhpType::literal_string_raw, literal_int,
    literal_float).
  2. Add a widening step in the array shape and list tracking layers
    so that $arr['name'] = 'John' still infers
    array{name: string} rather than array{name: 'John'}.
  3. Audit and update tests — an initial attempt showed 15 failures
    in shape/list tracking tests that expect widened types.
  4. Once the resolver preserves literals, remove the
    narrow_literal_type workaround from the diagnostic path.

Context

Discovered while fixing #180. The narrowing approach works for the
immediate issue but is architecturally fragile — it requires
enumerating every compound expression form rather than getting
precision for free from the resolver.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions