Skip to content

fix(php): give grouped function and const imports their own identity - #1

Closed
DivyamTalwar wants to merge 2 commits into
v8from
harness/bf-phpalias/fix-a
Closed

fix(php): give grouped function and const imports their own identity#1
DivyamTalwar wants to merge 2 commits into
v8from
harness/bf-phpalias/fix-a

Conversation

@DivyamTalwar

Copy link
Copy Markdown
Owner

Problem

PHP grouped symbol imports are misclassified as class imports.

Given use function App\Helpers\{slug as s};, the alias s is recorded in the class-use map. The resolver then mints a sourceless App\Helpers\slug class 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_clause in graphify/extractors/resolution.py decided "is this a class import?" by scanning the clause's own children for a function or const token, 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_clause takes a declaration_kind, supplied by the caller that already knows whether the declaration said function or const.
  • The early return on encountering the token becomes clause_kind = c.type, so a token on the clause itself is still honoured and now records rather than aborts.
  • The not-a-class-import decision moves to after parsing, keyed on the resolved 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:

f6e8f07  test: add failing regression for grouped PHP symbol imports
52b39a6  fix: propagate PHP symbol import kind across use clauses

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:

PASS  skillgen --check
PASS  skillgen --audit-coverage
PASS  skillgen --schema-singleton
PASS  skillgen --monolith-roundtrip
PASS  skillgen --always-on-roundtrip
PASS  pytest tests/ -q  ->  5509 passed, 13 skipped, 6 warnings in 90.94s

The same blocking set was green on v8 before 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_clauses pins that.

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
@DivyamTalwar

Copy link
Copy Markdown
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.

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