@@ -1812,6 +1812,62 @@ TEST(c_caller_attribution) {
18121812 PASS ();
18131813}
18141814
1815+ /* adc8304 (the dedup refactor bundled into #463) re-pointed the C/C++ enclosing-
1816+ * function resolver at the canonical declarator walker: qualified names (Foo::bar)
1817+ * now resolve via resolve_qualified_name(), and `type_identifier` was dropped from
1818+ * the terminal-name set. These guard that out-of-line C++ method / ctor / dtor
1819+ * definitions still attribute their inner calls to the enclosing function, not the
1820+ * module — i.e. that the dedup did not reintroduce the #438 regression on the
1821+ * qualified-declarator path. Module QN for "m.cpp" under prefix "t" is "t.m". */
1822+ TEST (cpp_out_of_line_method_caller_attribution ) {
1823+ CBMFileResult * r = extract ("struct Foo { void bar(); };\n"
1824+ "int helper(int x) { return x; }\n"
1825+ "void Foo::bar() { helper(1); }\n" ,
1826+ CBM_LANG_CPP , "t" , "m.cpp" );
1827+ ASSERT_NOT_NULL (r );
1828+ ASSERT_FALSE (r -> has_error );
1829+ ASSERT_GT (r -> calls .count , 0 );
1830+ int saw_helper = 0 ;
1831+ for (int i = 0 ; i < r -> calls .count ; i ++ ) {
1832+ if (strcmp (r -> calls .items [i ].callee_name , "helper" ) == 0 ) {
1833+ saw_helper = 1 ;
1834+ /* enclosing must be the out-of-line method, NOT empty and NOT the module. */
1835+ ASSERT_NOT_NULL (r -> calls .items [i ].enclosing_func_qn );
1836+ ASSERT_FALSE (strcmp (r -> calls .items [i ].enclosing_func_qn , "" ) == 0 );
1837+ ASSERT_FALSE (strcmp (r -> calls .items [i ].enclosing_func_qn , "t.m" ) == 0 );
1838+ }
1839+ }
1840+ ASSERT (saw_helper );
1841+ cbm_free_result (r );
1842+ PASS ();
1843+ }
1844+
1845+ /* Out-of-line constructor (Foo::Foo) and destructor (Foo::~Foo) exercise the
1846+ * identifier and destructor_name branches of resolve_qualified_name(). A call
1847+ * inside either must attribute to that special member, not the module. */
1848+ TEST (cpp_out_of_line_ctor_dtor_caller_attribution ) {
1849+ CBMFileResult * r = extract ("struct Foo { Foo(); ~Foo(); };\n"
1850+ "int helper(int x) { return x; }\n"
1851+ "Foo::Foo() { helper(1); }\n"
1852+ "Foo::~Foo() { helper(2); }\n" ,
1853+ CBM_LANG_CPP , "t" , "m.cpp" );
1854+ ASSERT_NOT_NULL (r );
1855+ ASSERT_FALSE (r -> has_error );
1856+ ASSERT_GT (r -> calls .count , 0 );
1857+ int helper_calls = 0 ;
1858+ for (int i = 0 ; i < r -> calls .count ; i ++ ) {
1859+ if (strcmp (r -> calls .items [i ].callee_name , "helper" ) == 0 ) {
1860+ helper_calls ++ ;
1861+ ASSERT_NOT_NULL (r -> calls .items [i ].enclosing_func_qn );
1862+ ASSERT_FALSE (strcmp (r -> calls .items [i ].enclosing_func_qn , "" ) == 0 );
1863+ ASSERT_FALSE (strcmp (r -> calls .items [i ].enclosing_func_qn , "t.m" ) == 0 );
1864+ }
1865+ }
1866+ ASSERT (helper_calls >= 1 );
1867+ cbm_free_result (r );
1868+ PASS ();
1869+ }
1870+
18151871/* --- Wolfram parse (simple assignment) --- */
18161872TEST (wolfram_parse ) {
18171873 CBMFileResult * r = extract ("x = 42;\ny = x + 1;\n" , CBM_LANG_WOLFRAM , "t" , "simple.wl" );
@@ -3117,6 +3173,8 @@ SUITE(extraction) {
31173173 RUN_TEST (wolfram_call );
31183174 RUN_TEST (wolfram_caller_attribution );
31193175 RUN_TEST (c_caller_attribution );
3176+ RUN_TEST (cpp_out_of_line_method_caller_attribution );
3177+ RUN_TEST (cpp_out_of_line_ctor_dtor_caller_attribution );
31203178 RUN_TEST (wolfram_parse );
31213179 RUN_TEST (wolfram_import );
31223180 RUN_TEST (wolfram_nested_def );
0 commit comments