Repository navigation
fix(php): consolidated PHP support — member-call resolution, use-import handling, node identity, cross-language isolation, query seeding (#1682, #2615) #2617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v8
Are you sure you want to change the base?
Changes from all commits
43cd7a2
ae95261
a707b42
6d33de1
4f17dd8
f567a36
2e68ec6
5de8f76
0cb78af
8d051c6
49b665e
c2c3e89
85bb8df
db7c8f8
102ae79
cf49aff
f310fb5
fe25b51
f8c58e6
6e91dbd
fbda3ec
d97e37d
2a9f9fe
e147764
ee3a411
d40d006
e188ff6
45f7de1
7006988
68a3274
64da0eb
e713e02
3e148ef
cec9cdd
49c25ff
10cdee2
d724156
bf0a92c
520e58f
5517334
725d082
ff970bb
87a7398
cebf231
4e8ff46
635db73
ebe7b67
457b31e
2033c46
be80845
acf29e1
a9adfc6
075038e
313a168
9c4db41
258d8e3
293d57e
1606462
18c17d8
9069df5
630072e
26f7b03
ec4d97e
93657ef
5e9d95c
4492f9f
a79ebe0
b69dab8
42d8903
56c108a
a2c445b
d42ef6a
d8789ae
bfad35e
bd4c524
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -135,6 +135,22 @@ def _prefer_file_node( | |
| return None | ||
|
|
||
|
|
||
| def _is_sourced(graph: nx.Graph, node_id: str) -> bool: | ||
| return bool(str(graph.nodes[node_id].get("source_file") or "")) | ||
|
|
||
|
|
||
| def _prefer_sourced_node(graph: nx.Graph, node_ids: list[str]) -> str | None: | ||
| """Return the one node with a source file among label rivals, else None. | ||
|
|
||
| Mirrors serve's `_find_node_tiers` exact-tier rule (#49): a sourceless node is | ||
| an unresolved-reference placeholder, so a real declaration sharing its label is | ||
| the answer rather than a tie. Without this, `explain` resolved the sourced node | ||
| while `affected` refused with "No unique node match" — #46's symptom. | ||
| """ | ||
| sourced = [node_id for node_id in node_ids if _is_sourced(graph, node_id)] | ||
| return sourced[0] if len(sourced) == 1 else None | ||
|
|
||
|
|
||
| def resolve_seed(graph: nx.Graph, query: str, root: Path | None = None) -> str | None: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
fans out to 6 callees (efferent coupling); 19 callers depend on it (afferent coupling). Grounded coupling-delta finding (deterministic), not an LLM guess. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
fans out to 6 callees (efferent coupling); 19 callers depend on it (afferent coupling). Grounded coupling-delta finding (deterministic), not an LLM guess. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
fans out to 6 callees (efferent coupling); 19 callers depend on it (afferent coupling). Grounded coupling-delta finding (deterministic), not an LLM guess. |
||
| # A trailing path separator must not change a source-file match — serve's | ||
| # _find_node tokenizes the path (which drops it), so strip it here for parity | ||
|
|
@@ -148,8 +164,17 @@ def resolve_seed(graph: nx.Graph, query: str, root: Path | None = None) -> str | | |
| for node_id, data in graph.nodes(data=True) | ||
| if _normalize_label(str(data.get("label", ""))) == query_lower | ||
| ] | ||
| if len(exact_label_matches) == 1: | ||
| if len(exact_label_matches) == 1 and _is_sourced(graph, exact_label_matches[0]): | ||
| return exact_label_matches[0] | ||
| if exact_label_matches: | ||
| sourced = _prefer_sourced_node(graph, exact_label_matches) | ||
| if sourced is not None: | ||
| return sourced | ||
| # A lone match that is a sourceless stub is NOT yet an answer: the real | ||
| # declaration may carry a decorated label ("handle()") that only the | ||
| # bare-name pass below reaches, and serve's exact tier — which matches | ||
| # both forms — would prefer it (#49). That pass is a superset of this | ||
| # one, so a stub with no sourced rival still resolves there. | ||
| # Callable labels are decorated ("name()"), so a bare "name" query falls | ||
| # through exact matching and then ties with any "name*" sibling in the | ||
| # contains pass. Match on the undecorated name before giving up. | ||
|
|
@@ -161,6 +186,10 @@ def resolve_seed(graph: nx.Graph, query: str, root: Path | None = None) -> str | | |
| ] | ||
| if len(bare_name_matches) == 1: | ||
| return bare_name_matches[0] | ||
| if bare_name_matches: | ||
| sourced = _prefer_sourced_node(graph, bare_name_matches) | ||
| if sourced is not None: | ||
| return sourced | ||
| # Compare paths in repo-relative form. Only this branch is path-shaped; the | ||
| # label branches above keep the query verbatim. | ||
| query_path = _normalize_label(_as_repo_relative(query, root)) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve_seed()fans out to 6 callees (efferent coupling); 19 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.