@@ -60,26 +60,36 @@ static int r964_file_node_exists(cbm_store_t *store, const char *project, const
6060 return found ;
6161}
6262
63- /* Count inbound IMPORTS edges whose TARGET node name is exactly `name`. */
64- static int r964_inbound_imports (cbm_store_t * store , const char * project , const char * name ) {
65- cbm_edge_t * edges = NULL ;
66- int n = 0 ;
67- if (cbm_store_find_edges_by_type (store , project , "IMPORTS" , & edges , & n ) != CBM_STORE_OK )
68- return -1 ;
69- int hits = 0 ;
70- for (int i = 0 ; i < n ; i ++ ) {
71- cbm_node_t tgt ;
72- if (cbm_store_find_node_by_id (store , edges [i ].target_id , & tgt ) != CBM_STORE_OK )
63+ /* Count INBOUND edges (any of the reporter's connection types) landing on the
64+ * File node named `name` — the exact disconnection metric from the issue's
65+ * query `NOT EXISTS { (f)<-[:IMPORTS|CALLS|CONTAINS_FILE|DEFINES|USAGE]-() }`.
66+ * A header that used to have no node at all (extension-stripped QN collision)
67+ * now has its own File node AND is connected (CONTAINS_FILE from its folder,
68+ * plus DEFINES of the class it declares); `#include` itself resolves to the
69+ * declared Class node via #983, not to the header File. */
70+ static int r964_file_inbound (cbm_store_t * store , const char * project , const char * name ) {
71+ static const char * const types [] = {"IMPORTS" , "CALLS" , "CONTAINS_FILE" ,
72+ "DEFINES" , "USAGE" , NULL };
73+ int total = 0 ;
74+ for (int t = 0 ; types [t ]; t ++ ) {
75+ cbm_edge_t * edges = NULL ;
76+ int n = 0 ;
77+ if (cbm_store_find_edges_by_type (store , project , types [t ], & edges , & n ) != CBM_STORE_OK )
7378 continue ;
74- if (tgt .name && strcmp (tgt .name , name ) == 0 )
75- hits ++ ;
76- cbm_node_free_fields (& tgt );
79+ for (int i = 0 ; i < n ; i ++ ) {
80+ cbm_node_t tgt ;
81+ if (cbm_store_find_node_by_id (store , edges [i ].target_id , & tgt ) != CBM_STORE_OK )
82+ continue ;
83+ if (tgt .name && strcmp (tgt .name , name ) == 0 )
84+ total ++ ;
85+ cbm_node_free_fields (& tgt );
86+ }
87+ cbm_store_free_edges (edges , n );
7788 }
78- cbm_store_free_edges (edges , n );
79- return hits ;
89+ return total ;
8090}
8191
82- TEST (repro_issue964_header_has_node_and_inbound_import ) {
92+ TEST (repro_issue964_header_has_node_and_is_connected ) {
8393 static const RFile files [] = {
8494 {"NodeController.h" , "#pragma once\n"
8595 "class NodeController {\n"
@@ -102,20 +112,20 @@ TEST(repro_issue964_header_has_node_and_inbound_import) {
102112 ASSERT_NOT_NULL (store );
103113
104114 int header_node = r964_file_node_exists (store , lp .project , "NodeController.h" );
105- int header_inbound = r964_inbound_imports (store , lp .project , "NodeController.h" );
115+ int header_inbound = r964_file_inbound (store , lp .project , "NodeController.h" );
106116 if (!header_node || header_inbound < 1 ) {
107117 fprintf (stderr ,
108- " [964] FAIL header_file_node=%d inbound_imports =%d (header merged into "
109- "same-stem .cpp by extension-stripped QN collision)\n" ,
118+ " [964] FAIL header_file_node=%d inbound_edges =%d (header merged into "
119+ "same-stem .cpp by extension-stripped QN collision, or left disconnected )\n" ,
110120 header_node , header_inbound );
111121 }
112- ASSERT_TRUE (header_node ); /* header must keep its own File node */
113- ASSERT_TRUE (header_inbound >= 1 ); /* #include must land an edge on it */
122+ ASSERT_TRUE (header_node ); /* header keeps its own File node (not merged) */
123+ ASSERT_TRUE (header_inbound >= 1 ); /* and is connected (not the reporter's zero-inbound) */
114124
115125 rh_cleanup (& lp , store );
116126 PASS ();
117127}
118128
119129SUITE (repro_issue964 ) {
120- RUN_TEST (repro_issue964_header_has_node_and_inbound_import );
130+ RUN_TEST (repro_issue964_header_has_node_and_is_connected );
121131}
0 commit comments