Commit Graph
4 Commits
Author SHA1 Message Date
Martin Vogel 20dd651a2e feat(php-lsp): @return $this/static, if-condition call walk, +50 tests
Phase 4aa, 4ab partial, 4y/z best-effort, 5e: more stdlib + tests.

  - @return $this / @return static / @return self in PHPDoc all bind to
    the enclosing class type, enabling fluent-builder chains.
  - Declared return-type re-parse uses ctx->enclosing_class_qn so the
    `: self` shorthand on a method correctly returns NAMED(class).
  - php_resolve_class_name now checks self/static/parent BEFORE the
    builtin-name guard, fixing a long-standing miss where these
    pseudo-types were rejected as builtins.
  - if-condition walk: calls inside `if ($it->valid())` are now
    resolved (they previously only got narrowing analysis).
  - Removed duplicate Generator type registration in stdlib data.

Stdlib expansion (Phase 5e):
  - Symfony Cache (CacheInterface, ItemInterface) + PSR Cache pool/item
  - Symfony EventDispatcher (EventDispatcherInterface, Event)
  - Symfony Mailer (MailerInterface) + Mime\\Email fluent builder
  - Symfony Validator (ValidatorInterface, ConstraintViolationListInterface)
  - Laravel Http\\Request, JsonResponse, Response with method chains
  - Laravel Auth\\Guard, Authenticatable contracts
  - Laravel Session, View
  - ReactPHP / GuzzleHttp Promise interfaces
  - Monolog Logger (extends PSR LoggerInterface)
  - Reflection API (ReflectionClass/Method/Property/Function)

Tests: 150 -> 200 covering @return $this fluent chains, Symfony
Cache/Mailer/Validator, Laravel Request/Auth/View, Promise chains,
Monolog, Reflection, multi-interface impl, deep parent chains, foreach
key-value chains, and a realistic Laravel UserController integration.

Total tests: 2963 -> 3013 / 0 failed.
2026-05-09 14:31:26 +02:00
Martin Vogel a3a64fc009 feat(php-lsp): generic substitution + 25 more features toward LSP parity
Phase 4m–w in one push, plus 25 new tests (100 → 125, all green).

New capabilities:

  - Generic @template substitution:
    @template T on a class records T as a type-param;
    @return T on a method gets parsed as NAMED("T");
    when receiver is TEMPLATE("Container", [User]), method-call return
    types are substituted T → User so chains like
    `$container->get()->name()` resolve through to the element type.
  - Closure use(...) capture: typed variables in `use ($a, $b)` clauses
    of `function () use (...) {}` are inherited from the parent scope.
  - Multi-conjunction narrowing: `if ($x instanceof A && $y instanceof B)`
    narrows BOTH $x and $y in the if-body. Up to 8 simultaneous narrowings.
  - is_a($x, Foo::class) narrowing — both the ::class and string forms.
  - Subscript inference: $arr[$k] on `array<T>` returns T;
    on `array<K, V>` returns V;
    on a NAMED ArrayAccess class consults registered offsetGet.
  - Enum case access: Suit::Hearts evaluates to NAMED(Suit) so
    Suit::Hearts->label() resolves to Suit::label.
  - @extends ParentClass<X>: appends ParentClass to embedded_types so
    the inheritance walk picks up methods from generic ancestors.
  - PHPDoc @return on methods with no declared return type updates
    the registry signature.
  - foreach key=>value: handles tree-sitter-php's `pair` node so
    `foreach ($users as $idx => $user)` binds $user to the element type.
  - Class-level @template-extends, @template-implements parsed without
    crashing.
  - Don't override TEMPLATE/NAMED bindings with `null` literal —
    preserves @var-typed variables across `$x = null` initialization.

Stdlib expansion (Phase 5d):
  - Symfony Console: SymfonyStyle, InputInterface, OutputInterface
  - Doctrine ORM: EntityManagerInterface, QueryBuilder chain, Query,
    EntityRepository
  - Guzzle HTTP: Client and ClientInterface (returning PSR-7 responses)
  - Twig: Environment, TemplateWrapper
  - Eloquent Model: forwarded static methods (where, whereIn, etc.)
    so `User::where()->first()` chains resolve immediately.

Test coverage: 100 -> 125 tests; total 2913 -> 2938 / 0 failed.
2026-05-09 11:10:28 +02:00
Martin Vogel 82d0935fbd feat(php-lsp): Phase 4 — depth toward LSP-server parity
Adds the resolver passes that close most of the gap to a real PHP language
server's type inference, while staying in-process and PHP-runtime-free.

New capabilities:
  - Type narrowing: instanceof, is_string/int/float/bool/array/object/...
    is_callable, is_iterable, is_numeric. Active inside if/elseif bodies
    and after assert(...) (sequential narrowing)
  - Property type tracking: typed property declarations, constructor
    property promotion (public X $foo), and constructor-body inference
    ($this->bar = $bar where $bar is typed)
  - Late-static-binding chain depth: full ancestor walk with cycle
    detection for $obj->method() and Class::method() resolution
  - Trait flattening: methods from `use Trait;` inside a class body are
    re-registered with the using class as receiver; basic `as` aliasing
    is honored
  - PHPDoc class-level tags: @property TYPE $name binds a virtual field
    on the class; @method TYPE name() registers a virtual method
    (Eloquent-style dynamic dispatch)
  - Match expression result type: union over arm result expressions,
    first concrete type wins
  - Ternary result type: prefers the consequent, falls back to alternative
  - Variadic parameter binding: $args bound to `array`
  - `use Vendor\\Helper as H;` two-name form correctly maps H -> Vendor.Helper
  - Static-call unindexed fallback: when class is resolved from `use`
    but methods unindexed, emit php_static_unindexed for the bridge
  - Stdlib expansion: full Eloquent Builder + Model + Collection method
    chains, Symfony HttpFoundation Request/Response, Carbon date/time,
    PSR-7 with*() builders

Test suite expanded from 16 to 81 tests (2829 -> 2893 total).
2026-05-09 02:05:39 +02:00
Martin Vogel d674b43e0d feat(php-lsp): add Light Semantic Pass for PHP
Mirrors go_lsp.c and c_lsp.c. In-process C type resolver, no PHP runtime,
no new dependencies. Uses already-vendored tree-sitter-php.

Coverage (Phase 1+2+3 from PLAN_PHP_LSP_INTEGRATION):
  - namespace + use clause resolution (class / function / const)
  - typed parameter binding (incl. arrow functions and closures)
  - $this and self/parent/static dispatch
  - method chaining via return-type propagation
  - object_creation_expression typing for new X() chains
  - PHPDoc minimal parser (@var, @param) for legacy untyped code
  - magic methods: __call (php_method_dynamic), __callStatic (php_dynamic_unresolved)
  - synthetic php_method_typed_unindexed for vendor receiver types,
    used by the pipeline bridge to suppress name-fallback misroutes
  - corpus-seeded stdlib (~200 lines): SPL containers/iterators, PSR
    interfaces, DateTime, Throwable hierarchy, Closure, plus the top
    Laravel framework facades + base types from the benchmark corpus
2026-05-08 23:32:52 +02:00