@@ -3477,6 +3477,159 @@ static TSNode find_function_params(TSNode func_node, CBMLanguage lang) {
34773477 return params ;
34783478}
34793479
3480+ /* ── C-family declared return type ──────────────────────────────────
3481+ * The C-family grammars split a declared return type three ways: the `type`
3482+ * field (`char`), sibling type_qualifier nodes (`const`), and the
3483+ * pointer/reference declarators wrapping the function declarator (`*`). Taking
3484+ * only the `type` field published `const char *get_name(void)` as "char".
3485+ *
3486+ * Canonical spelling: leading cv-qualifiers in source order, the base type text
3487+ * verbatim, then one space and the declarator markers outermost-first with no
3488+ * space between them — `const char *`, `char **`, `Text &`, `Text *&`. A
3489+ * qualifier on a pointer level follows its `*` and is separated from the next
3490+ * marker by a space: `char *const *`. A qualifier written after the base type
3491+ * (`char const *`) is normalized to the leading position. */
3492+
3493+ /* Output sink: measures when buf is NULL, writes otherwise. Rendering twice
3494+ * sizes the arena allocation exactly without a second copy of the logic. */
3495+ typedef struct {
3496+ char * buf ;
3497+ size_t len ;
3498+ } c_rt_out_t ;
3499+
3500+ static void c_rt_put (c_rt_out_t * out , const char * text , size_t n ) {
3501+ if (out -> buf ) {
3502+ memcpy (out -> buf + out -> len , text , n );
3503+ }
3504+ out -> len += n ;
3505+ }
3506+
3507+ static void c_rt_put_str (c_rt_out_t * out , const char * text ) {
3508+ c_rt_put (out , text , strlen (text ));
3509+ }
3510+
3511+ static void c_rt_put_node (c_rt_out_t * out , TSNode node , const char * source ) {
3512+ uint32_t start = ts_node_start_byte (node );
3513+ uint32_t end = ts_node_end_byte (node );
3514+ if (end > start ) {
3515+ c_rt_put (out , source + start , (size_t )(end - start ));
3516+ }
3517+ }
3518+
3519+ /* type_qualifier also covers keywords that are not part of the type
3520+ * (`constexpr`, `_Noreturn`, `mutable`, `__extension__`, …). Keep only the ones
3521+ * that are, so `constexpr int f()` still returns "int". */
3522+ static bool is_c_return_cv_qualifier (TSNode node , const char * source ) {
3523+ static const char * const kept [] = {"const" , "volatile" , "restrict" , "__restrict" ,
3524+ "__restrict__" , "_Atomic" , NULL };
3525+ if (strcmp (ts_node_type (node ), "type_qualifier" ) != 0 ) {
3526+ return false;
3527+ }
3528+ uint32_t start = ts_node_start_byte (node );
3529+ uint32_t end = ts_node_end_byte (node );
3530+ size_t len = end > start ? (size_t )(end - start ) : 0 ;
3531+ for (const char * const * k = kept ; * k ; k ++ ) {
3532+ if (strlen (* k ) == len && memcmp (source + start , * k , len ) == 0 ) {
3533+ return true;
3534+ }
3535+ }
3536+ return false;
3537+ }
3538+
3539+ static bool is_c_declarator_lang (CBMLanguage lang ) {
3540+ return lang == CBM_LANG_C || lang == CBM_LANG_CPP || lang == CBM_LANG_CUDA ||
3541+ lang == CBM_LANG_GLSL || lang == CBM_LANG_HLSL || lang == CBM_LANG_ISPC ||
3542+ lang == CBM_LANG_SLANG || lang == CBM_LANG_OBJC ;
3543+ }
3544+
3545+ /* Render the canonical return type into `out`; returns how many qualifiers and
3546+ * markers were added around the base type (0 = the base text alone is already
3547+ * the whole type). The declarator walk is one strict child chain, so it is
3548+ * O(depth) with no recursion and needs no depth cap. It stops at the first node
3549+ * that is neither a pointer nor a reference declarator: for a function returning
3550+ * a function pointer (`int (*f(void))(int)`) that is the outer
3551+ * function_declarator, which leaves the base type as it was. */
3552+ static size_t c_rt_render (c_rt_out_t * out , TSNode func_node , TSNode type_node , TSNode declarator ,
3553+ const char * source ) {
3554+ size_t added = 0 ;
3555+ uint32_t decl_start = ts_node_start_byte (declarator );
3556+ uint32_t nc = ts_node_named_child_count (func_node );
3557+ for (uint32_t i = 0 ; i < nc ; i ++ ) {
3558+ TSNode ch = ts_node_named_child (func_node , i );
3559+ if (ts_node_start_byte (ch ) >= decl_start ) {
3560+ break ;
3561+ }
3562+ if (is_c_return_cv_qualifier (ch , source )) {
3563+ c_rt_put_node (out , ch , source );
3564+ c_rt_put_str (out , " " );
3565+ added ++ ;
3566+ }
3567+ }
3568+ c_rt_put_node (out , type_node , source );
3569+
3570+ bool need_space = true;
3571+ TSNode decl = declarator ;
3572+ while (!ts_node_is_null (decl )) {
3573+ const char * dk = ts_node_type (decl );
3574+ bool is_ref = strcmp (dk , "reference_declarator" ) == 0 ;
3575+ if (!is_ref && strcmp (dk , "pointer_declarator" ) != 0 ) {
3576+ break ;
3577+ }
3578+ if (need_space ) {
3579+ c_rt_put_str (out , " " );
3580+ need_space = false;
3581+ }
3582+ /* A reference_declarator opens with its `&` / `&&` token. */
3583+ TSNode marker = ts_node_child (decl , 0 );
3584+ if (is_ref && !ts_node_is_null (marker ) && !ts_node_is_named (marker )) {
3585+ c_rt_put_node (out , marker , source );
3586+ } else {
3587+ c_rt_put_str (out , is_ref ? "&" : "*" );
3588+ }
3589+ added ++ ;
3590+ uint32_t dn = ts_node_named_child_count (decl );
3591+ for (uint32_t i = 0 ; i < dn ; i ++ ) {
3592+ TSNode q = ts_node_named_child (decl , i );
3593+ if (is_c_return_cv_qualifier (q , source )) {
3594+ c_rt_put_node (out , q , source );
3595+ need_space = true;
3596+ added ++ ;
3597+ }
3598+ }
3599+ /* tree-sitter-cpp/-cuda give a reference_declarator's inner declarator no
3600+ * `declarator` field (see find_c_params); it is the one named child. */
3601+ TSNode inner = ts_node_child_by_field_name (decl , TS_FIELD ("declarator" ));
3602+ if (ts_node_is_null (inner ) && is_ref && dn > 0 ) {
3603+ inner = ts_node_named_child (decl , 0 );
3604+ }
3605+ decl = inner ;
3606+ }
3607+ return added ;
3608+ }
3609+
3610+ /* Declared return type of a C-family function/method node whose `type` field is
3611+ * `type_node`. Any other language, and any type with nothing around its base
3612+ * type, gets the base type text exactly as before. */
3613+ static char * c_declared_return_type (CBMExtractCtx * ctx , TSNode func_node , TSNode type_node ) {
3614+ CBMArena * a = ctx -> arena ;
3615+ TSNode declarator = ts_node_child_by_field_name (func_node , TS_FIELD ("declarator" ));
3616+ if (!is_c_declarator_lang (ctx -> language ) || ts_node_is_null (declarator )) {
3617+ return cbm_node_text (a , type_node , ctx -> source );
3618+ }
3619+ c_rt_out_t out = {NULL , 0 };
3620+ if (c_rt_render (& out , func_node , type_node , declarator , ctx -> source ) == 0 ) {
3621+ return cbm_node_text (a , type_node , ctx -> source );
3622+ }
3623+ out .buf = (char * )cbm_arena_alloc (a , out .len + NULL_TERM );
3624+ if (!out .buf ) {
3625+ return cbm_node_text (a , type_node , ctx -> source );
3626+ }
3627+ out .len = 0 ;
3628+ (void )c_rt_render (& out , func_node , type_node , declarator , ctx -> source );
3629+ out .buf [out .len ] = '\0' ;
3630+ return out .buf ;
3631+ }
3632+
34803633// C++: resolve trailing return type (auto f() -> Type) on a declarator node.
34813634// Updates def->return_type and def->return_types if trailing type found.
34823635static void resolve_cpp_trailing_return (CBMArena * a , TSNode func_node , const char * source ,
@@ -3712,7 +3865,7 @@ static void extract_func_def(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec
37123865 for (const char * * f = rt_fields ; * f ; f ++ ) {
37133866 TSNode rt = ts_node_child_by_field_name (func_node , * f , (uint32_t )strlen (* f ));
37143867 if (!ts_node_is_null (rt )) {
3715- def .return_type = cbm_node_text ( a , rt , ctx -> source );
3868+ def .return_type = c_declared_return_type ( ctx , func_node , rt );
37163869 def .return_types = extract_return_types (a , rt , ctx -> source , ctx -> language );
37173870 break ;
37183871 }
@@ -4964,7 +5117,7 @@ static void push_method_def(CBMExtractCtx *ctx, TSNode child, TSNode class_node,
49645117 for (const char * * f = rt_fields ; * f ; f ++ ) {
49655118 TSNode rt = ts_node_child_by_field_name (child , * f , (uint32_t )strlen (* f ));
49665119 if (!ts_node_is_null (rt )) {
4967- def .return_type = cbm_node_text ( a , rt , ctx -> source );
5120+ def .return_type = c_declared_return_type ( ctx , child , rt );
49685121 break ;
49695122 }
49705123 }
0 commit comments