mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-13 15:40:05 +00:00
fix(extract): drop Swift Self type annotation from references
A `-> Self` / `: Self` annotation names the enclosing type, not a corpus
symbol, but `_swift_collect_type_refs` appended the `Self` type_identifier
as a type reference and ran it through the sourceless-stub path. That
fabricated a node with the bare id `self` that every `Self`-returning
method in the corpus collapses onto — a god node, the same keyword-type
pitfall Rust's and Python's `Self` were fixed for.
`Self` is now skipped in both the `user_type` and bare `type_identifier`
arms. Genuine user types and the `Int`/`String` builtin type hubs are
unaffected.
(cherry picked from commit c4fb250b3b)
This commit is contained in:
1 parent
dafdbec33b
commit
6dbd10a311
2 files changed
+66
-2
No files matched your search
@@ -1083,7 +1083,7 @@ def _swift_collect_type_refs(node, source: bytes, generic: bool, out: list[tuple
|
||||
for c in node.children:
|
||||
if c.type == "type_identifier":
|
||||
text = _read_text(c, source)
|
||||
if text:
|
||||
if text and text != "Self":
|
||||
out.append((text, "generic_arg" if generic else "type"))
|
||||
break
|
||||
for c in node.children:
|
||||
@@ -1094,7 +1094,12 @@ def _swift_collect_type_refs(node, source: bytes, generic: bool, out: list[tuple
|
||||
return
|
||||
if t == "type_identifier":
|
||||
text = _read_text(node, source)
|
||||
if text:
|
||||
# `Self` names the enclosing type, not a corpus symbol. Emitting a ref
|
||||
# mints a sourceless stub with the bare id `self` that every `-> Self`
|
||||
# method collapses onto (a god node), the same keyword-type pitfall as
|
||||
# Rust's `Self`. A genuine type (including `Int`/`String` builtin hubs)
|
||||
# is unaffected.
|
||||
if text and text != "Self":
|
||||
out.append((text, "generic_arg" if generic else "type"))
|
||||
return
|
||||
if t in ("optional_type", "implicitly_unwrapped_optional_type", "array_type",
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Swift `Self` must not mint a sourceless god node.
|
||||
|
||||
A `-> Self` (or `: Self`) annotation names the enclosing type, not a corpus
|
||||
symbol. The Swift type-ref pass treated the `Self` type_identifier as a type
|
||||
reference and ran it through the sourceless-stub path, fabricating a node with
|
||||
the bare id `self` that every `Self`-returning method in the corpus collapses
|
||||
onto — the same keyword-type god node Rust's `Self` was fixed for. A genuine
|
||||
type (including the `Int`/`String` builtin hubs) is unaffected.
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from graphify.extract import extract
|
||||
|
||||
|
||||
def test_self_return_type_mints_no_stub(tmp_path):
|
||||
(tmp_path / "w.swift").write_text(
|
||||
"class Widget {\n"
|
||||
" func copy() -> Self { return self }\n"
|
||||
"}\n"
|
||||
)
|
||||
r = extract([tmp_path / "w.swift"], cache_root=tmp_path, parallel=False)
|
||||
assert not any(n["id"] == "self" for n in r["nodes"])
|
||||
assert not any(e.get("target") == "self" for e in r["edges"])
|
||||
|
||||
|
||||
def test_self_parameter_type_mints_no_stub(tmp_path):
|
||||
(tmp_path / "n.swift").write_text(
|
||||
"class Node {\n"
|
||||
" func link(_ other: Self) -> Self { return self }\n"
|
||||
"}\n"
|
||||
)
|
||||
r = extract([tmp_path / "n.swift"], cache_root=tmp_path, parallel=False)
|
||||
assert not any(n["id"] == "self" for n in r["nodes"])
|
||||
|
||||
|
||||
def test_self_is_not_a_cross_file_god_node(tmp_path):
|
||||
(tmp_path / "a.swift").write_text(
|
||||
"class A {\n func dup() -> Self { return self }\n}\n")
|
||||
(tmp_path / "b.swift").write_text(
|
||||
"class B {\n func dup() -> Self { return self }\n}\n")
|
||||
r = extract(sorted(tmp_path.glob("*.swift")), cache_root=tmp_path, parallel=False)
|
||||
assert not any(n["id"] == "self" for n in r["nodes"])
|
||||
|
||||
|
||||
def test_real_return_type_still_referenced(tmp_path):
|
||||
# The guard must not suppress a genuine user return type.
|
||||
(tmp_path / "m.swift").write_text(
|
||||
"class Money {}\n"
|
||||
"class Account {\n"
|
||||
" func balance() -> Money { return Money() }\n"
|
||||
"}\n"
|
||||
)
|
||||
r = extract([tmp_path / "m.swift"], cache_root=tmp_path, parallel=False)
|
||||
money_ids = [n["id"] for n in r["nodes"] if n.get("label") == "Money"]
|
||||
assert money_ids
|
||||
assert any(
|
||||
e.get("relation") == "references" and e.get("target") in money_ids
|
||||
for e in r["edges"]
|
||||
)
|
||||
Reference in new issue
Block a user