@@ -241,6 +241,20 @@ static void ei_cleanup(EILangProj *lp, cbm_store_t *store) {
241241
242242/* Index `files`, check IMPORTS count >= `floor`. Dumps a diagnostic on
243243 * failure so failures are self-diagnosable without re-running manually. */
244+ /* Exact-count variant of ei_edge_present: a fabricated EXTRA edge must fail
245+ * the probe, so a floor is not enough (#1932's negative-assertion gap). */
246+ static int ei_edge_count_is (const EILangFile * files , int nfiles , const char * edge_type ,
247+ int expected ) {
248+ EILangProj lp ;
249+ cbm_store_t * store = ei_index_files (& lp , files , nfiles );
250+ int got = store ? cbm_store_count_edges_by_type (store , lp .project , edge_type ) : -1 ;
251+ if (got != expected ) {
252+ fprintf (stderr , " [%s] FAIL count=%d expected==%d\n" , edge_type , got , expected );
253+ }
254+ ei_cleanup (& lp , store );
255+ return got == expected ;
256+ }
257+
244258static int ei_edge_present (const EILangFile * files , int nfiles , const char * edge_type , int floor ) {
245259 EILangProj lp ;
246260 cbm_store_t * store = ei_index_files (& lp , files , nfiles );
@@ -513,6 +527,34 @@ TEST(ei_go_two_consumers_same_package) {
513527 PASS ();
514528}
515529
530+ TEST (ei_go_import_never_binds_symbol ) {
531+ /* #1934: a Go import path names a package, never a symbol. `os/exec` is
532+ * external (not in the graph), so the ONLY correct outcome is no edge —
533+ * but Strategy 3's symbol-name fallback matched the path's last segment
534+ * against any project definition named `exec` and bound the import to a
535+ * test harness's method. Exact count: the internal `util` import (edge 1,
536+ * via Strategy 1 → the package Folder) must be the whole IMPORTS
537+ * relation; the fallback edge onto harness.exec (reproduce-first RED:
538+ * count 2) must not exist. */
539+ static const EILangFile f [] = {
540+ {"go.mod" , "module example.com/fxi\n\ngo 1.22\n" },
541+ {"util/util.go" , "package util\n\nfunc Tag() string { return \"t\" }\n" },
542+ {"helper/harness.go" , "package helper\n\ntype harness struct{ n int }\n\n"
543+ "func (h *harness) exec(cmd string) error { return nil }\n" },
544+ /* Same-package decoy: the field-measured survivor bound os/exec to a
545+ * method in the IMPORTER'S OWN package (Strategy 1b's sibling-file
546+ * resolution accepts symbol labels too), so the fixture needs the
547+ * collision both cross-package and same-package. */
548+ {"app/aux.go" , "package app\n\ntype runner struct{ n int }\n\n"
549+ "func (r *runner) exec(cmd string) error { return nil }\n" },
550+ {"app/run.go" , "package app\n\nimport (\n\t\"os/exec\"\n\n"
551+ "\t\"example.com/fxi/util\"\n)\n\n"
552+ "func Run() error {\n\t_ = util.Tag()\n"
553+ "\treturn exec.Command(\"true\").Run()\n}\n" }};
554+ ASSERT_TRUE (ei_edge_count_is (f , 5 , "IMPORTS" , 1 ));
555+ PASS ();
556+ }
557+
516558/* C++: header include should resolve to the header file node, not the same-stem
517559 * source node. Also exercises angle-bracket include resolution. */
518560TEST (ei_cpp_header_include_targets_header_file ) {
@@ -1037,6 +1079,7 @@ SUITE(edge_imports) {
10371079 RUN_TEST (ei_go_subpackage_import );
10381080 RUN_TEST (ei_go_blank_import );
10391081 RUN_TEST (ei_go_two_consumers_same_package );
1082+ RUN_TEST (ei_go_import_never_binds_symbol );
10401083 RUN_TEST (ei_cpp_header_include_targets_header_file );
10411084
10421085 /* ── RED REPRODUCTIONS — Rust (expected to FAIL until pipeline fixed) ── */
0 commit comments