From 5f9ac15818e3bbda9e031db54867dfab2b6c531e Mon Sep 17 00:00:00 2001 From: lorenzozanee Date: Tue, 8 Sep 2026 12:39:54 +0800 Subject: [PATCH 1/2] fix(csharp): index C# 14 extension declaration members Signed-off-by: lorenzozanee --- internal/cbm/extract_defs.c | 30 ++++++++++++++++++++++++++++++ tests/test_grammar_labels.c | 1 + tests/test_grammar_regression.c | 14 ++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/internal/cbm/extract_defs.c b/internal/cbm/extract_defs.c index b49147dc6..a9c7fe613 100644 --- a/internal/cbm/extract_defs.c +++ b/internal/cbm/extract_defs.c @@ -5014,6 +5014,28 @@ static void push_method_def(CBMExtractCtx *ctx, TSNode child, TSNode class_node, cbm_defs_push(&ctx->result->defs, a, def); } +/* Older C# grammars parse an extension declaration as a constructor whose body + * contains local functions. Recover those members without descending into + * ordinary C# method bodies. */ +static void extract_csharp_extension_members(CBMExtractCtx *ctx, TSNode node, TSNode class_node, + const char *class_qn, const CBMLangSpec *spec) { + const char *kind = ts_node_type(node); + if (strcmp(kind, "local_function_statement") == 0 || + strcmp(kind, "method_declaration") == 0) { + TSNode name_node = resolve_method_name(node, ctx->language); + if (!ts_node_is_null(name_node)) { + push_method_def(ctx, node, class_node, class_qn, spec, name_node); + } + return; + } + + uint32_t count = ts_node_named_child_count(node); + for (uint32_t i = 0; i < count; i++) { + extract_csharp_extension_members(ctx, ts_node_named_child(node, i), class_node, class_qn, + spec); + } +} + // Extract methods from an ObjC implementation_definition node. static void extract_objc_impl_methods(CBMExtractCtx *ctx, TSNode impl_node, const char *class_qn, const CBMLangSpec *spec) { @@ -5132,6 +5154,14 @@ static void extract_class_methods(CBMExtractCtx *ctx, TSNode class_node, const c continue; } + if (ctx->language == CBM_LANG_CSHARP && + strcmp(ts_node_type(method_node), "constructor_declaration") == 0) { + char *name = cbm_func_name_node_text(ctx->arena, name_node, ctx->source, ctx->language); + if (name && strcmp(name, "extension") == 0) { + extract_csharp_extension_members(ctx, method_node, class_node, class_qn, spec); + continue; + } + } push_method_def(ctx, method_node, class_node, class_qn, spec, name_node); } } diff --git a/tests/test_grammar_labels.c b/tests/test_grammar_labels.c index 7818c4c44..f2fff5eb0 100644 --- a/tests/test_grammar_labels.c +++ b/tests/test_grammar_labels.c @@ -93,6 +93,7 @@ static const LabelGolden LABEL_GOLDENS[] = { {"ruby", "Class:1,Function:1,Module:1"}, {"php", "Class:1,Function:1,Module:1"}, {"c_sharp", "Class:1,Method:1,Module:1"}, + {"csharp_extension_declaration", "Class:1,Method:1,Module:1"}, {"bash", "Function:2,Module:1"}, {"zsh", "Function:2,Module:1"}, {"lua", "Function:2,Module:1"}, diff --git a/tests/test_grammar_regression.c b/tests/test_grammar_regression.c index 219c68edb..a8a786a0a 100644 --- a/tests/test_grammar_regression.c +++ b/tests/test_grammar_regression.c @@ -81,6 +81,20 @@ const GrammarCase CBM_GRAMMAR_CASES[] = { "class A {\n void Foo() {}\n}\n", 2, {"A", "Foo", NULL}}, + {"csharp_extension_declaration", + CBM_LANG_CSHARP, + "Extensions.cs", + "using System.Collections.Generic;\n" + "using System.Linq;\n" + "public static class NumberExtensions\n" + "{\n" + " extension(IEnumerable numbers)\n" + " {\n" + " public int SumPositive() => numbers.Where(n => n > 0).Aggregate(0, static (total, n) => total + n);\n" + " }\n" + "}\n", + 2, + {"NumberExtensions", "SumPositive", NULL}}, {"bash", CBM_LANG_BASH, "a.sh", From f6c310c08e6dad38b92e953c85c7c5876f6df839 Mon Sep 17 00:00:00 2001 From: lorenzozanee Date: Tue, 8 Sep 2026 13:58:52 +0800 Subject: [PATCH 2/2] fix(csharp): index C# 14 extension declaration members Signed-off-by: lorenzozanee --- internal/cbm/extract_defs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/cbm/extract_defs.c b/internal/cbm/extract_defs.c index a9c7fe613..d6de69ac0 100644 --- a/internal/cbm/extract_defs.c +++ b/internal/cbm/extract_defs.c @@ -5020,8 +5020,7 @@ static void push_method_def(CBMExtractCtx *ctx, TSNode child, TSNode class_node, static void extract_csharp_extension_members(CBMExtractCtx *ctx, TSNode node, TSNode class_node, const char *class_qn, const CBMLangSpec *spec) { const char *kind = ts_node_type(node); - if (strcmp(kind, "local_function_statement") == 0 || - strcmp(kind, "method_declaration") == 0) { + if (strcmp(kind, "local_function_statement") == 0 || strcmp(kind, "method_declaration") == 0) { TSNode name_node = resolve_method_name(node, ctx->language); if (!ts_node_is_null(name_node)) { push_method_def(ctx, node, class_node, class_qn, spec, name_node);