mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
test(doc_mentions_go): receiver-qualified Go method nodes (#1913)
main's Go doc-link tests were written against flat Go method QNs, where `(A) Close` and `(B) Close` shared one node `alpha.Close`. That is the fused-node defect #1913 removes. The expectations move to the new scheme, and each changed assertion pins an edge onto, or a count of, the receiver-qualified node (production: the previous commit and #1913): - sources: RefM's source is p.pkg.S.M, not p.pkg.M. - forms: [Widget.Run] lands on alpha.Widget.Run, [beta.Thing.Do] on beta.Thing.Do. - plain_text, struct_fields: Run's doc is the source alpha.Widget.Run. - lookup_order: two alpha.Run sources and beta.Promoted named no node any more, so they held vacuously. They are now alpha.Widget.Run and beta.Embedded.Promoted. The alias links land on beta.Thing.Do and alpha.Deep10.End. - node_owner, the positive contract that replaces the fused case: - two Method nodes, alpha.A.Close and alpha.B.Close, and none named alpha.Close; - [A.Close] and [B.Close] are edges, not graph_gap rows; - each method's own [Target] is an edge from its own doc line (9 and 12), with no unresolved row; - the type Open keeps alpha.Open: [Open] is the type (no graph_gap), [B.Open] is the method, and UsesOpen has 2 targets. - scope_blob: - method records carry w `-`; - swapping two methods of one name no longer changes the blob; - a func/type pair swap still does (new case); - the k1-k3 records follow (a method neither keeps nor contests a name); - a new k4 keeps the "two on one line contest it" rule covered with a var and a const. - incremental_equals_full: - [b.T.M], [b.W.Name] and [b.V.Name] all resolve, and U.M's [Target] is an edge; - removing T.M breaks only [b.T.M]; - swapping V.Name and W.Name moves nothing and is now an ordinary closure repair instead of a forced full rebuild. - scope_delta_rules: - a method/function swap is LOCAL; - removing the method of such a pair is LOCAL with the name "Open" (a member left); - new func/type cases keep the GLOBAL keeper-change rules covered for swap and removal. - workers_agree: [p01.T3.Close] now resolves (exact edge, no row). The graph_gap reason stays covered by the named Shared assertion: one function declared by every file of a package, which is still a gap. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
033be76c70
commit
4c62b61845
1 file changed
+122
-68
+122
-68
@@ -228,10 +228,10 @@ TEST(doc_mentions_go_sources) {
|
||||
const char *raw;
|
||||
const char *source;
|
||||
} want[] = {
|
||||
{"RefF", "p.pkg.F"}, {"RefS", "p.pkg.S"}, {"RefField", "p.pkg.S.Field"},
|
||||
{"RefM", "p.pkg.M"}, /* Go method nodes have a flat name */
|
||||
{"RefI", "p.pkg.I"}, {"RefDo", "p.pkg.I.Do"}, {"RefC", "p.pkg.C"},
|
||||
{"RefK", "p.pkg.K"}, {"RefV", "p.pkg.V"}, {"RefA", "p.pkg.A"},
|
||||
{"RefF", "p.pkg.F"}, {"RefS", "p.pkg.S"}, {"RefField", "p.pkg.S.Field"},
|
||||
{"RefM", "p.pkg.S.M"}, /* #1913: Go method nodes are receiver-qualified */
|
||||
{"RefI", "p.pkg.I"}, {"RefDo", "p.pkg.I.Do"}, {"RefC", "p.pkg.C"},
|
||||
{"RefK", "p.pkg.K"}, {"RefV", "p.pkg.V"}, {"RefA", "p.pkg.A"},
|
||||
};
|
||||
for (size_t i = 0; i < GO_COUNT(want); i++) {
|
||||
const CBMDocLink *t = dm_find_token(r, want[i].raw);
|
||||
@@ -772,14 +772,15 @@ TEST(doc_mentions_go_scope_blob) {
|
||||
"T\tAlias\ta\tal.Widget\t1",
|
||||
"T\tSame\ta\tKind\t1",
|
||||
"T\tList\ts\t-\t1",
|
||||
/* functions and methods; two methods named Run compete for one node
|
||||
* and the later one keeps it */
|
||||
/* functions and methods; a method node is receiver-qualified
|
||||
* (#1913), so a method claims no flat name and its w is `-`: the two
|
||||
* methods named Run no longer compete */
|
||||
"F\tNew\t1",
|
||||
"F\tMap\t1",
|
||||
"M\tWidget\tRun\t0",
|
||||
"M\tKind\tRun\t1",
|
||||
"M\tList\tPush\t1",
|
||||
"M\tlower\tExported\t1",
|
||||
"M\tWidget\tRun\t-",
|
||||
"M\tKind\tRun\t-",
|
||||
"M\tList\tPush\t-",
|
||||
"M\tlower\tExported\t-",
|
||||
/* own type parameters, by the declaration's first and last line */
|
||||
"g\t36\t36\tT,U",
|
||||
"g\t42\t42\tA,B",
|
||||
@@ -814,15 +815,20 @@ TEST(doc_mentions_go_scope_blob) {
|
||||
char *portable = cbm_doclink_portable_scope(s);
|
||||
ASSERT_NOT_NULL(portable);
|
||||
ASSERT_NULL(strstr(portable, "\ng\t"));
|
||||
ASSERT_TRUE(go_has_rec(portable, "M\tKind\tRun\t1"));
|
||||
ASSERT_TRUE(go_has_rec(portable, "M\tKind\tRun\t-"));
|
||||
ASSERT(strncmp(portable, "go1\n", 4) == 0);
|
||||
cbm_free(CBM_MEM_CLASS_OTHER, portable);
|
||||
cbm_free_result(r);
|
||||
|
||||
/* moving declarations changes nothing unless a node changes its owner */
|
||||
/* moving declarations changes nothing unless a node changes its owner.
|
||||
* Methods own no flat name (#1913), so swapping two methods of one name
|
||||
* changes nothing either; two package-level declarations of one name
|
||||
* still hand <dir>.<name> over when swapped */
|
||||
const char *a = "package p\n\nfunc (a A) Run() {}\nfunc (b B) Run() {}\nfunc F() {}\n";
|
||||
const char *moved = "package p\n\nfunc F() {}\n\n\nfunc (a A) Run() {}\nfunc (b B) Run() {}\n";
|
||||
const char *swapped = "package p\n\nfunc (b B) Run() {}\nfunc (a A) Run() {}\nfunc F() {}\n";
|
||||
const char *pair = "package p\n\nfunc Open() {}\ntype Open struct{}\n";
|
||||
const char *pair_swapped = "package p\n\ntype Open struct{}\nfunc Open() {}\n";
|
||||
CBMFileResult *ra = go_extract(a, "p/x.go");
|
||||
CBMFileResult *rm = go_extract(moved, "p/x.go");
|
||||
CBMFileResult *rs = go_extract(swapped, "p/x.go");
|
||||
@@ -830,16 +836,25 @@ TEST(doc_mentions_go_scope_blob) {
|
||||
ASSERT_NOT_NULL(rm);
|
||||
ASSERT_NOT_NULL(rs);
|
||||
ASSERT_STR_EQ(ra->doc_scope, rm->doc_scope);
|
||||
ASSERT(strcmp(ra->doc_scope, rs->doc_scope) != 0);
|
||||
ASSERT_TRUE(go_has_rec(rs->doc_scope, "M\tA\tRun\t1"));
|
||||
ASSERT_TRUE(go_has_rec(rs->doc_scope, "M\tB\tRun\t0"));
|
||||
ASSERT_STR_EQ(ra->doc_scope, rs->doc_scope);
|
||||
ASSERT_TRUE(go_has_rec(rs->doc_scope, "M\tA\tRun\t-"));
|
||||
ASSERT_TRUE(go_has_rec(rs->doc_scope, "M\tB\tRun\t-"));
|
||||
cbm_free_result(ra);
|
||||
cbm_free_result(rm);
|
||||
cbm_free_result(rs);
|
||||
CBMFileResult *rp = go_extract(pair, "p/x.go");
|
||||
CBMFileResult *rq = go_extract(pair_swapped, "p/x.go");
|
||||
ASSERT_NOT_NULL(rp);
|
||||
ASSERT_NOT_NULL(rq);
|
||||
ASSERT(strcmp(rp->doc_scope, rq->doc_scope) != 0);
|
||||
ASSERT_TRUE(go_has_rec(rp->doc_scope, "T\tOpen\ts\t-\t1"));
|
||||
ASSERT_TRUE(go_has_rec(rq->doc_scope, "F\tOpen\t1"));
|
||||
cbm_free_result(rp);
|
||||
cbm_free_result(rq);
|
||||
|
||||
/* the node <dir>.<name> is one for a function, a type, a value and the
|
||||
* methods of that name: the last declaration keeps it, whatever its kind,
|
||||
* and two on one line contest it */
|
||||
/* the node <dir>.<name> is one for a function, a type and a value of
|
||||
* that name: the last declaration keeps it, whatever its kind, and two on
|
||||
* one line contest it. A method of that name claims nothing (#1913) */
|
||||
CBMFileResult *k1 = go_extract("package p\n\nfunc Open() {}\nfunc (t T) Open() {}\n", "p/k.go");
|
||||
CBMFileResult *k2 = go_extract("package p\n\nfunc (t T) Open() {}\nfunc Open() {}\n", "p/k.go");
|
||||
CBMFileResult *k3 = go_extract("package p\n\ntype Open int\nvar Open = 1\n"
|
||||
@@ -848,14 +863,21 @@ TEST(doc_mentions_go_scope_blob) {
|
||||
ASSERT_NOT_NULL(k1);
|
||||
ASSERT_NOT_NULL(k2);
|
||||
ASSERT_NOT_NULL(k3);
|
||||
ASSERT_TRUE(go_has_rec(k1->doc_scope, "F\tOpen\t0"));
|
||||
ASSERT_TRUE(go_has_rec(k1->doc_scope, "M\tT\tOpen\t1"));
|
||||
ASSERT_TRUE(go_has_rec(k1->doc_scope, "F\tOpen\t1"));
|
||||
ASSERT_TRUE(go_has_rec(k1->doc_scope, "M\tT\tOpen\t-"));
|
||||
ASSERT_TRUE(go_has_rec(k2->doc_scope, "F\tOpen\t1"));
|
||||
ASSERT_TRUE(go_has_rec(k2->doc_scope, "M\tT\tOpen\t0"));
|
||||
ASSERT_TRUE(go_has_rec(k2->doc_scope, "M\tT\tOpen\t-"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "T\tOpen\to\t-\t0"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "V\tOpen\t0"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "V\tOpen\tx"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "M\tT\tOpen\tx"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "V\tOpen\t1"));
|
||||
ASSERT_TRUE(go_has_rec(k3->doc_scope, "M\tT\tOpen\t-"));
|
||||
/* two package-level declarations on one line contest the name */
|
||||
CBMFileResult *k4 =
|
||||
go_extract("package p\n\ntype Open int\nvar Open = 1; const Open = 2\n", "p/k.go");
|
||||
ASSERT_NOT_NULL(k4);
|
||||
ASSERT_TRUE(go_has_rec(k4->doc_scope, "T\tOpen\to\t-\t0"));
|
||||
ASSERT_TRUE(go_has_rec(k4->doc_scope, "V\tOpen\tx"));
|
||||
cbm_free_result(k4);
|
||||
cbm_free_result(k1);
|
||||
cbm_free_result(k2);
|
||||
cbm_free_result(k3);
|
||||
@@ -1030,7 +1052,7 @@ static const char GO_ALPHA_C[] = "package alpha\n"
|
||||
"// Target is a target; see [A.Close] and [B.Close].\n"
|
||||
"func Target() {}\n"
|
||||
"\n"
|
||||
"// Open is a type that loses its node name to the method below.\n"
|
||||
"// Open is a type; a method of B below has the same name.\n"
|
||||
"type Open struct{}\n"
|
||||
"\n"
|
||||
"// Open opens: a method named like the type.\n"
|
||||
@@ -1295,7 +1317,7 @@ TEST(doc_mentions_go_forms) {
|
||||
static const go_want_edge_t edges[] = {
|
||||
/* [Name], [Recv.Method], an interface's method, a constant, a variable */
|
||||
{"alpha.Widget", "alpha.New", "unique"},
|
||||
{"alpha.Widget", "alpha.Run", "unique"},
|
||||
{"alpha.Widget", "alpha.Widget.Run", "unique"},
|
||||
{"alpha.Widget", "alpha.Runner.Run", "unique"},
|
||||
{"alpha.Widget", "alpha.Max", "unique"},
|
||||
{"alpha.Widget", "alpha.Default", "unique"},
|
||||
@@ -1311,7 +1333,7 @@ TEST(doc_mentions_go_forms) {
|
||||
{"alpha.New", "sub.deep.Deep", "exact"},
|
||||
{"alpha.New", "nested.pkg.Far", "exact"},
|
||||
/* [pkg.Recv.Name]: a method, a field, an interface method */
|
||||
{"alpha.New", "beta.Do", "exact"},
|
||||
{"alpha.New", "beta.Thing.Do", "exact"},
|
||||
{"alpha.New", "beta.Thing.Field", "exact"},
|
||||
{"alpha.New", "beta.Doer.Act", "exact"},
|
||||
/* an identifier with letters beyond ASCII */
|
||||
@@ -1395,7 +1417,7 @@ TEST(doc_mentions_go_plain_text) {
|
||||
ASSERT_EQ(go_bad_rows(db, rows, GO_COUNT(rows)), 0);
|
||||
/* and none of them is an edge: Run's doc holds rows, plain text and one
|
||||
* link, to the field Widget.Size */
|
||||
ASSERT_EQ(dm_mentions_from(db, "alpha.Run"), 1);
|
||||
ASSERT_EQ(dm_mentions_from(db, "alpha.Widget.Run"), 1);
|
||||
ASSERT_EQ(dm_mentions_from(db, "alpha.Bare"), 0);
|
||||
static const go_want_edge_t edges[] = {
|
||||
{"alpha.Sibling", "alpha.Widget", "unique"},
|
||||
@@ -1424,14 +1446,14 @@ TEST(doc_mentions_go_struct_fields) {
|
||||
static const go_want_edge_t edges[] = {
|
||||
/* the same package: a field named from a method's doc and from a
|
||||
* sibling field's doc */
|
||||
{"alpha.Run", "alpha.Widget.Size", "exact"},
|
||||
{"alpha.Widget.Run", "alpha.Widget.Size", "exact"},
|
||||
{"alpha.Widget.Name", "alpha.Widget.Size", "exact"},
|
||||
/* another package, by its import */
|
||||
{"alpha.New", "beta.Thing.Field", "exact"},
|
||||
{"gamma.ViaAlias", "alpha.Widget.Name", "exact"},
|
||||
/* Depth is a field of beta.Embedded, which Widget embeds: it is no
|
||||
* field of Widget */
|
||||
{"alpha.Run", "beta.Embedded.Depth", ""},
|
||||
{"alpha.Widget.Run", "beta.Embedded.Depth", ""},
|
||||
};
|
||||
ASSERT_EQ(go_bad_edges(db, edges, GO_COUNT(edges)), 0);
|
||||
/* the promoted field exists as a node: the test above is not vacuous */
|
||||
@@ -1545,7 +1567,7 @@ TEST(doc_mentions_go_lookup_order) {
|
||||
* file that imports "fmt", the repository's own in a file that imports
|
||||
* example.com/demo/fmt */
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/a.go", "fmt.Println"), "external");
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Run", "fmt.Println"), "");
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Widget.Run", "fmt.Println"), "");
|
||||
ASSERT_STR_EQ(go_tier(db, "gamma.Local", "fmt.Println"), "exact");
|
||||
/* 5. the longest module path decides: a directory inside the root module
|
||||
* that belongs to a nested module of another path is not reachable through
|
||||
@@ -1555,17 +1577,17 @@ TEST(doc_mentions_go_lookup_order) {
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.New", "sub.deep.Deep"), "exact");
|
||||
/* 6. the bare name stops at the package: beta.Make is imported and still
|
||||
* no [Make] */
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Run", "beta.Make"), "");
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Widget.Run", "beta.Make"), "");
|
||||
/* 7. members stop at the type: nothing promoted through embedding */
|
||||
ASSERT_STR_EQ(go_reason(db, "gamma/k.go", "alpha.Widget.Promoted"), "missing");
|
||||
ASSERT_STR_EQ(go_tier(db, "gamma.ViaAlias", "beta.Promoted"), "");
|
||||
ASSERT_STR_EQ(go_tier(db, "gamma.ViaAlias", "beta.Embedded.Promoted"), "");
|
||||
/* 8. an alias is followed for a member written with its package, to the
|
||||
* type that declares the member */
|
||||
static const go_want_edge_t alias[] = {
|
||||
{"gamma.ViaAlias", "beta.Do", "exact"},
|
||||
{"gamma.ViaAlias", "beta.Thing.Do", "exact"},
|
||||
{"gamma.ViaAlias", "beta.Thing.Field", "exact"},
|
||||
{"gamma.ViaAlias", "alpha.Alias", "exact"},
|
||||
{"gamma.ViaAlias", "alpha.End", "exact"}, /* seven aliases away */
|
||||
{"gamma.ViaAlias", "alpha.Deep10.End", "exact"}, /* seven aliases away */
|
||||
{"gamma.ViaAlias", "alpha.Widget.Name", "exact"},
|
||||
};
|
||||
ASSERT_EQ(go_bad_edges(db, alias, GO_COUNT(alias)), 0);
|
||||
@@ -1638,28 +1660,42 @@ TEST(doc_mentions_go_node_owner) {
|
||||
char tmp[256];
|
||||
char db[512];
|
||||
ASSERT_EQ(go_index_lookup(tmp, sizeof(tmp), db, sizeof(db)), 0);
|
||||
/* Go method nodes have a flat name: A.Close and B.Close share
|
||||
* alpha.Close, and the later declaration keeps it */
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM nodes WHERE qualified_name LIKE '%.alpha.Close'"),
|
||||
/* #1913: Go method nodes are receiver-qualified. A.Close and B.Close are
|
||||
* two nodes (no shared alpha.Close), each a target of its own */
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM nodes WHERE label = 'Method' AND "
|
||||
"qualified_name LIKE '%.alpha.A.Close'"),
|
||||
1);
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Target", "alpha.Close"), "unique");
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/c.go", "A.Close"), "graph_gap");
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM nodes WHERE label = 'Method' AND "
|
||||
"qualified_name LIKE '%.alpha.B.Close'"),
|
||||
1);
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM nodes WHERE qualified_name LIKE '%.alpha.Close'"),
|
||||
0);
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Target", "alpha.A.Close"), "unique");
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.Target", "alpha.B.Close"), "unique");
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/c.go", "A.Close"), "");
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/c.go", "B.Close"), "");
|
||||
/* the doc of the method that lost the name is not attributed to the one
|
||||
* that kept it: B.Close's [Target] is the edge, A.Close's a row */
|
||||
/* each method's doc is attributed to its own node: both [Target]s are
|
||||
* edges, from the line of that method's doc, and neither is a row */
|
||||
char props[512];
|
||||
int n = 0;
|
||||
dm_edge(db, "alpha.Close", "alpha.Target", props, sizeof(props), &n);
|
||||
dm_edge(db, "alpha.A.Close", "alpha.Target", props, sizeof(props), &n);
|
||||
ASSERT_EQ(n, 1);
|
||||
ASSERT_NOT_NULL(strstr(props, "\"line\":9,\"count\":1"));
|
||||
dm_edge(db, "alpha.B.Close", "alpha.Target", props, sizeof(props), &n);
|
||||
ASSERT_EQ(n, 1);
|
||||
ASSERT_NOT_NULL(strstr(props, "\"line\":12,\"count\":1"));
|
||||
ASSERT_EQ(dm_count(db, "SELECT line FROM doc_link_unresolved WHERE rel_path = 'alpha/c.go' "
|
||||
"AND raw = 'Target'"),
|
||||
9);
|
||||
/* a type that lost its name to a method of that name is no target, the
|
||||
* method is */
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/c.go", "Open"), "graph_gap");
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM doc_link_unresolved WHERE rel_path = "
|
||||
"'alpha/c.go' AND raw = 'Target'"),
|
||||
0);
|
||||
/* a type and a method of the same name no longer share a node: [Open]
|
||||
* is the type, [B.Open] the method */
|
||||
ASSERT_STR_EQ(go_reason(db, "alpha/c.go", "Open"), "");
|
||||
ASSERT_EQ(dm_count(db, "SELECT COUNT(*) FROM nodes WHERE label <> 'Method' AND "
|
||||
"qualified_name LIKE '%.alpha.Open'"),
|
||||
1);
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.UsesOpen", "alpha.Open"), "unique");
|
||||
ASSERT_EQ(dm_mentions_from(db, "alpha.UsesOpen"), 1);
|
||||
ASSERT_STR_EQ(go_tier(db, "alpha.UsesOpen", "alpha.B.Open"), "unique");
|
||||
ASSERT_EQ(dm_mentions_from(db, "alpha.UsesOpen"), 2);
|
||||
|
||||
/* build variants are one entity: whichever file holds the node */
|
||||
static const go_want_edge_t variants[] = {
|
||||
@@ -1836,15 +1872,16 @@ TEST(doc_mentions_go_incremental_equals_full) {
|
||||
ASSERT_EQ(dm_index(repo, inc_db, NULL), 0);
|
||||
/* the preconditions the steps below move away from */
|
||||
static const go_want_edge_t before[] = {
|
||||
{"a.Use", "b.Target", "exact"}, {"a.Use", "b.Gone", "exact"},
|
||||
{"a.Use", "b.M", "exact"}, /* T.M, in the first file of the directory, keeps b.M */
|
||||
{"a.Use", "b.Name", "exact"}, /* W.Name, the later declaration, keeps b.Name */
|
||||
{"a.Use", "a.Local", "unique"}, {"a.doc.go.__file__", "a.Local", "unique"},
|
||||
{"a.Use", "b.Target", "exact"}, {"a.Use", "b.Gone", "exact"},
|
||||
{"a.Use", "b.T.M", "exact"}, /* #1913: every method has its own node */
|
||||
{"a.Use", "b.W.Name", "exact"}, {"a.Use", "b.V.Name", "exact"},
|
||||
{"b.U.M", "b.Target", "unique"}, /* U.M is a source of its own */
|
||||
{"a.Use", "a.Local", "unique"}, {"a.doc.go.__file__", "a.Local", "unique"},
|
||||
{"a.Other", "b.Target", ""}, /* other.go does not import b */
|
||||
};
|
||||
ASSERT_EQ(go_bad_edges(inc_db, before, GO_COUNT(before)), 0);
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.V.Name"), "graph_gap");
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "b/c.go", "Target"), "graph_gap"); /* U.M has no node */
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.V.Name"), "");
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "b/c.go", "Target"), "");
|
||||
|
||||
/* a doc-comment edit re-extracts the file alone */
|
||||
go_w(repo, "a/a.go", GO_INC_A_DOC_EDIT);
|
||||
@@ -1880,23 +1917,27 @@ TEST(doc_mentions_go_incremental_equals_full) {
|
||||
0);
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.Gone"), "missing");
|
||||
|
||||
/* the method that kept a shared node name goes: the other file's method of
|
||||
* that name takes the node over, and its doc becomes a source */
|
||||
/* a method goes: links to it break, and another receiver's method of
|
||||
* the same name, in another file, is untouched */
|
||||
go_w(repo, "b/b.go", GO_INC_B_NO_M);
|
||||
ASSERT_EQ(
|
||||
dm_step(repo, inc_db, full_db, "name keeper removed", CBM_INCREMENTAL_ROUTE_CLOSURE_REPAIR),
|
||||
0);
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.T.M"), "missing");
|
||||
ASSERT_STR_EQ(go_tier(inc_db, "b.M", "b.Target"), "unique");
|
||||
ASSERT_STR_EQ(go_tier(inc_db, "b.U.M", "b.Target"), "unique");
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "b/c.go", "Target"), "");
|
||||
|
||||
/* declaration order decides which declaration keeps a shared name:
|
||||
* swapping two methods moves the node, for files with no edge into it too */
|
||||
/* declaration order no longer decides anything: swapping two methods of
|
||||
* the same name keeps both nodes and both links */
|
||||
go_w(repo, "b/d.go", GO_INC_D_SWAPPED);
|
||||
ASSERT_EQ(dm_step(repo, inc_db, full_db, "methods swapped", CBM_INCREMENTAL_ROUTE_FORCED_FULL),
|
||||
0);
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.W.Name"), "graph_gap");
|
||||
/* and the scope blob does not change (no method w moves), so the step
|
||||
* is an ordinary closure repair, no longer a forced full rebuild */
|
||||
ASSERT_EQ(
|
||||
dm_step(repo, inc_db, full_db, "methods swapped", CBM_INCREMENTAL_ROUTE_CLOSURE_REPAIR), 0);
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.W.Name"), "");
|
||||
ASSERT_STR_EQ(go_reason(inc_db, "a/a.go", "b.V.Name"), "");
|
||||
ASSERT_STR_EQ(go_tier(inc_db, "a.Use", "b.W.Name"), "exact");
|
||||
ASSERT_STR_EQ(go_tier(inc_db, "a.Use", "b.V.Name"), "exact");
|
||||
|
||||
/* renaming a target */
|
||||
go_w(repo, "b/b.go", GO_INC_B_RENAME);
|
||||
@@ -2006,10 +2047,20 @@ TEST(doc_mentions_go_scope_delta_rules) {
|
||||
GO_DELTA_HEAD GO_DELTA_F GO_DELTA_G GO_DELTA_T GO_DELTA_M GO_DELTA_I GO_DELTA_C
|
||||
"var (\n\tV = 2\n)\n",
|
||||
CBM_DOCLINK_DELTA_GLOBAL, NULL},
|
||||
/* the declaration that keeps a shared node name changes */
|
||||
{"same-named declarations swapped", twins,
|
||||
"package p\n\nfunc (T) Open() {}\nfunc Open() {}\n", CBM_DOCLINK_DELTA_GLOBAL, NULL},
|
||||
{"one of two same-named declarations removed", twins, "package p\n\nfunc Open() {}\n",
|
||||
/* a method and a function of one name no longer share a node
|
||||
* (#1913): swapping them changes no keeper */
|
||||
{"method and function swapped", twins, "package p\n\nfunc (T) Open() {}\nfunc Open() {}\n",
|
||||
CBM_DOCLINK_DELTA_LOCAL, ""},
|
||||
/* the declaration that keeps a shared node name changes: two
|
||||
* package-level declarations of one name still share <dir>.<name> */
|
||||
{"same-named declarations swapped", "package p\n\nfunc Open() {}\ntype Open struct{}\n",
|
||||
"package p\n\ntype Open struct{}\nfunc Open() {}\n", CBM_DOCLINK_DELTA_GLOBAL, NULL},
|
||||
/* the method of a method/function pair removed: a member left, the
|
||||
* function keeps its node either way */
|
||||
{"method of a same-named pair removed", twins, "package p\n\nfunc Open() {}\n",
|
||||
CBM_DOCLINK_DELTA_LOCAL, "Open"},
|
||||
{"one of two same-named declarations removed",
|
||||
"package p\n\nfunc Open() {}\ntype Open struct{}\n", "package p\n\nfunc Open() {}\n",
|
||||
CBM_DOCLINK_DELTA_GLOBAL, NULL},
|
||||
/* other files follow `type X = q.Y` through this file's imports */
|
||||
{"import changed under a qualified alias", alias,
|
||||
@@ -2091,7 +2142,10 @@ TEST(doc_mentions_go_workers_agree) {
|
||||
ASSERT_STR_EQ(go_tier(par_db, "p00.T0", "p01.T0"), "exact");
|
||||
ASSERT_STR_EQ(go_reason(par_db, "p00/f0.go", "p01.Gone"), "missing");
|
||||
ASSERT_STR_EQ(go_reason(par_db, "p00/f0.go", "fmt.Println"), "external");
|
||||
ASSERT_STR_EQ(go_reason(par_db, "p00/f0.go", "p01.T3.Close"), "graph_gap");
|
||||
/* #1913: [p01.T3.Close] lands on its own method node now; the graph_gap
|
||||
* reason stays covered by Shared, one name declared by every file */
|
||||
ASSERT_STR_EQ(go_reason(par_db, "p00/f0.go", "p01.T3.Close"), "");
|
||||
ASSERT_STR_EQ(go_tier(par_db, "p00.T0", "p01.T3.Close"), "exact");
|
||||
ASSERT_STR_EQ(go_reason(par_db, "p00/f3.go", "Shared"), "graph_gap");
|
||||
ASSERT_TRUE(dm_count(par_db, "SELECT COUNT(*) FROM edges WHERE type = 'MENTIONS'") > 100);
|
||||
dm_unlink_db(par_db);
|
||||
|
||||
Reference in new issue
Block a user