Repository navigation
fix(php): keep proven vendor types out of project name fallbacks - #2520
Conversation
|
Checkpoint and handover (2026-10-03 UTC) Published head: PHP vendor type handling is published with the allocator/ownership follow-up. Review covered target filtering, namespace keys tracked as OTHER, borrowed values and libc-owned allocations; the raw-allocation baseline was not relaxed. The earlier lint failure requires a fresh hosted result on this corrected head. Local lint/runtime verification was not run. Import prerequisites are #2509 and #2515. Hosted snapshot at 2026-10-03 21:54:18 UTC: 3 queued. Confirm the required checks on this exact head before treating it as ready. No additional local build, test, lint, sanitizer, benchmark or CI runs were performed at this checkpoint, as requested. Earlier executed evidence remains historical; prepared tests and the newer source-reviewed changes must still be validated by the hosted gate. The campaign is paused at the maintainer’s request. Local monitoring has stopped; hosted jobs remain running. No merge was performed. Thanks for reviewing this change. |
Preserve the arena-owned raw Module caller name so top-level resolutions join the extractor in both pipeline drivers. Add occurrence and ownership controls, including package modules and named lambdas. Signed-off-by: Martin Vogel <[email protected]>
Resolve symbols declared in __init__.py under their package while retaining the raw module identity for file and top-level caller nodes. Carry explicit language through registry callers, select Python package imports, preserve long exact names, and report scope allocation failures without partial output. Signed-off-by: Martin Vogel <[email protected]>
Use tracked zeroed storage for the parallel LSP failure array and release it with the matching memory class after worker joins. Share import-filter failure cleanup without changing fallback state. Keep existing raw-allocation allowances unchanged. Signed-off-by: Martin Vogel <[email protected]>
cbm_scope_lookup_local() returns the frame binding (CBMVarBinding), not its type, so passing it to pylsp_init_named_type() did not compile (-Werror=incompatible-pointer-types). Add pylsp_local_type(), which returns the binding's type field or NULL when the name is unbound, and assert on that type as the test intended. Signed-off-by: Martin Vogel <[email protected]>
…alk call The cross and register scope-failure cases defined S and called s.m() inside pkg/__init__.py. The per-file LSP already resolves those calls, and the parallel driver skips the cross-file walk for a file whose call sites are all resolved, so CBM_TEST_PY_LSP_SCOPE_FAIL_STAGE=cross and =register never fired on the parallel path and both parallel cases saw no file error. For those two stages, move S into memo_def.py (imported by pkg/__init__.py) so run() holds calls only the cross walk resolves, and count that module's File node. The raw stage keeps its single-file fixture; the error assertions are unchanged. With the corrected fixture all eight package-scope pipeline cases pass; removing the CBM_PY_LSP_SCOPE_ERROR capture in pass_parallel.c turns exactly the two parallel cross/register cases RED again. Signed-off-by: Martin Vogel <[email protected]>
…vice_edge The call now carries both the route mount and the caller language; wrap its arguments the way clang-format lays them out so lint-format passes. No code change. Signed-off-by: Martin Vogel <[email protected]>
Signed-off-by: Martin Vogel <[email protected]>
6e2c18b to
d3d1a67
Compare
Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
Use tracked allocations for binding arrays and namespace keys, share IMPORTS cleanup, and consolidate namespace construction failure cleanup. Preserve libc ownership for existing FQN values and JSON writer buffers. Remove the redundant condition in binding validation. Signed-off-by: Martin Vogel <[email protected]>
collect_use_declaration dropped the group prefix of `use A\B\{C as D}`
and read the function/const keyword only from the declaration, so a
grouped alias mapped to the bare `C` with the declaration's kind. The
cross-file LSP then could not recognise a grouped vendor class import as
external, and a grouped `function`/`const` alias was registered as a
class. Prefix each grouped clause with the group namespace and take the
keyword from the clause when the declaration has none, mirroring
emit_php_use_decl in extract_imports.c.
Refs #1186
Signed-off-by: Martin Vogel <[email protected]>
When a cross-file lsp_cross_crate or php_external_receiver row supersedes the per-file records of its exact parser occurrence, every matching record was rewritten in place. Two earlier records at one occurrence therefore became two identical rows, which the dedup table only indexes and never collapses, so a single call site carried a duplicated resolution. Rewrite the first matching record and drop the others at that occurrence; records at other occurrences are untouched. Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
Two gaps let a call on a vendor class escape the #1186 evidence: - A vendor class the PHP LSP knows from its seeded library data (for example GuzzleHttp.Client) resolved as php_static_resolved or php_method_typed. Its target has no graph node either, but without the php_external_receiver strategy the pipeline fell through to a name-only registry binding. Methods found on a class reached through a vendor `use` now carry php_external_receiver as well. - A vendor call skips the registry, and the extractor names a scoped call by its method alone (only a literal `Route::` scope is kept), so `GuzzleClient::get('/x')` reached the route-verb fallback and became a route registration instead of an HTTP call. When the callee text names no service, the service patterns now also see the vendor method the LSP proved (`GuzzleHttp.Client.get`, `Kafka.Producer.publish`), in both the sequential and the parallel pass. Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
The vendor-types test never got past its extraction precondition, so two of its assertions had never run against production: - The precondition looked for the import alias in the callee text of `GuzzleClient::get` and `KafkaProducer::publish`. The extractor names a scoped call by its method alone and keeps only a literal `Route::` scope (#952). The aliased scope is now proven on the occurrence text at the call's site span, which identifies the same call just as strictly. - The sequential/parallel parity fingerprint hashed the HTTP_CALLS and ASYNC_CALLS properties whole. The parallel emitter appends the call's "args" member last; the sequential pass leaves route/service properties without it by design (calls_emit_edge). Those two edge types are now compared with only that trailing member removed; their type, endpoints and every other property must still agree. Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
The parallel pass appends the call's ,"args":[...] member to every HTTP_CALLS and ASYNC_CALLS edge (finalize_and_emit). The sequential pass appended args to CALLS only, so the same service call produced different edge properties depending on which pass indexed the repo, and create_data_flows had no caller_args for small (sequential) repos. Split the args-and-closing-brace step out of finalize_and_emit as cbm_pipeline_close_call_props and let the sequential emit_http_async_edge build its props in the same CBM_SZ_2K buffer and finish them with that function. Field order, expression sanitizing, escaping and truncation are therefore one implementation; CALLS edges and their "line" member are unchanged in both passes. Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
Both passes now emit byte-identical HTTP_CALLS and ASYNC_CALLS properties, so the vendor-types parity fingerprint hashes them whole again instead of trimming the "args" member. The precondition change from the previous fixture correction stays. Refs #1186 Signed-off-by: Martin Vogel <[email protected]>
d3d1a67 to
87a815a
Compare
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 DeusData#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]>
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]>
PHP imports whose declared namespaces are absent from the indexed project can currently resolve constructors, inherited types, usages, and typed receiver calls to unrelated project symbols sharing a short name. This adds typed, per-file import evidence and uses it before PHP refinement and generic name fallback in both pipeline paths. Exact semantic targets and HTTP/ASYNC classification retain precedence.
Both PHP IMPORTS writers retain class/function/constant provenance, including same-alias bindings that share an edge. File evidence is accepted only when the full current import manifest and stored edges agree. Rebuilds invalidate the old completion marker before allocating replacement metadata. Namespace-map construction fails closed on detectable allocation or insertion failures so a partial map cannot prove absence. Borrowed evidence is released before result disposal or spill/reload. Binding arrays and namespace keys use tracked memory-core allocation and matching cleanup; existing FQN values and JSON writer buffers retain their allocator ownership. Sequential and parallel import paths share cleanup without increasing the raw-allocation baseline.
The absence proof is intentionally bounded. Missing namespace maps, overlong names, unresolved global/path imports, and covered-but-missing or empty-prefix PSR-4 imports leave the whole file unknown; resolved IMPORTS remain. Ordinary usage sites with genuinely ambiguous import kinds may remain unresolved. This does not claim full PHP import-kind coverage or complete resolution of #1186.
Adds regressions for known-empty versus unknown evidence, grouped import kinds and project positives, pre-refinement property/return/trait cases, same-occurrence stale results, service calls, and sequential/parallel spill parity.
Validation: UNRUN by explicit user waiver: local builds, tests, compiler/syntax checks, lint, sanitizers, benchmarks, revert proof, and CI. Changed-range formatting with clang-format 20, source/ownership review, and diff/byte/literal/hash integrity checks were performed. Runtime and lint confirmation are deferred to PR CI. Existing graphs need reindexing to obtain the new PHP import evidence.
Base: main. Depends on #2515 (the import-resolution prerequisite); this branch includes that prerequisite. Refs #1186.