Repository navigation
fix(php): prove an unaliased global class use absent from the project - #2705
Merged
Merged
Conversation
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 <[email protected]>
…own-absent Signed-off-by: Martin Vogel <[email protected]>
…own-absent Signed-off-by: Martin Vogel <[email protected]>
…own-absent Signed-off-by: Martin Vogel <[email protected]>
3 of 4 tasks
DeusData
pushed a commit
that referenced
this pull request
Oct 20, 2026
When the PHP LSP knows a receiver's class but the class is not in the index, it emits php_method_typed_unindexed at 0.55, below the LSP confidence floor. The resolver skipped that row and the weak short-name fallback bound the call to a same-named project method: `$file->getUid()` on a vendor File became a CALLS edge to App\Item::getUid. #2520 turns this row into php_external_receiver when a vendor `use` proves the class external, but two cases still reach the fallback. A global function import (`use function sprintf;`) leaves the file's import bindings unproven, since #2705 proves only class imports absent. And a vendor type that arrives through a return type (`$repository->findFile()->getUid()`) has no `use` in the calling file at all. Both resolvers now drop a unique_name or suffix_match edge when the LSP row names a receiver class that no project class-like symbol shares by simple name. A project class with that name keeps the fallback, because the PHP LSP does not follow every cross-file type. The test pair covers both cases, through pass_calls.c and pass_parallel.c. Each case is RED on main on its own (vendorCall, chainedVendorCall); calls on a project-typed receiver and on $this still resolve. On a 211-file TYPO3 project PHP CALLS edges go from 1083 to 1059 (-18 suffix_match, -6 unique_name; typed php_* edges unchanged): 25 removed, and 1 that a removed edge on the same caller had absorbed. Signed-off-by: Wolfgang Klinger <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
PHP files record their import evidence as complete (
php_imports_complete) only when everyuseeither 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.
build/and compiles every grammar object pluslsp_all.oandprod_lsp_all.o.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 NullOutputbound toConsole\View\Components\Component,new ConstraintViolationListbound toCore\Validation\Validator), and adds 19 livewireRoute::getregistrations 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.
$this->routeCollection->addtoBus\Batch::add,$session->gettoBroadcastManager::getandStr::ofto a global helperof. Also removed:parent::calls bound to the class's own method, and$this->x()bound to magic__callwhen a used trait definesx. The trait calls now reach the trait method, at unique_name 0.38.php_*edges, for exampleparent::setUpto the project TestCase,Server::getand$this->request->getParamtoRequestInterface.CustomTapeLabelextendsLabeland uses the traitHasCustomLabelSupports. The calls$this->getSupportAssetTag(),getSupportLogo()andgetSupport1DBarcode()used to go toLabel(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.Logger::addLogunderuse Logger;. No class namedLoggerexists anywhere in the repository, so the removed guess (PrestaShopLoggerCore::addLog, unique_name 0.38) named a different class.ComponentAttributeBag, checked against the source:TextColumnandTextEntryimportIlluminate\View\ComponentAttributeBagand alias filament's own class asFilamentComponentAttributeBag, so the removed edges to filament'sComponentAttributeBag::stylewere wrong.ComponentAttributeBag::__call,parent::hasMacroandBaseComponentAttributeBag::hasMacroboth call the vendor parent, so the removed edge to the class's ownhasMacrowas wrong as well.Request/Authenticator, ApiPlatformProviderInterface, LaminasFilterInterface, builtinStringable, PHPUnit and OrchestraTestCase, ComposerEvent, MonologLogger.ISharingRegistry(6 edges) and koelPreference. The added edges include first-class callables and the firefly-iii GitHub releases call.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 plususe Exception;. Without the fix the typedphp_*call tolib/Services/Svc.phpis lost and a wrong name guess intolib/Other/Svc.phpappears; 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 onpass_pkgmap.care all clean.Head 4ae1909 merges main 27c3303. Main has not touched
pass_pkgmap.cortests/test_parallel.csince 658639e, and merge-tree is clean.