diff --git a/tests/test_extraction.c b/tests/test_extraction.c index 9aba9dc7..7b2a1071 100644 --- a/tests/test_extraction.c +++ b/tests/test_extraction.c @@ -1812,6 +1812,62 @@ TEST(c_caller_attribution) { PASS(); } +/* adc8304 (the dedup refactor bundled into #463) re-pointed the C/C++ enclosing- + * function resolver at the canonical declarator walker: qualified names (Foo::bar) + * now resolve via resolve_qualified_name(), and `type_identifier` was dropped from + * the terminal-name set. These guard that out-of-line C++ method / ctor / dtor + * definitions still attribute their inner calls to the enclosing function, not the + * module — i.e. that the dedup did not reintroduce the #438 regression on the + * qualified-declarator path. Module QN for "m.cpp" under prefix "t" is "t.m". */ +TEST(cpp_out_of_line_method_caller_attribution) { + CBMFileResult *r = extract("struct Foo { void bar(); };\n" + "int helper(int x) { return x; }\n" + "void Foo::bar() { helper(1); }\n", + CBM_LANG_CPP, "t", "m.cpp"); + ASSERT_NOT_NULL(r); + ASSERT_FALSE(r->has_error); + ASSERT_GT(r->calls.count, 0); + int saw_helper = 0; + for (int i = 0; i < r->calls.count; i++) { + if (strcmp(r->calls.items[i].callee_name, "helper") == 0) { + saw_helper = 1; + /* enclosing must be the out-of-line method, NOT empty and NOT the module. */ + ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn); + ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0); + ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.m") == 0); + } + } + ASSERT(saw_helper); + cbm_free_result(r); + PASS(); +} + +/* Out-of-line constructor (Foo::Foo) and destructor (Foo::~Foo) exercise the + * identifier and destructor_name branches of resolve_qualified_name(). A call + * inside either must attribute to that special member, not the module. */ +TEST(cpp_out_of_line_ctor_dtor_caller_attribution) { + CBMFileResult *r = extract("struct Foo { Foo(); ~Foo(); };\n" + "int helper(int x) { return x; }\n" + "Foo::Foo() { helper(1); }\n" + "Foo::~Foo() { helper(2); }\n", + CBM_LANG_CPP, "t", "m.cpp"); + ASSERT_NOT_NULL(r); + ASSERT_FALSE(r->has_error); + ASSERT_GT(r->calls.count, 0); + int helper_calls = 0; + for (int i = 0; i < r->calls.count; i++) { + if (strcmp(r->calls.items[i].callee_name, "helper") == 0) { + helper_calls++; + ASSERT_NOT_NULL(r->calls.items[i].enclosing_func_qn); + ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "") == 0); + ASSERT_FALSE(strcmp(r->calls.items[i].enclosing_func_qn, "t.m") == 0); + } + } + ASSERT(helper_calls >= 1); + cbm_free_result(r); + PASS(); +} + /* --- Wolfram parse (simple assignment) --- */ TEST(wolfram_parse) { CBMFileResult *r = extract("x = 42;\ny = x + 1;\n", CBM_LANG_WOLFRAM, "t", "simple.wl"); @@ -3117,6 +3173,8 @@ SUITE(extraction) { RUN_TEST(wolfram_call); RUN_TEST(wolfram_caller_attribution); RUN_TEST(c_caller_attribution); + RUN_TEST(cpp_out_of_line_method_caller_attribution); + RUN_TEST(cpp_out_of_line_ctor_dtor_caller_attribution); RUN_TEST(wolfram_parse); RUN_TEST(wolfram_import); RUN_TEST(wolfram_nested_def);