2424 SIG_OPERATOR_LEN = 8 , /* strlen("operator") */
2525 SIG_CONST_LEN = 5 , /* strlen("const") */
2626 SIG_ASYNC_LEN = 5 , /* strlen("async") */
27+ SIG_ARROW_LEN = 2 , /* strlen("=>") */
2728 SIG_VOLATILE_LEN = 8 , /* strlen("volatile") */
2829 SIG_ARITY_DIGITS = 16 , /* "(%d)" scratch */
2930 SIG_CAPPED_TAIL = 1 + SIG_HASH_HEX + 1 , /* "#" hex ")" */
@@ -933,10 +934,11 @@ static void sig_generic_params(sig_ctx_t *c, TSNode node) {
933934
934935/* ── Spelling pass ─────────────────────────────────────────────── */
935936
936- /* Qualified type paths keep their last segment; "->" becomes "=>"; any other
937+ /* Swift qualified paths use '/'; other type paths keep their last segment.
938+ * "->" becomes "=>"; any other
937939 * '.' or "::" (a receiver function type, a leading global scope) is dropped,
938940 * so the suffix contract holds whatever the grammar produced. */
939- static size_t sig_spell (char * s , size_t n ) {
941+ static size_t sig_spell (char * s , size_t n , CBMLanguage lang ) {
940942 size_t out = 0 ;
941943 size_t i = 0 ;
942944 while (i < n ) {
@@ -947,6 +949,11 @@ static size_t sig_spell(char *s, size_t n) {
947949 sep = 2 ;
948950 }
949951 if (sep ) {
952+ if (lang == CBM_LANG_SWIFT ) {
953+ s [out ++ ] = '/' ;
954+ i += sep ;
955+ continue ;
956+ }
950957 bool ident_before = out > 0 && sig_ident_char ((unsigned char )s [out - 1 ]);
951958 bool ident_after = i + sep < n && sig_ident_char ((unsigned char )s [i + sep ]);
952959 if (ident_before && ident_after ) {
@@ -1079,6 +1086,11 @@ static void sig_render(sig_ctx_t *c, TSNode node) {
10791086 break ;
10801087 }
10811088 }
1089+ TSNode result = sig_field (node , "return_type" );
1090+ if (!ts_node_is_null (result ) && sig_writes_types (c )) {
1091+ sig_raw_str (c , "=>" );
1092+ sig_type_tokens (c , result );
1093+ }
10821094 }
10831095}
10841096
@@ -1129,7 +1141,7 @@ const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *sou
11291141 if (!spelled ) {
11301142 return NULL ;
11311143 }
1132- size_t n = sig_spell (spelled , full );
1144+ size_t n = sig_spell (spelled , full , lang );
11331145 if (n <= CBM_CALLABLE_SIG_MAX ) {
11341146 return sig_roundtrips (spelled , n ) ? spelled : sig_hashed (a , spelled , n );
11351147 }
@@ -1140,12 +1152,12 @@ const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *sou
11401152 int limit = c .too_many ? SIG_MAX_ENTRIES : c .entries ;
11411153 for (int i = 0 ; i < limit ; i ++ ) {
11421154 char * prefix = cbm_arena_strndup (a , c .buf , c .entry_end [i ]);
1143- c .entry_end [i ] = prefix ? sig_spell (prefix , c .entry_end [i ]) : 0 ;
1155+ c .entry_end [i ] = prefix ? sig_spell (prefix , c .entry_end [i ], lang ) : 0 ;
11441156 }
11451157 char * open_prefix = cbm_arena_strndup (a , c .buf , c .open_off );
11461158 char * close_prefix = cbm_arena_strndup (a , c .buf , c .close_off );
1147- c .open_off = open_prefix ? sig_spell (open_prefix , c .open_off ) : 0 ;
1148- c .close_off = close_prefix ? sig_spell (close_prefix , c .close_off ) : 0 ;
1159+ c .open_off = open_prefix ? sig_spell (open_prefix , c .open_off , lang ) : 0 ;
1160+ c .close_off = close_prefix ? sig_spell (close_prefix , c .close_off , lang ) : 0 ;
11491161 c .buf = spelled ;
11501162 c .len = n ;
11511163 const char * capped = sig_capped (a , & c , spelled , n );
@@ -1213,13 +1225,70 @@ size_t cbm_qn_callable_base_len_named(const char *qn, const char *name) {
12131225 return anchored ? base : len ;
12141226}
12151227
1228+ /* Locate only a declaration's return marker, never a nested closure arrow or
1229+ * an arrow in generic requirements. Shared by inverse and Swift registry. */
1230+ static bool sig_return_type_valid (const char * type ) {
1231+ char stack [SIG_DEPTH_LIMIT ];
1232+ unsigned depth = 0 ;
1233+ if (!type [0 ] || strpbrk (type , ".#;{}" ) || strstr (type , "::" ) || strstr (type , "->" )) {
1234+ return false;
1235+ }
1236+ for (size_t i = 0 ; type [i ]; i ++ ) {
1237+ char ch = type [i ];
1238+ if (strchr ("([<" , ch )) {
1239+ if (depth == SIG_DEPTH_LIMIT ) {
1240+ return false;
1241+ }
1242+ stack [depth ++ ] = ch ;
1243+ } else if (strchr (")]>" , ch ) && !(ch == '>' && i > 0 && type [i - 1 ] == '=' )) {
1244+ char open = ch == ')' ? '(' : (ch == ']' ? '[' : '<' );
1245+ if (!depth || stack [-- depth ] != open ) {
1246+ return false;
1247+ }
1248+ }
1249+ }
1250+ return depth == 0 ;
1251+ }
1252+
1253+ size_t cbm_callable_return_offset (const char * suffix ) {
1254+ size_t len = strlen (suffix );
1255+ if (!strstr (suffix , "=>" )) {
1256+ return len ;
1257+ }
1258+ int parens = 0 , brackets = 0 , angles = 0 ;
1259+ for (size_t i = 0 ; i + 1 < len ; i ++ ) {
1260+ char ch = suffix [i ];
1261+ bool arrow = ch == '=' && suffix [i + 1 ] == '>' ;
1262+ bool after_params = i && (suffix [i - 1 ] == ')' ||
1263+ (i >= SIG_ASYNC_LEN &&
1264+ memcmp (suffix + i - SIG_ASYNC_LEN , "async" , SIG_ASYNC_LEN ) == 0 ));
1265+ if (arrow && after_params && parens == 0 && brackets == 0 && angles == 0 ) {
1266+ return sig_return_type_valid (suffix + i + SIG_ARROW_LEN ) ? i : 0 ;
1267+ }
1268+ if (ch == '(' ) {
1269+ parens ++ ;
1270+ } else if (ch == ')' ) {
1271+ parens -- ;
1272+ } else if (ch == '[' ) {
1273+ brackets ++ ;
1274+ } else if (ch == ']' ) {
1275+ brackets -- ;
1276+ } else if (ch == '<' && (angles > 0 || sig_ident_char ((unsigned char )suffix [i + 1 ]))) {
1277+ angles ++ ;
1278+ } else if (ch == '>' && angles > 0 && (i == 0 || suffix [i - 1 ] != '=' )) {
1279+ angles -- ;
1280+ }
1281+ }
1282+ return len ;
1283+ }
1284+
12161285size_t cbm_qn_callable_base_len (const char * qn ) {
12171286 if (!qn ) {
12181287 return 0 ;
12191288 }
12201289 size_t len = strlen (qn );
12211290 /* Trailing cvref (C++): const / volatile / & / && after the ')'. */
1222- size_t t = len ;
1291+ size_t t = cbm_callable_return_offset ( qn ) ;
12231292 if (t >= SIG_ASYNC_LEN && memcmp (qn + t - SIG_ASYNC_LEN , "async" , SIG_ASYNC_LEN ) == 0 ) {
12241293 t -= SIG_ASYNC_LEN ;
12251294 }
0 commit comments