Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions graphify/extractors/resolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,19 +1250,34 @@ def _ts_walk_class_members(class_node, source: bytes, path: Path, class_nid: str
line = class_node.start_point[0] + 1
for child in class_node.children:
if child.type == "class_heritage":
saw_clause = False
for clause in child.children:
if clause.type == "extends_clause":
saw_clause = True
for name in _ts_heritage_clause_entries(clause, source):
facts.uses.append(
_SymbolUseFact(path, class_nid, name, "inherits", "type",
clause.start_point[0] + 1)
)
elif clause.type == "implements_clause":
saw_clause = True
for name in _ts_heritage_clause_entries(clause, source):
facts.uses.append(
_SymbolUseFact(path, class_nid, name, "implements", "type",
clause.start_point[0] + 1)
)
if not saw_clause:
# The JavaScript grammar carries the base directly under
# class_heritage (`extends Base` -> [extends, identifier]) with no
# extends_clause/implements_clause wrapper like the TypeScript
# grammar. Treat the heritage node itself as the extends clause so
# `class Derived extends Base {}` in a .js file still emits an
# inherits edge. Mirrors the extends_type_clause branch below.
for name in _ts_heritage_clause_entries(child, source):
facts.uses.append(
_SymbolUseFact(path, class_nid, name, "inherits", "type",
child.start_point[0] + 1)
)
elif child.type == "extends_type_clause":
# Interface heritage (`interface A extends B, C`) is an
# extends_type_clause node, NOT a class_heritage. Its base entries
Expand Down
15 changes: 15 additions & 0 deletions tests/test_ts_inheritance.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@ def test_class_extends_same_file(tmp_path):
assert _has_inherits(result, "src/a.ts", "Dog", "src/a.ts", "Animal")


def test_js_class_extends_same_file(tmp_path):
"""`class Dog extends Animal {}` in a plain .js file must emit an inherits edge.

The JavaScript grammar stores the base identifier directly under the
`class_heritage` node (no `extends_clause` wrapper as in the TypeScript
grammar), so the heritage walk dropped the edge for .js classes while the
equivalent .ts file (test_class_extends_same_file) already worked.
"""
f = _write(tmp_path / "src" / "a.js",
"class Animal {}\n"
"class Dog extends Animal {}\n")
result = extract([f], cache_root=tmp_path)
assert _has_inherits(result, "src/a.js", "Dog", "src/a.js", "Animal")


def test_interface_extends_generic_base_same_file(tmp_path):
f = _write(tmp_path / "src" / "a.ts",
"interface Base<T> { x: T; }\n"
Expand Down
Loading