mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
test(resolver): attest class mentions and cover C# operator scopes
A matrix row on a class target brings that class's other roles into the harness audit. `make(): Widget` and `anon(): User` name the class they also construct, so the signature's USAGE, and USES_TYPE in Scala and Swift, become required rows at that exact token. mkc_c2_csharp_operator_overloads pins the operator scope for overloads stored as variants, for a spaced `operator -` and for the word form `operator true`. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
2840080f69
commit
852cb8859f
2 files changed
+126
-19
No files matched your search
@@ -183,12 +183,6 @@ static const MKC_Golden mkc_goldens[] = {
|
||||
" public float Dot(Vec2 o) { return X*o.X + Y*o.Y; }", "Method", 8, 0, false},
|
||||
{"mkc_c2_csharp_operator_plus", "operator+", "new Vec2(a.X + b.X, a.Y + b.Y)", "Vec2",
|
||||
" public Vec2(float x, float y) { X = x; Y = y; }", "Method", 4, 0, true},
|
||||
{"mkc_c2_csharp_operator_overloads", "operator+", "new Vec2(a.X + b.X)", "Vec2",
|
||||
" public Vec2(float x) { X = x; }", "Method", 4, 0, true},
|
||||
{"mkc_c2_csharp_operator_overloads", "operator+", "new Vec2(a.X + b)", "Vec2",
|
||||
" public Vec2(float x) { X = x; }", "Method", 4, 0, true},
|
||||
{"mkc_c2_csharp_operator_overloads", "operator-", "new Vec2(a.X - b.X)", "Vec2",
|
||||
" public Vec2(float x) { X = x; }", "Method", 4, 0, true},
|
||||
{"mkc_c2_scala_symbolic_method", "run", "a + b", "+",
|
||||
" def +(other: Vec2): Vec2 = Vec2(x + other.x, y + other.y)", "Method", 2, 0, false},
|
||||
{"mkc_c2_scala_symbolic_method", "run", "(a + b).dot(b)", "dot",
|
||||
@@ -271,6 +265,23 @@ static const MKC_Golden mkc_goldens[] = {
|
||||
{"mkc_c8_kotlin_constructor_samefile", "run", "make(name).label()", "label",
|
||||
" fun label(): String = name", "Method", 2, 0, true},
|
||||
};
|
||||
/* The harness audits a known target under every role. A class named in a signature is
|
||||
* referenced there, in these languages as USAGE and as USES_TYPE, apart from the call. */
|
||||
typedef struct {
|
||||
const char *test, *caller, *needle, *relation, *target, *declaration;
|
||||
int line;
|
||||
unsigned occurrence;
|
||||
} MKC_TypeMention;
|
||||
static const MKC_TypeMention mkc_type_mentions[] = {
|
||||
{"mkc_c1_scala_constructor_new", "make", "Widget", "USAGE", "Widget",
|
||||
"class Widget(val name: String) {", 1, 1},
|
||||
{"mkc_c1_scala_constructor_new", "make", "Widget", "USES_TYPE", "Widget",
|
||||
"class Widget(val name: String) {", 1, 1},
|
||||
{"mkc_c1_swift_initializer", "make", "Widget", "USAGE", "Widget", "class Widget {", 1, 1},
|
||||
{"mkc_c1_swift_initializer", "make", "Widget", "USES_TYPE", "Widget", "class Widget {", 1, 1},
|
||||
{"mkc_c8_kotlin_constructor_samefile", "make", "Widget", "USAGE", "Widget",
|
||||
"class Widget(val name: String) {", 1, 1},
|
||||
};
|
||||
typedef struct {
|
||||
const char *test, *caller, *needle;
|
||||
unsigned occurrence;
|
||||
@@ -346,6 +357,28 @@ static bool mkc_quality(const MKC_File *original, int count, const char *test) {
|
||||
required += g->required;
|
||||
n++;
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(mkc_type_mentions) / sizeof(mkc_type_mentions[0]); i++) {
|
||||
const MKC_TypeMention *g = mkc_type_mentions + i;
|
||||
if (strcmp(test, g->test))
|
||||
continue;
|
||||
if (n == sizeof(expected) / sizeof(expected[0]))
|
||||
return false;
|
||||
snprintf(ids[n], sizeof(ids[n]), "%s-%zu", test, n);
|
||||
expected[n] = (RQ_Expected){.case_id = ids[n],
|
||||
.site = {.file = original[0].name,
|
||||
.caller = g->caller,
|
||||
.needle = g->needle,
|
||||
.occurrence_index = g->occurrence},
|
||||
.relation = g->relation,
|
||||
.target = {.file = original[0].name,
|
||||
.suffix = g->target,
|
||||
.label = "Class",
|
||||
.declaration_line = g->line,
|
||||
.declaration_text = g->declaration},
|
||||
.mode = RQ_REQUIRED};
|
||||
required++;
|
||||
n++;
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(mkc_forbidden) / sizeof(mkc_forbidden[0]); i++) {
|
||||
const MKC_Forbidden *g = mkc_forbidden + i;
|
||||
if (strcmp(test, g->test))
|
||||
@@ -661,20 +694,75 @@ TEST(mkc_c2_csharp_operator_plus) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A C# operator's body has its own node, also when the operator is overloaded. A call in it
|
||||
* belongs to that operator, never to the file. */
|
||||
/* A C# operator's body has its own node, also when the operator is overloaded or spelled
|
||||
* with a space or a word. A call in it belongs to that operator, never to the file. */
|
||||
TEST(mkc_c2_csharp_operator_overloads) {
|
||||
static const MKC_File f[] = {
|
||||
{"Vec.cs", "namespace App {\n"
|
||||
" struct Vec2 {\n"
|
||||
" public float X;\n"
|
||||
" public Vec2(float x) { X = x; }\n"
|
||||
" public static Vec2 operator+(Vec2 a, Vec2 b) { return new Vec2(a.X + b.X); }\n"
|
||||
" public static Vec2 operator+(Vec2 a, float b) { return new Vec2(a.X + b); }\n"
|
||||
" public static Vec2 operator -(Vec2 a, Vec2 b) { return new Vec2(a.X - b.X); }\n"
|
||||
" }\n"
|
||||
"}\n"}};
|
||||
ASSERT_TRUE(mkc_quality(f, 1, __func__));
|
||||
static const char source[] =
|
||||
"namespace App {\n"
|
||||
" struct Vec2 {\n"
|
||||
" public float X;\n"
|
||||
" public Vec2(float x) { X = x; }\n"
|
||||
" public static Vec2 operator+(Vec2 a, Vec2 b) { return new Vec2(a.X + b.X); }\n"
|
||||
" public static Vec2 operator+(Vec2 a, float b) { return new Vec2(a.X + b); }\n"
|
||||
" public static Vec2 operator -(Vec2 a, Vec2 b) { return new Vec2(a.X - b.X); }\n"
|
||||
" public static bool operator true(Vec2 a) { return Check(a); }\n"
|
||||
" static bool Check(Vec2 a) { return true; }\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
const MKC_File files[] = {{"Vec.cs", source}};
|
||||
const RQ_Source sources[] = {{.path = "Vec.cs", .text = source}};
|
||||
const RQ_Target ctor = {.file = "Vec.cs",
|
||||
.suffix = "Vec2.Vec2",
|
||||
.label = "Method",
|
||||
.declaration_line = 4,
|
||||
.declaration_text = " public Vec2(float x) { X = x; }"};
|
||||
const RQ_Target check = {.file = "Vec.cs",
|
||||
.suffix = "Vec2.Check",
|
||||
.label = "Method",
|
||||
.declaration_line = 9,
|
||||
.declaration_text =
|
||||
" static bool Check(Vec2 a) { return true; }"};
|
||||
const RQ_Expected expected[] = {
|
||||
{.case_id = "first-plus",
|
||||
.site = {.file = "Vec.cs",
|
||||
.caller = "Vec2.operator+",
|
||||
.needle = "new Vec2(a.X + b.X)",
|
||||
.caller_variant_start_line = 5,
|
||||
.caller_variant_end_line = 5,
|
||||
.caller_variant_declaration = " public static Vec2 operator+(Vec2 a, Vec2 "
|
||||
"b) { return new Vec2(a.X + b.X); }"},
|
||||
.relation = "CALLS",
|
||||
.target = ctor,
|
||||
.mode = RQ_REQUIRED},
|
||||
{.case_id = "second-plus",
|
||||
.site = {.file = "Vec.cs",
|
||||
.caller = "Vec2.operator+",
|
||||
.needle = "new Vec2(a.X + b)",
|
||||
.caller_variant_start_line = 6,
|
||||
.caller_variant_end_line = 6,
|
||||
.caller_variant_declaration = " public static Vec2 operator+(Vec2 a, float "
|
||||
"b) { return new Vec2(a.X + b); }"},
|
||||
.relation = "CALLS",
|
||||
.target = ctor,
|
||||
.mode = RQ_REQUIRED},
|
||||
{.case_id = "minus",
|
||||
.site = {.file = "Vec.cs", .caller = "Vec2.operator-", .needle = "new Vec2(a.X - b.X)"},
|
||||
.relation = "CALLS",
|
||||
.target = ctor,
|
||||
.mode = RQ_REQUIRED},
|
||||
{.case_id = "word",
|
||||
.site = {.file = "Vec.cs", .caller = "Vec2.operator true", .needle = "Check(a)"},
|
||||
.relation = "CALLS",
|
||||
.target = check,
|
||||
.mode = RQ_REQUIRED}};
|
||||
MKC_Proj project;
|
||||
cbm_store_t *store = mkc_index(&project, files, 1);
|
||||
RQ_Report report = {0};
|
||||
bool ok = store && rq_check_project_declarations(store, project.project, __func__, sources, 1,
|
||||
expected, 4, NULL, 0, &report, stderr);
|
||||
mkc_cleanup(&project, store);
|
||||
ASSERT_TRUE(ok);
|
||||
ASSERT_EQ(report.found, 4);
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,25 @@ static bool mn_quality(const char *test, const char *filename, const char *conte
|
||||
}
|
||||
if (n == 1)
|
||||
return false;
|
||||
/* The harness audits a known target under every role: `anon(): User` names the class
|
||||
* it also constructs, and that signature is a USAGE of it. */
|
||||
if (!strcmp(test, "mn_record_kotlin_data_class")) {
|
||||
if (n == sizeof(expected) / sizeof(expected[0]))
|
||||
return false;
|
||||
snprintf(ids[n], sizeof(ids[n]), "%s-%zu", test, n);
|
||||
expected[n] = (RQ_Expected){
|
||||
.case_id = ids[n],
|
||||
.site = {.file = filename, .caller = "anon", .needle = "User", .occurrence_index = 1},
|
||||
.relation = "USAGE",
|
||||
.target = {.file = filename,
|
||||
.suffix = "User",
|
||||
.label = "Class",
|
||||
.declaration_line = 1,
|
||||
.declaration_text = "data class User(val name: String, val age: Int) {"},
|
||||
.mode = RQ_REQUIRED};
|
||||
required++;
|
||||
n++;
|
||||
}
|
||||
MN_LangProj lp;
|
||||
cbm_store_t *store = mn_index_files(&lp, files, 2);
|
||||
RQ_Report report = {0};
|
||||
|
||||
Reference in new issue
Block a user