fix(php): give grouped function and const imports their own identity - #1
Closed
DivyamTalwar wants to merge 2 commits into
Closed
fix(php): give grouped function and const imports their own identity#1DivyamTalwar wants to merge 2 commits into
DivyamTalwar wants to merge 2 commits into
Conversation
Problem PHP grouped function and const imports acquire class-style graph identities. Their imports edges can be redirected to sourceless FQNs or unrelated classes, and same-named class and symbol imports can collapse into one wrong target. Root cause The PHP import boundary discarded effective import kind before emitting edges. Tree-sitter stores homogeneous group kinds on namespace_use_declaration and homogeneous comma-list kinds only on the first clause, while mixed groups keep kind on each symbol clause. The PHP namespace resolver also classified clauses without this inherited kind. Once class and symbol edges shared a bare ID, the PHP FQN path or generic unique-stub rewire treated both as class references. Approach Derive effective kind from the clause, its declaration, or the first governed clause when the internal PHP extractor creates each edge. Mark function/const edges before deduplication, propagate the same kind when building the class-use map, and skip only marked targets during generic stub rewiring. Public extract_php strips the marker immediately; internal aggregate dispatch retains it through cache and resolution, then removes it before returning the graph. This preserves same-line class/symbol imports, mixed groups, multi-namespace files, and legitimate class relations while retaining symbol targets. Rejected alternatives reconstructed kind from line/target pairs, disabled class FQN resolution, or deleted bad stubs after other relations were corrupted. Verification INITIAL RED: FF [100%] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_aliases_are_not_class_imports[function] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_aliases_are_not_class_imports[const] 2 failed in 0.14s SHARED-STUB RED: FFFF [100%] 4 failed, 1 warning in 0.15s PRODUCER-IDENTITY RED: FFF [100%] FAILED tests/test_php_type_resolution.py::test_php_symbol_import_does_not_hide_same_named_class_import FAILED tests/test_php_type_resolution.py::test_php_mixed_group_preserves_same_named_class_and_function_imports FAILED tests/test_php_type_resolution.py::test_php_symbol_import_survives_multi_namespace_resolution_skip 3 failed, 1 warning in 0.35s PUBLIC-EXTRACTOR RED: F [100%] FAILED tests/test_php_type_resolution.py::test_php_single_file_extractor_hides_symbol_import_marker 1 failed, 1 warning in 0.14s GREEN: ................... [100%] 19 passed, 1 warning in 0.13s PHP LANGUAGE CONTROLS: ................... [100%] 19 passed, 388 deselected, 1 warning in 0.11s FINAL TEST-ONLY MUTATION: FFFFFFF [100%] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_aliases_are_not_class_imports[function] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_aliases_are_not_class_imports[const] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_import_does_not_share_class_rewire[function] FAILED tests/test_php_type_resolution.py::test_php_grouped_symbol_import_does_not_share_class_rewire[const] FAILED tests/test_php_type_resolution.py::test_php_symbol_import_does_not_hide_same_named_class_import FAILED tests/test_php_type_resolution.py::test_php_mixed_group_preserves_same_named_class_and_function_imports FAILED tests/test_php_type_resolution.py::test_php_symbol_import_survives_multi_namespace_resolution_skip 7 failed in 0.59s mutation_test_exit=1 The repository gate is run after this commit so its record can certify the final commit SHA; the run artifacts carry the gate result and record path. Impact Grouped, homogeneous comma-separated, aliased, same-line, mixed, and multi-namespace function/const imports retain symbol targets and no longer influence class-style resolution. No dependency, public API, or output schema changes. Risk / rollback Internal AST/cache edges temporarily carry a private marker; public direct and aggregate extraction paths are covered for cleanup. Roll back with git revert HEAD while this commit is the branch tip. Closes none
Owner
Author
|
Superseded by the upstream contribution: Graphify-Labs#3466. This fork-internal PR was opened before the target was corrected to the original repository; the branch and commits are unchanged. |
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.
Problem
PHP grouped symbol imports are misclassified as class imports.
Given
use function App\Helpers\{slug as s};, the aliassis recorded in the class-use map. The resolver then mints a sourcelessApp\Helpers\slugclass node and points the file's imports edge at it instead of retaining the symbol target. A function import acquires a class-style graph identity it should never have had.Root cause
_record_use_clauseingraphify/extractors/resolution.pydecided "is this a class import?" by scanning the clause's own children for afunctionorconsttoken, returning early if it found one.For a grouped import, tree-sitter attaches that token to the parent declaration, not to the nested clause. The nested clause therefore never sees it, the guard never fires, and the grouped symbol import falls through into the class path.
The guard was correct for ungrouped imports and structurally blind to grouped ones.
Approach
Propagate the declaration-level kind into the clauses beneath it.
_record_use_clausetakes adeclaration_kind, supplied by the caller that already knows whether the declaration saidfunctionorconst.returnon encountering the token becomesclause_kind = c.type, so a token on the clause itself is still honoured and now records rather than aborts.clause_kind.That ordering is the fix. Deciding before parsing is what made the original blind to a token living one level up.
Verification
The failing test was written and committed before the fix, so the fix is falsifiable:
Eight regression tests cover grouped aliases, mixed class-and-function groups, multi-namespace files, and a same-named class and function in one group.
The repository's own CI gate, run on this branch:
The same blocking set was green on
v8before this change, so none of that is inherited.Impact
Grouped function and const imports keep their symbol identity and stop creating false class nodes. Class imports are unaffected: the guard still fires for them, just later in the same function.
Risk and rollback
Confined to one helper and its callers in the PHP resolution path. No schema change, no migration, no public API change. Reverting the second commit restores the previous behaviour exactly, and the tests from the first commit would then go red, which is why they are committed separately.
The case to watch is a declaration whose kind disagrees with a token on the clause itself. The clause token wins as the more specific statement, and
test_php_symbol_import_kind_applies_to_all_declaration_clausespins that.