mirror of
https://github.com/safishamsi/graphify.git
synced 2026-10-14 07:59:40 +00:00
My #4141 fix used a one-off calls/generic_dispatch relation; PR #4142 (@rajatnagda45) implements the same fix with the codebase-standard dispatches_to relation and earns author-preserving credit. Reverting the Fortran hunk here so #4142 can be cherry-picked; the Julia fixes from the same original commit are unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
3f8260fcc3
commit
a009ce7131
2 files changed
-111
No files matched your search
@@ -132,11 +132,6 @@ def extract_fortran(path: Path) -> dict:
|
||||
# internal procedures have their nodes.
|
||||
type_bound_procs: list[tuple[str, str, int]] = []
|
||||
|
||||
# (generic_nid, specific_procedure_name, line) for every `module procedure`
|
||||
# listed under a named generic interface. Resolved after the walk, like
|
||||
# type-bound procedures, so a call to the generic name links to each specific.
|
||||
generic_procs: list[tuple[str, str, int]] = []
|
||||
|
||||
def _fortran_name(stmt_node) -> str | None:
|
||||
"""Extract name from a *_statement node. Fortran is case-insensitive; lowercase."""
|
||||
for child in stmt_node.children:
|
||||
@@ -361,32 +356,6 @@ def extract_fortran(path: Path) -> dict:
|
||||
add_edge(scope_nid, imp_nid, "imports", line, context="use")
|
||||
return
|
||||
|
||||
if t == "interface":
|
||||
# A NAMED generic interface (`interface area; module procedure
|
||||
# area_circle, area_square; end interface area`) introduces a callable
|
||||
# generic name that overloads the listed specific procedures. Without a
|
||||
# node for the generic name, a call to it dangled. Unnamed and
|
||||
# operator/assignment interfaces carry no `name` child, so they are left
|
||||
# alone here; their explicit interface-body procedures are still walked.
|
||||
stmt = next((c for c in node.children if c.type == "interface_statement"), None)
|
||||
name = _fortran_name(stmt) if stmt else None
|
||||
if name:
|
||||
nid = _make_id(stem, name)
|
||||
line = node.start_point[0] + 1
|
||||
add_node(nid, f"{name}()", line)
|
||||
add_edge(scope_nid, nid, "defines", line)
|
||||
for pstmt in node.children:
|
||||
if pstmt.type != "procedure_statement":
|
||||
continue
|
||||
for mn in pstmt.children:
|
||||
if mn.type == "method_name":
|
||||
generic_procs.append(
|
||||
(nid, _read_text(mn, source).lower(),
|
||||
pstmt.start_point[0] + 1))
|
||||
for child in node.children:
|
||||
walk(child, scope_nid)
|
||||
return
|
||||
|
||||
for child in node.children:
|
||||
walk(child, scope_nid)
|
||||
|
||||
@@ -401,14 +370,6 @@ def extract_fortran(path: Path) -> dict:
|
||||
if tgt != type_nid:
|
||||
add_edge(type_nid, tgt, "method", line, context="type_bound_procedure")
|
||||
|
||||
# Link each named generic interface to the specific procedures it overloads,
|
||||
# now that the module's procedures have nodes. An out-of-module specific
|
||||
# resolves to a sourceless stub (same treatment as type-bound procedures).
|
||||
for generic_nid, proc_name, line in generic_procs:
|
||||
tgt = ensure_named_node(proc_name, line)
|
||||
if tgt != generic_nid:
|
||||
add_edge(generic_nid, tgt, "calls", line, context="generic_dispatch")
|
||||
|
||||
_stmt_headers = {
|
||||
"subroutine_statement", "function_statement",
|
||||
"program_statement", "module_statement",
|
||||
|
||||
@@ -2677,78 +2677,6 @@ def test_fortran_type_bound_procedures_link_to_the_type(tmp_path):
|
||||
assert ("circle", "scale") in methods
|
||||
|
||||
|
||||
def test_fortran_named_generic_interface_is_extracted(tmp_path):
|
||||
"""A named generic interface introduces a callable generic name that overloads
|
||||
the listed specific procedures. The `interface` node had no walk branch, so the
|
||||
generic name was dropped and a call to it dangled (#4141)."""
|
||||
src = tmp_path / "shapes.f90"
|
||||
src.write_text(
|
||||
"module shapes\n"
|
||||
" implicit none\n"
|
||||
" interface area\n"
|
||||
" module procedure area_circle, area_square\n"
|
||||
" end interface area\n"
|
||||
"contains\n"
|
||||
" real function area_circle(r)\n"
|
||||
" real :: r\n"
|
||||
" area_circle = 3.14 * r * r\n"
|
||||
" end function area_circle\n"
|
||||
" real function area_square(s)\n"
|
||||
" real :: s\n"
|
||||
" area_square = s * s\n"
|
||||
" end function area_square\n"
|
||||
" subroutine report(x)\n"
|
||||
" real :: x, a\n"
|
||||
" a = area(x)\n"
|
||||
" end subroutine report\n"
|
||||
"end module shapes\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
r = extract_fortran(src)
|
||||
assert "error" not in r
|
||||
assert "area" in [n["label"].rstrip("()") for n in r["nodes"]], "generic interface node dropped"
|
||||
# the call to the generic name now resolves instead of dangling
|
||||
assert ("report", "area") in _edge_labels(r, "calls")
|
||||
# the generic dispatches to each listed specific procedure
|
||||
dispatch = _edge_labels(r, "calls", "generic_dispatch")
|
||||
assert ("area", "area_circle") in dispatch
|
||||
assert ("area", "area_square") in dispatch
|
||||
node_ids = {n["id"] for n in r["nodes"]}
|
||||
for e in r["edges"]:
|
||||
assert e["source"] in node_ids and e["target"] in node_ids, e
|
||||
|
||||
|
||||
def test_fortran_operator_and_unnamed_interfaces_make_no_generic_node(tmp_path):
|
||||
"""An `operator(+)`/`assignment(=)` interface and an unnamed interface carry no
|
||||
generic name, so they must not mint a generic node; an unnamed interface body's
|
||||
explicit procedure declaration is still extracted (no regression to #4141)."""
|
||||
src = tmp_path / "ops.f90"
|
||||
src.write_text(
|
||||
"module m\n"
|
||||
" implicit none\n"
|
||||
" interface operator(+)\n"
|
||||
" module procedure add_vec\n"
|
||||
" end interface\n"
|
||||
" interface\n"
|
||||
" subroutine ext(x)\n"
|
||||
" real :: x\n"
|
||||
" end subroutine ext\n"
|
||||
" end interface\n"
|
||||
"contains\n"
|
||||
" function add_vec(a, b) result(c)\n"
|
||||
" real :: a, b, c\n"
|
||||
" c = a + b\n"
|
||||
" end function add_vec\n"
|
||||
"end module m\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
r = extract_fortran(src)
|
||||
assert "error" not in r
|
||||
labels = {n["label"].rstrip("()") for n in r["nodes"]}
|
||||
assert "ext" in labels, "unnamed-interface body procedure dropped"
|
||||
assert not ({"+", "operator", "assignment", ""} & labels), "spurious generic node for operator/unnamed interface"
|
||||
|
||||
|
||||
def test_fortran_type_bound_procedure_from_other_module_is_sourceless(tmp_path):
|
||||
"""A binding to a procedure implemented in another module resolves to a
|
||||
sourceless stub the corpus rewire can collapse, never a dangling edge."""
|
||||
|
||||
Reference in new issue
Block a user