diff --git a/internal/cbm/cbm.c b/internal/cbm/cbm.c index f38eaad6..6505216d 100644 --- a/internal/cbm/cbm.c +++ b/internal/cbm/cbm.c @@ -147,6 +147,47 @@ void cbm_rw_push(CBMRWArray *arr, CBMArena *a, CBMReadWrite rw) { arr->items[arr->count++] = rw; } +static bool def_label_is_preprocessed_callable(const char *label) { + return label && (strcmp(label, "Function") == 0 || strcmp(label, "Method") == 0); +} + +static bool result_has_same_def_identity(const CBMFileResult *result, const CBMDefinition *def) { + if (!result || !def || !def->label || !def->qualified_name) { + return false; + } + for (int i = 0; i < result->defs.count; i++) { + const CBMDefinition *cur = &result->defs.items[i]; + if (!cur->label || !cur->qualified_name) { + continue; + } + if (strcmp(cur->label, def->label) == 0 && + strcmp(cur->qualified_name, def->qualified_name) == 0) { + return true; + } + } + return false; +} + +static void merge_missing_preprocessed_callables(CBMFileResult *dst, const CBMFileResult *src, + CBMArena *arena) { + if (!dst || !src) { + return; + } + for (int i = 0; i < src->defs.count; i++) { + const CBMDefinition *def = &src->defs.items[i]; + if (!def->qualified_name || !def->name) { + continue; + } + if (!def_label_is_preprocessed_callable(def->label)) { + continue; + } + if (result_has_same_def_identity(dst, def)) { + continue; + } + cbm_defs_push(&dst->defs, arena, *def); + } +} + void cbm_typerefs_push(CBMTypeRefArray *arr, CBMArena *a, CBMTypeRef tr) { GROW_ARRAY(arr, a); arr->items[arr->count++] = tr; @@ -879,8 +920,10 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len, // metrics. Remember the boundary. int orig_calls_count = result->calls.count; - // Second pass: preprocess C/C++/CUDA and extract additional macro-hidden calls. - // Defs keep original-source line numbers; only CALLS are extracted from expanded source. + // Second pass: preprocess C/C++/CUDA and extract additional macro-hidden + // callables/calls. Existing defs keep original-source line numbers; only + // callables missing from the raw tree-sitter pass are filled from expanded + // source so #ifdef/#else signature blocks cannot swallow later methods. if (language == CBM_LANG_C || language == CBM_LANG_CPP || language == CBM_LANG_CUDA) { uint64_t pp_start = now_ns(); char *expanded = cbm_preprocess(source, source_len, rel_path, extra_defines, include_paths, @@ -919,6 +962,14 @@ static CBMFileResult *cbm_extract_file_impl(const char *source, int source_len, .module_qn = result->module_qn, .root = pp_root, }; + CBMFileResult pp_defs_result = {0}; + pp_defs_result.module_qn = result->module_qn; + pp_defs_result.is_test_file = result->is_test_file; + CBMExtractCtx pp_defs_ctx = pp_ctx; + pp_defs_ctx.result = &pp_defs_result; + cbm_extract_definition_nodes(&pp_defs_ctx); + merge_missing_preprocessed_callables(result, &pp_defs_result, a); + // Re-run unified extraction on expanded source. // This adds macro-expanded calls; duplicates with original calls are // harmless (pipeline deduplicates by caller+callee). diff --git a/internal/cbm/cbm.h b/internal/cbm/cbm.h index 6b422813..53ae603e 100644 --- a/internal/cbm/cbm.h +++ b/internal/cbm/cbm.h @@ -609,6 +609,8 @@ void cbm_channels_push(CBMChannelArray *arr, CBMArena *a, CBMChannel ch); // --- Sub-extractor entry points --- void cbm_extract_definitions(CBMExtractCtx *ctx); +// Run only the definition walker: no Module node and no variable extraction. +void cbm_extract_definition_nodes(CBMExtractCtx *ctx); void cbm_extract_imports(CBMExtractCtx *ctx); void cbm_extract_usages(CBMExtractCtx *ctx); void cbm_extract_semantic(CBMExtractCtx *ctx); diff --git a/internal/cbm/extract_defs.c b/internal/cbm/extract_defs.c index 2de63ffb..8b725351 100644 --- a/internal/cbm/extract_defs.c +++ b/internal/cbm/extract_defs.c @@ -6396,3 +6396,11 @@ void cbm_extract_definitions(CBMExtractCtx *ctx) { // Extract module-level variables extract_variables(ctx, ctx->root, spec); } + +void cbm_extract_definition_nodes(CBMExtractCtx *ctx) { + const CBMLangSpec *spec = cbm_lang_spec(ctx->language); + if (!spec) { + return; + } + walk_defs(ctx, ctx->root, spec, 0); +} diff --git a/tests/test_extraction.c b/tests/test_extraction.c index f8228e46..90fa420a 100644 --- a/tests/test_extraction.c +++ b/tests/test_extraction.c @@ -1237,6 +1237,52 @@ TEST(cpp_out_of_line_method_issue428) { PASS(); } +/* #946: C++ code commonly gates alternate out-of-line method signatures with + * #ifdef/#else/#endif. Raw tree-sitter can mis-balance the duplicate opening + * braces and lose methods that follow the conditional block; extraction must + * still recover later methods from the translation unit. */ +TEST(cpp_preproc_signature_gap_issue946) { + CBMFileResult *r = + extract("struct Rect {};\n" + "struct IBinder {};\n" + "struct IRegionSamplingListener {};\n" + "typedef int status_t;\n" + "\n" + "class SurfaceFlinger {\n" + "public:\n" + " status_t addRegionSamplingListener(const Rect&, const IBinder&,\n" + " const IRegionSamplingListener&, bool);\n" + " status_t addRegionSamplingListener(const Rect&, const IBinder&,\n" + " const IRegionSamplingListener&);\n" + " void commit();\n" + " void composite();\n" + "};\n" + "\n" + "#ifdef FLYME_GRAPHICS_EXTEND_LUMARGB\n" + "status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea,\n" + " const IBinder& stopLayerHandle,\n" + " const IRegionSamplingListener& listener,\n" + " const bool rgbSample) {\n" + "#else\n" + "status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea,\n" + " const IBinder& stopLayerHandle,\n" + " const IRegionSamplingListener& listener) {\n" + "#endif\n" + " return 0;\n" + "}\n" + "\n" + "void SurfaceFlinger::commit() {}\n" + "\n" + "void SurfaceFlinger::composite() {}\n", + CBM_LANG_CPP, "t", "SurfaceFlinger.cpp"); + ASSERT_NOT_NULL(r); + ASSERT_FALSE(r->has_error); + ASSERT(has_def(r, "Method", "commit")); + ASSERT(has_def(r, "Method", "composite")); + cbm_free_result(r); + PASS(); +} + /* --- COBOL paragraph --- */ TEST(cobol_paragraph) { CBMFileResult *r = @@ -3601,6 +3647,7 @@ SUITE(extraction) { RUN_TEST(zig_struct); RUN_TEST(cpp_function); RUN_TEST(cpp_out_of_line_method_issue428); + RUN_TEST(cpp_preproc_signature_gap_issue946); RUN_TEST(cobol_paragraph); RUN_TEST(verilog_module); RUN_TEST(cuda_kernel);