mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(go): a module rooted at the repository root imports as the Project node
A Go import targets the node of its package's directory, and the strict
import path accepted only Folder nodes. The repository root has none: the
Project node is that directory, and its QN is the module of every root
file. caddy keeps go.mod and `package caddy` at the root, imported as
"github.com/caddyserver/caddy/v2", so that import never resolved and every
caddy.X call, and everything typed through one, stayed unknown under the
generic shared_binding_type_fixed_point reason. No cap was involved.
go_package_node accepts the Project node for the root directory in the
module-path strategy, the duplicate-module fallback and the ownership
check, which reads the package directory ("" for the root) from it. The
ownership rule itself is unchanged: a module without a replace directive
or go.work entry still takes the path from the module cache.
caddy against SCIP: recall 83.45% -> 95.89%, real lost 794 -> 200, never
found 229 -> 54, new false 0 -> 0. traefik, minio, cli, helm, prometheus
and etcd unchanged.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
a1c437432f
commit
0ecf116e61
2 files changed
+123
-9
No files matched your search
@@ -5445,6 +5445,27 @@ static const cbm_gbuf_node_t *typescript_paths_target(const cbm_pipeline_ctx_t *
|
||||
static const cbm_gbuf_node_t *go_import_by_source_module(const cbm_pipeline_ctx_t *ctx,
|
||||
const char *source_rel, const char *path);
|
||||
|
||||
/* A Go package is a directory, and an import targets that directory's node. The repository's
|
||||
* own root is no Folder: the Project node is that directory, its QN the module of every root
|
||||
* file (graph_buffer.c). caddy's go.mod and `package caddy` both sit there. *directory is the
|
||||
* package's directory relative to the repository, NULL when the node records none. */
|
||||
static bool go_package_node(const cbm_pipeline_ctx_t *ctx, const cbm_gbuf_node_t *node,
|
||||
const char **directory) {
|
||||
if (!node || !node->label)
|
||||
return false;
|
||||
if (!strcmp(node->label, "Folder")) {
|
||||
if (directory)
|
||||
*directory = node->file_path;
|
||||
return true;
|
||||
}
|
||||
if (strcmp(node->label, "Project") || !ctx->project_name || !node->qualified_name ||
|
||||
strcmp(node->qualified_name, ctx->project_name))
|
||||
return false;
|
||||
if (directory)
|
||||
*directory = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
static const cbm_gbuf_node_t *resolve_import_strategies(
|
||||
const cbm_pipeline_ctx_t *ctx, const char *source_rel, const CBMImport *imp,
|
||||
CBMHashTable *namespace_map, CBMLanguage src_lang, bool php_only, bool *ambiguous) {
|
||||
@@ -5498,7 +5519,7 @@ static const cbm_gbuf_node_t *resolve_import_strategies(
|
||||
free(qn);
|
||||
if (target && (!php_only || node_in_php_file(target)) &&
|
||||
(import_language_matches(src_lang, target) ||
|
||||
(src_lang == CBM_LANG_GO && target->label && strcmp(target->label, "Folder") == 0))) {
|
||||
(src_lang == CBM_LANG_GO && go_package_node(ctx, target, NULL)))) {
|
||||
if (target->label &&
|
||||
(strcmp(target->label, "Module") == 0 || strcmp(target->label, "File") == 0)) {
|
||||
char sym[CBM_SZ_256];
|
||||
@@ -5517,7 +5538,7 @@ static const cbm_gbuf_node_t *resolve_import_strategies(
|
||||
}
|
||||
return target;
|
||||
}
|
||||
if (src_lang == CBM_LANG_GO && target->label && strcmp(target->label, "Folder") == 0)
|
||||
if (src_lang == CBM_LANG_GO && go_package_node(ctx, target, NULL))
|
||||
return target;
|
||||
/* Python's dotted import path may name a declaration exactly; a
|
||||
* plain module import may never bind an arbitrary function/class. */
|
||||
@@ -6506,7 +6527,7 @@ static const cbm_gbuf_node_t *go_import_by_source_module(const cbm_pipeline_ctx_
|
||||
char *qn = cbm_pipeline_fqn_folder(ctx->project_name, package);
|
||||
const cbm_gbuf_node_t *node = qn ? cbm_gbuf_find_by_qn(ctx->gbuf, qn) : NULL;
|
||||
free(qn);
|
||||
return node && node->label && !strcmp(node->label, "Folder") ? node : NULL;
|
||||
return go_package_node(ctx, node, NULL) ? node : NULL;
|
||||
}
|
||||
|
||||
static bool go_import_inside_module(const char *import, const char *module, const char *owner,
|
||||
@@ -6524,18 +6545,17 @@ static bool go_import_inside_module(const char *import, const char *module, cons
|
||||
|
||||
bool cbm_pipeline_go_import_owned(const cbm_pipeline_ctx_t *ctx, const char *source_rel,
|
||||
const CBMImport *import, const cbm_gbuf_node_t *target) {
|
||||
if (!ctx || !source_rel || !import || !import->module_path || !target || !target->file_path ||
|
||||
!target->label || strcmp(target->label, "Folder"))
|
||||
const char *package_dir = NULL;
|
||||
if (!ctx || !source_rel || !import || !import->module_path ||
|
||||
!go_package_node(ctx, target, &package_dir) || !package_dir)
|
||||
return false;
|
||||
char source_owner[PKGMAP_PATH_BUF], source_module[PKGMAP_PATH_BUF],
|
||||
target_owner[PKGMAP_PATH_BUF], target_module[PKGMAP_PATH_BUF];
|
||||
if (!go_module_owner(ctx, source_rel, false, source_owner, source_module,
|
||||
sizeof(source_owner)) ||
|
||||
!go_module_owner(ctx, target->file_path, true, target_owner, target_module,
|
||||
sizeof(target_owner)))
|
||||
!go_module_owner(ctx, package_dir, true, target_owner, target_module, sizeof(target_owner)))
|
||||
return false;
|
||||
if (!go_import_inside_module(import->module_path, target_module, target_owner,
|
||||
target->file_path))
|
||||
if (!go_import_inside_module(import->module_path, target_module, target_owner, package_dir))
|
||||
return false;
|
||||
if (!strcmp(source_owner, target_owner))
|
||||
return !strcmp(source_module, target_module);
|
||||
|
||||
@@ -1912,6 +1912,84 @@ static int go_duplicate_module_case(bool parallel) {
|
||||
th_rmtree(root);
|
||||
PASS();
|
||||
}
|
||||
/* caddy's go.mod sits at the repository root and its root directory is a package, imported as
|
||||
* "github.com/caddyserver/caddy/v2" and bound as caddy. That directory has no Folder node: the
|
||||
* Project node is it, since its QN is every root file's module. A decoy namesake lives in a
|
||||
* subpackage, and a module without a replace directive takes the path from the module cache.
|
||||
* With `duplicate`, a fixture's go.mod also declares the module path, as cli/cli's CodeQL tests
|
||||
* do: the importer's own module still means the root, never the fixture's namesake package. */
|
||||
static int go_module_root_package_case(bool parallel, bool duplicate) {
|
||||
const char *root = th_mktempdir("cbm_go_root_package");
|
||||
ASSERT_NOT_NULL(root);
|
||||
char repo[1024], database[1024];
|
||||
snprintf(repo, sizeof(repo), "%s/repo", root);
|
||||
snprintf(database, sizeof(database), "%s/index.db", root);
|
||||
ASSERT(cbm_mkdir_p(TH_PATH(repo, "caddyconfig/caddyfile"), 0755));
|
||||
ASSERT(cbm_mkdir_p(TH_PATH(repo, "foreign"), 0755));
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "go.mod"), "module example/caddy/v2\ngo 1.22\n"), 0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "foreign/go.mod"), "module example/foreign\ngo 1.22\n"),
|
||||
0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "caddy.go"),
|
||||
"// Copyright notice.\n\npackage caddy\n\n"
|
||||
"type Replacer struct{}\n\n"
|
||||
"func NewReplacer() *Replacer { return &Replacer{} }\n\n"
|
||||
"func (r *Replacer) ReplaceKnown(input, empty string) string {\n"
|
||||
"\treturn input\n}\n\n"
|
||||
"func GetModule(name string) (string, error) { return name, nil }\n"),
|
||||
0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "caddyconfig/adapters.go"),
|
||||
"package caddyconfig\n\n"
|
||||
"func GetModule(name string) (string, error) { return \"\", nil }\n"),
|
||||
0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "caddyconfig/caddyfile/adapter.go"),
|
||||
"package caddyfile\n\nimport (\n\t\"example/caddy/v2\"\n)\n\n"
|
||||
"func UnmarshalModule(moduleID string) (string, error) {\n"
|
||||
"\tmod, err := caddy.GetModule(moduleID)\n"
|
||||
"\trepl := caddy.NewReplacer()\n"
|
||||
"\treturn repl.ReplaceKnown(mod, \"\"), err\n}\n"),
|
||||
0);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, "foreign/use.go"),
|
||||
"package foreign\n\nimport \"example/caddy/v2\"\n\n"
|
||||
"func Cached() { caddy.GetModule(\"x\") }\n"),
|
||||
0);
|
||||
if (duplicate) {
|
||||
ASSERT(cbm_mkdir_p(TH_PATH(repo, "fixture"), 0755));
|
||||
ASSERT_EQ(
|
||||
th_write_file(TH_PATH(repo, "fixture/go.mod"), "module example/caddy/v2\ngo 1.22\n"),
|
||||
0);
|
||||
ASSERT_EQ(
|
||||
th_write_file(TH_PATH(repo, "fixture/caddy.go"),
|
||||
"package caddy\n\n"
|
||||
"func GetModule(name string) (string, error) { return \"\", nil }\n"),
|
||||
0);
|
||||
}
|
||||
for (int i = 0; parallel && i < 52; i++) {
|
||||
char name[64], source[96];
|
||||
snprintf(name, sizeof(name), "padding_%02d.py", i);
|
||||
snprintf(source, sizeof(source), "def padding_%02d():\n return %d\n", i, i);
|
||||
ASSERT_EQ(th_write_file(TH_PATH(repo, name), source), 0);
|
||||
}
|
||||
ASSERT_EQ(index_fixture(repo, database, parallel), 0);
|
||||
cbm_store_t *store = cbm_store_open_path_existing(database);
|
||||
ASSERT_NOT_NULL(store);
|
||||
sqlite3 *db = cbm_store_get_db(store);
|
||||
const char *user = "test.caddyconfig.caddyfile.UnmarshalModule";
|
||||
ASSERT_EQ(
|
||||
count_edge_between(db, "IMPORTS", "test.caddyconfig.caddyfile.adapter.go.__file__", "test"),
|
||||
1);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", user, "test.GetModule"), 1);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", user, "test.NewReplacer"), 1);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", user, "test.Replacer.ReplaceKnown"), 1);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", user, "test.caddyconfig.GetModule"), 0);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", user, "test.fixture.GetModule"), 0);
|
||||
ASSERT_EQ(count_edge_between(db, "IMPORTS", "test.foreign.use.go.__file__", "test"), 0);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", "test.foreign.Cached", "test.GetModule"), 0);
|
||||
ASSERT_EQ(count_edge_between(db, "CALLS", "test.foreign.Cached", "test.caddyconfig.GetModule"),
|
||||
0);
|
||||
cbm_store_close(store);
|
||||
th_rmtree(root);
|
||||
PASS();
|
||||
}
|
||||
/* Go 1.26's new(expr) is an ERROR to the grammar, which used to cost the declaration around it.
|
||||
* A decoy method and a function that merely end in new stay what they are. */
|
||||
static int go_new_expression_case(bool parallel) {
|
||||
@@ -2219,6 +2297,18 @@ TEST(resolver_stabilization_go_duplicate_module_path_sequential) {
|
||||
TEST(resolver_stabilization_go_duplicate_module_path_parallel) {
|
||||
return go_duplicate_module_case(true);
|
||||
}
|
||||
TEST(resolver_stabilization_go_module_root_package_sequential) {
|
||||
return go_module_root_package_case(false, false);
|
||||
}
|
||||
TEST(resolver_stabilization_go_module_root_package_parallel) {
|
||||
return go_module_root_package_case(true, false);
|
||||
}
|
||||
TEST(resolver_stabilization_go_module_root_beside_duplicate_module_sequential) {
|
||||
return go_module_root_package_case(false, true);
|
||||
}
|
||||
TEST(resolver_stabilization_go_module_root_beside_duplicate_module_parallel) {
|
||||
return go_module_root_package_case(true, true);
|
||||
}
|
||||
TEST(resolver_stabilization_go_pipeline_references_sequential) {
|
||||
return go_references_case(false);
|
||||
}
|
||||
@@ -2337,6 +2427,10 @@ SUITE(resolver_stabilization) {
|
||||
RUN_TEST(resolver_stabilization_go_anonymous_structs_and_conversions_parallel);
|
||||
RUN_TEST(resolver_stabilization_go_duplicate_module_path_sequential);
|
||||
RUN_TEST(resolver_stabilization_go_duplicate_module_path_parallel);
|
||||
RUN_TEST(resolver_stabilization_go_module_root_package_sequential);
|
||||
RUN_TEST(resolver_stabilization_go_module_root_package_parallel);
|
||||
RUN_TEST(resolver_stabilization_go_module_root_beside_duplicate_module_sequential);
|
||||
RUN_TEST(resolver_stabilization_go_module_root_beside_duplicate_module_parallel);
|
||||
RUN_TEST(resolver_stabilization_go_pipeline_references_sequential);
|
||||
RUN_TEST(resolver_stabilization_go_pipeline_references_parallel);
|
||||
RUN_TEST(resolver_stabilization_go_pipeline_twins_and_package_level_sequential);
|
||||
|
||||
Reference in new issue
Block a user