tests/test_csharp_extension_resolution.py
Rama v8 · réplica de lectura del repositorio original.
"""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"]