Skip to content

Commit 011dda9

Browse files
committed
fix(powershell): link enum members with case_of instead of contains
A PowerShell enum member is a discriminant case, not a declared field, so it should hang off its enum with a case_of edge like every other language that has enums (Java #1719, C#, Swift, Rust, VB.NET). PowerShell was routing enum members through the same contains path used for class members. The relation is not cosmetic: case_of targets are excluded from constructor binding, so an enum member named like a type could previously be mistaken for one. Update the existing enum test to expect case_of and add a focused regression test asserting enum members get case_of (and not contains) while the file still contains the enum type via contains.
1 parent 35adf43 commit 011dda9

2 files changed

Lines changed: 36 additions & 5 deletions

File tree

‎graphify/extractors/powershell.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,13 @@ def walk(node, parent_class_nid: str | None = None) -> None:
150150
m_line = member.start_point[0] + 1
151151
member_nid = _make_id(enum_nid, member_name)
152152
add_node(member_nid, member_name, m_line)
153-
add_edge(enum_nid, member_nid, "contains", m_line)
153+
# An enum member is a discriminant case, not a declared
154+
# field, so it gets `case_of` like every other language with
155+
# enums (Java #1719, C#, Swift, Rust, VB.NET). The relation
156+
# also matters to resolution: `case_of` targets are excluded
157+
# from constructor binding, so an enum member named like a
158+
# type can no longer be mistaken for one.
159+
add_edge(enum_nid, member_nid, "case_of", m_line)
154160
return
155161

156162
if t == "class_statement":

‎tests/test_languages.py‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,19 +2699,44 @@ def test_powershell_enum_is_extracted_and_reference_resolves(tmp_path):
26992699
color = next((n for n in r["nodes"] if n["label"] == "Color"), None)
27002700
assert color is not None, "enum definition dropped"
27012701
assert color["source_file"] != "", "enum must be a real sourced definition, not a phantom stub"
2702-
# members are captured
2703-
contained = {
2702+
# members are captured, hanging off the enum via case_of (not contains)
2703+
cases = {
27042704
n["label"] for n in r["nodes"]
27052705
for e in r["edges"]
2706-
if e["relation"] == "contains" and e["source"] == color["id"] and e["target"] == n["id"]
2706+
if e["relation"] == "case_of" and e["source"] == color["id"] and e["target"] == n["id"]
27072707
}
2708-
assert {"Red", "Green", "Blue"} <= contained, f"enum members missing: {contained}"
2708+
assert {"Red", "Green", "Blue"} <= cases, f"enum members missing: {cases}"
27092709
# the field type reference resolves to the real enum node
27102710
ref_targets = {e["target"] for e in r["edges"]
27112711
if e["relation"] == "references" and e.get("context") == "field"}
27122712
assert color["id"] in ref_targets, "[Color] field reference did not resolve to the enum"
27132713

27142714

2715+
def test_powershell_enum_members_emit_case_of_not_contains(tmp_path):
2716+
"""A PowerShell enum member is a discriminant case, so it must get a
2717+
`case_of` edge like every other language with enums (Java #1719, C#, Swift,
2718+
Rust, VB.NET), not the `contains` edge used for real class fields.
2719+
PowerShell was routing enum members through the same `contains` path as a
2720+
class property. The relation also matters to resolution: `case_of` targets
2721+
are excluded from constructor binding, so an enum member named like a type
2722+
can no longer be mistaken for one.
2723+
"""
2724+
f = tmp_path / "status.ps1"
2725+
f.write_text("enum Status {\n Active\n Paused\n Closed\n}\n")
2726+
r = extract_powershell(f)
2727+
case_of = _edge_labels(r, "case_of")
2728+
contains = _edge_labels(r, "contains")
2729+
# Each enum member hangs off its enum via case_of, not contains.
2730+
assert ("Status", "Active") in case_of
2731+
assert ("Status", "Paused") in case_of
2732+
assert ("Status", "Closed") in case_of
2733+
assert ("Status", "Active") not in contains
2734+
# Containment itself (the file owning the enum type) still uses `contains`;
2735+
# only the member-to-enum relation changed.
2736+
assert ("status.ps1", "Status") in contains
2737+
assert ("status.ps1", "Status") not in case_of
2738+
2739+
27152740
def test_powershell_property_field_type_context():
27162741
r = extract_powershell(FIXTURES / "sample.ps1")
27172742
assert ("DataProcessor", "string") in _edge_labels(r, "references", "field")

0 commit comments

Comments
 (0)