Skip to content

fix(php): prove an unaliased global class use absent from the project - #2705

Merged
DeusData merged 4 commits into
mainfrom
fix/php-global-use-known-absent
Oct 19, 2026
Merged

DeusData merged 4 commits into
mainfrom
fix/php-global-use-known-absent

Conversation

@DeusData

Copy link
Copy Markdown
Owner

What

PHP files record their import evidence as complete (php_imports_complete) only when every use either resolved to a project node or is proven absent from the project. Only then can the PHP LSP trust the bindings and type receivers through them (#1186 / #2520).

A global-namespace class use such as use Exception; had no absence proof. One such line, common in almost every PHP project, left the whole file's bindings unknown. Its project imports then reached the PHP LSP as File QNs again, so typed resolution fell back to name guesses.

An unaliased global class use is now proven absent when no PHP file in the project declares a class-like symbol of that name: Class, Interface, Trait, Enum, File or Module. Namespaced ones are included, so the proof stays conservative. Aliased global uses (use X as Y;) keep the old, unknown verdict.

Evidence

Corpus run on all 27 PHP corpus repos, against a clean build of main ea5c1f7.

venue CALLS attributes changed INHERITS USAGE TESTS
parallel +10,686 / -13,416 8,578 -360 -1,484 +4,770 / -5,892
sequential +10,500 / -13,587 8,732 -360 -1,404 +4,747 / -5,982

Also moving (par / seq): IMPLEMENTS -6 / -5, OVERRIDE -11 / -10, CONFIGURES +13 -63 / +13 -61, CALL_REFERENCE +3 / +3, HTTP_CALLS +1 / +1, SPAWNS 0 / +2. SEMANTICALLY_RELATED moves +1,047 -1,004 / +1,011 -889; that pass is TF-IDF over the whole repo, and these edges were not judged one by one. Every other edge type is unchanged on both venues. Parallel covers 27 repos and sequential 26: sebastianbergmann/phpunit hangs in sequential mode on main as well.

CALLS by strategy (par): removed suffix_match 12,008 and unique_name 1,394; added php_method_inherited 8,486, php_static_resolved 1,873 and php_self_static 307. Sequential also removes 446 import_map guesses that parallel never made (new NullOutput bound to Console\View\Components\Component, new ConstraintViolationList bound to Core\Validation\Validator), and adds 19 livewire Route::get registrations that parallel already had.

Judged sample: 105 edges, seed 20261017, 5 per type x strategy x direction, with source snippets (screened). Questionable cases were checked against the pinned source.

  • CALLS (51): 44 correct, 6 mixed, 1 leaning correct.
    • Removals are name guesses that bound a vendor or builtin receiver to an unrelated project method, for example $this->routeCollection->add to Bus\Batch::add, $session->get to BroadcastManager::get and Str::of to a global helper of. Also removed: parent:: calls bound to the class's own method, and $this->x() bound to magic __call when a used trait defines x. The trait calls now reach the trait method, at unique_name 0.38.
    • Additions are typed php_* edges, for example parent::setUp to the project TestCase, Server::get and $this->request->getParam to RequestInterface.
    • Mixed (snipe-it, 3 call sites, 6 rows): CustomTapeLabel extends Label and uses the trait HasCustomLabelSupports. The calls $this->getSupportAssetTag(), getSupportLogo() and getSupport1DBarcode() used to go to Label (php_method_inherited, 0.95). They now go to the trait's method of the same name (suffix_match, 0.02). PHP gives a trait method precedence over an inherited one, so the new target is the right one, but its confidence is low.
    • Leaning correct (PrestaShop, 1): Logger::addLog under use Logger;. No class named Logger exists anywhere in the repository, so the removed guess (PrestaShopLoggerCore::addLog, unique_name 0.38) named a different class.
  • Filament ComponentAttributeBag, checked against the source:
    • TextColumn and TextEntry import Illuminate\View\ComponentAttributeBag and alias filament's own class as FilamentComponentAttributeBag, so the removed edges to filament's ComponentAttributeBag::style were wrong.
    • In filament's ComponentAttributeBag::__call, parent::hasMacro and BaseComponentAttributeBag::hasMacro both call the vendor parent, so the removed edge to the class's own hasMacro was wrong as well.
  • Other edge types (34): all correct.
    • IMPLEMENTS, INHERITS, OVERRIDE and USAGE removals all name vendor or builtin types: Saloon Request/Authenticator, ApiPlatform ProviderInterface, Laminas FilterInterface, builtin Stringable, PHPUnit and Orchestra TestCase, Composer Event, Monolog Logger.
    • CONFIGURES, CALL_REFERENCE and HTTP_CALLS additions are typed. CONFIGURES also moves from implementation guesses (0.28) to the declared type (0.95): nextcloud ISharingRegistry (6 edges) and koel Preference. The added edges include first-class callables and the firefly-iii GitHub releases call.
  • TESTS (10): they follow the CALLS verdicts.

Tests (parallel, both resolvers):

  • parallel_php_lsp_types_imported_project_receivers_issue1186.
  • parallel_php_lsp_types_imported_project_receivers_unknown_bindings_issue1186: the same fixture plus use Exception;. Without the fix the typed php_* call to lib/Services/Svc.php is lost and a wrong name guess into lib/Other/Svc.php appears; with it, both are right.

Suites on this branch (a7f24ea), ASan test-runner: parallel, pipeline, php_lsp, extraction_imports, edge_imports, incremental, 1,026 passed. Lint: lint-memory-core, the NOLINT check, Homebrew clang-format and CI's cppcheck 2.20 on pass_pkgmap.c are all clean.

Head 4ae1909 merges main 27c3303. Main has not touched pass_pkgmap.c or tests/test_parallel.c since 658639e, and merge-tree is clean.

A PHP file's import evidence is written as complete only when every `use`
either resolved or is proven absent. A global-namespace class use such as
`use Exception;` had no absence proof, so one such line left the whole
file's bindings unknown, and its project imports reached the PHP LSP as
File QNs again (the #2520 path).

An unaliased global class use is now proven absent when no PHP file
declares a class-like symbol of that name (Class, Interface, Trait, Enum,
File or Module; namespaced ones included, so the proof stays
conservative). Aliased global uses keep the old, unknown verdict.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…own-absent

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…own-absent

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…own-absent

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData
DeusData merged commit d4980ad into main Oct 19, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant