Ctrl K
ComunidadGuías…

tests/test_csharp_extension_resolution.py

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

Archivo original ↗Preguntar a KiBot
"""Production behaviour for C# extension-call binding (#4828).

Ground truth lives in ``tests/csharp_extension_harness.py``: ``expect`` edges must
exist in the real pipeline output, ``forbid`` edges must not. ``STAGE_FIXTURES``
names the fixtures the pass is required to satisfy *at this stage* of the fix;
the forbidden-edge tests run against every fixture from the first stage, so a
later stage can only add recall, never a false edge.
"""
from __future__ import annotations

import pytest

from tests.csharp_extension_harness import (
    FIXTURES,
    expected_pairs,
    graph_bindings,
)

FIXTURE_IDS = list(FIXTURES)

# Stage 1: in-corpus receivers, scope, assignability, poison, arity.
# Stage 2: out-of-corpus receivers guarded by the BCL/regular-name rules.
# Stage 3: untyped/chained receivers.
STAGE_FIXTURES = {
    "domain_cross_file",
    "same_file",
    "arity",
    "type_param_vs_class",
    "base_and_interface",
    "inner_scope_wins",
    "instance_wins",
    # stage 2: out-of-corpus receivers (primitives, third-party types, `this T`)
    "primitive_and_thirdparty",
    "generic_wildcard",
    # stage 3: untyped/chained receivers
    "issue_repro",
}


@pytest.mark.parametrize("name", FIXTURE_IDS)
def test_production_never_emits_a_forbidden_edge(name):
    result = graph_bindings(name)
    assert result["violations"] == set(), sorted(result["violations"])


@pytest.mark.parametrize("name", FIXTURE_IDS)
def test_production_invents_no_call_site(name):
    assert graph_bindings(name)["phantoms"] == []


@pytest.mark.parametrize("name", sorted(STAGE_FIXTURES))
def test_production_binds_the_decidable_extension_calls(name):
    result = graph_bindings(name)
    missing = expected_pairs(name) - result["bindings"]
    assert not missing, f"{name}: missing {sorted(missing)}"


def test_extension_edge_is_inferred_at_the_call_site():
    from tests.csharp_extension_harness import corpus

    data = corpus("domain_cross_file")
    by_id = {n["id"]: n for n in data["nodes"]}
    paint_calls = [
        e for e in data["edges"]
        if e.get("relation") == "calls"
        and by_id.get(e.get("target"), {}).get("label") == ".Paint()"
    ]
    assert len(paint_calls) == 2
    assert {e["source_location"] for e in paint_calls} == {"L5", "L6"}
    for edge in paint_calls:
        assert edge["confidence"] == "INFERRED"
        assert edge["confidence_score"] == 0.85
        assert edge["context"] == "call"


def test_instance_member_wins_over_a_same_named_extension():
    """`Cache.GetValue()` is nominal; the extension of the same name must not win."""
    result = graph_bindings("instance_wins")
    assert ("// E1", ("Cache.cs", "// D1")) in result["bindings"]
    assert ("// E1", ("Cache.cs", "// D2")) not in result["bindings"]


def test_static_form_is_never_rebound_to_the_extension():
    """`Conv.ToInt32(x)` names its type in source and is bound by the nominal pass."""
    result = graph_bindings("static_class_receiver")
    assert ("// B1", ("E.cs", "// D1")) in result["bindings"]
    assert ("// B1", ("E.cs", "// D2")) not in result["bindings"]


def test_ambiguity_binds_nothing():
    result = graph_bindings("ambiguous_same_scope")
    assert not any(marker == "// E1" for marker, _target in result["bindings"])


def test_out_of_scope_extension_binds_nothing():
    result = graph_bindings("out_of_scope")
    assert not any(marker == "// E1" for marker, _target in result["bindings"])


def test_unresolved_base_poisons_the_lookup():
    result = graph_bindings("unresolved_base_poison")
    assert not any(marker == "// E1" for marker, _target in result["bindings"])


def test_out_of_corpus_receiver_binds_through_the_guarded_rung():
    """`string.Mask()`, `int.Twice()` and a third-party receiver all bind."""
    result = graph_bindings("primitive_and_thirdparty")
    for marker, target in (
        ("// E1", ("Ext.cs", "// D1")),
        ("// E2", ("Ext.cs", "// D2")),
        ("// E3", ("Ext.cs", "// D3")),
    ):
        assert (marker, target) in result["bindings"], marker


def test_out_of_corpus_extension_edge_is_scored_lower():
    from tests.csharp_extension_harness import corpus

    data = corpus("primitive_and_thirdparty")
    by_id = {n["id"]: n for n in data["nodes"]}
    scores = {
        by_id[e["target"]]["label"]: e["confidence_score"]
        for e in data["edges"]
        if e.get("relation") == "calls"
        and by_id.get(e["target"], {}).get("metadata", {}).get("extension")
    }
    assert scores[".Mask()"] == 0.75
    assert scores[".Twice()"] == 0.75


def test_bcl_member_name_on_an_undeclared_type_is_not_bound():
    """`PropertyInfo.GetValue(obj)` is an instance call; the corpus cannot prove
    otherwise, so the deny-list refuses the same-named extension."""
    result = graph_bindings("bcl_name_cost")
    assert not any(marker == "// E1" for marker, _target in result["bindings"])


def test_open_generic_extension_applies_only_as_a_wildcard():
    result = graph_bindings("generic_wildcard")
    assert ("// E1", ("Gen.cs", "// D1")) in result["bindings"]
    # The concrete class T keeps winning over the generic `this T` declaration.
    concrete = graph_bindings("type_param_vs_class")
    assert ("// E1", ("T.cs", "// D1")) in concrete["bindings"]
    assert ("// E1", ("T.cs", "// D2")) not in concrete["bindings"]


def test_chained_receiver_binds_when_the_name_is_unique():
    """The issue's `v.ToString().Mask()` line: no receiver type to check, so the
    guarded untyped rung is the only possible proof."""
    result = graph_bindings("issue_repro")
    assert ("// E1", ("Ext.cs", "// D1")) in result["bindings"]
    assert ("// E2", ("Ext.cs", "// D1")) in result["bindings"]


def test_untyped_rung_refuses_a_name_a_regular_method_uses():
    result = graph_bindings("untyped_regular_name")
    assert not any(marker == "// F1" for marker, _target in result["bindings"])


def test_untyped_rung_refuses_a_capitalized_type_receiver():
    """`Conv.ToInt32(x)` names a type; the extension must not be considered."""
    result = graph_bindings("static_class_receiver")
    assert ("// B1", ("E.cs", "// D2")) not in result["bindings"]