Ctrl K
ComunidadGuías…

tests/test_cpp_method_declarations.py

Rama v8 · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""A C++ member-function DECLARATION is a method, not a data member.

Inside a class body a `virtual double area() = 0;` (pure virtual), a
`virtual void draw();` and a plain prototype `int run();` all parse as a
`field_declaration` whose declarator is a `function_declarator`. The field
branch emitted a node for every declarator as a data member (a bare `area`
label with a `defines`/field edge), so:

  * a pure-virtual interface class had NO method nodes, only phantom fields;
  * an out-of-line or overriding definition did not share the declaration's
    node, because one was `.area()` (method) and the other `area` (field);
  * a call to the method had nothing of the right shape to resolve to.

The fix emits a function_declarator with the same shape as a defined method
(`.name()` label, `method` edge, callable), while a genuine data member still
gets its `defines` field edge.
"""
from __future__ import annotations

import os
import tempfile
from pathlib import Path

from graphify.extract import extract


def _extract(tmp_path, files: dict[str, str]) -> dict:
    for name, body in files.items():
        p = tmp_path / name
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(body)
    old = os.getcwd()
    try:
        os.chdir(tmp_path)
        return extract([Path(n) for n in files], cache_root=Path(tempfile.mkdtemp()))
    finally:
        os.chdir(old)


def _rel(r: dict, relation: str, src: str, tgt: str) -> bool:
    labels = {n["id"]: str(n.get("label", "")) for n in r["nodes"]}
    return any(
        e.get("relation") == relation
        and labels.get(e.get("source")) == src
        and labels.get(e.get("target")) == tgt
        for e in r["edges"]
    )


def _labels(r: dict) -> list[str]:
    return [str(n.get("label", "")) for n in r["nodes"]]


def test_pure_virtual_method_is_a_method_node(tmp_path):
    r = _extract(tmp_path, {"s.cpp": (
        "class Shape {\n"
        "public:\n"
        "  virtual double area() = 0;\n"
        "};\n"
    )})
    assert _rel(r, "method", "Shape", ".area()")
    # Not misclassified as a data member.
    assert not _rel(r, "defines", "Shape", "area")
    assert "area" not in _labels(r)


def test_declared_virtual_and_prototype_are_methods(tmp_path):
    r = _extract(tmp_path, {"s.cpp": (
        "class Svc {\n"
        "public:\n"
        "  virtual void draw();\n"
        "  int run();\n"
        "};\n"
    )})
    assert _rel(r, "method", "Svc", ".draw()")
    assert _rel(r, "method", "Svc", ".run()")


def test_data_member_is_still_a_field(tmp_path):
    r = _extract(tmp_path, {"s.cpp": (
        "class Box {\n"
        "public:\n"
        "  int count;\n"
        "  double area() = 0;\n"
        "};\n"
    )})
    assert _rel(r, "defines", "Box", "count")
    assert _rel(r, "method", "Box", ".area()")


def test_override_definition_shares_the_declaration_node(tmp_path):
    # The abstract declaration and the concrete override are both `.area()`,
    # so a call through the base resolves instead of hitting a phantom field.
    r = _extract(tmp_path, {"s.cpp": (
        "class Shape {\n"
        "public:\n"
        "  virtual double area() = 0;\n"
        "  double describe() { return area(); }\n"
        "};\n"
    )})
    assert _rel(r, "method", "Shape", ".area()")
    assert _rel(r, "calls", ".describe()", ".area()")