Skip to content

Commit f139c40

Browse files
committed
Merge origin/main into fix/manifest-walk-nul-file
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2 parents dd9558e + e0c7a1d commit f139c40

10 files changed

Lines changed: 770 additions & 13 deletions

File tree

‎internal/cbm/extract_calls.c‎

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4358,6 +4358,70 @@ static TSNode language_specific_callee_leaf(CBMLanguage language, TSNode expr,
43584358
return terminal_callee_leaf(ctx, expr);
43594359
}
43604360

4361+
/* `Base.init` for a `super.init(...)` call: Base is the first entry of the
4362+
* enclosing class's inheritance clause, where Swift requires the superclass to
4363+
* stand. The class node is carried by the walk, so no ancestor climb. NULL (no
4364+
* call) outside a class, without a clause, or when the entry does not read as
4365+
* a plain type name -- a lower-case or attributed root would pass the
4366+
* registry's receiver-chain check and bind by short name, which is the false
4367+
* edge this exists to prevent. */
4368+
static const char *swift_superclass_init_callee(CBMExtractCtx *ctx, const WalkState *state) {
4369+
TSNode cls = state ? state->enclosing_class_node : (TSNode){0};
4370+
if (ts_node_is_null(cls) || strcmp(ts_node_type(cls), "class_declaration") != 0) {
4371+
return NULL;
4372+
}
4373+
TSNode spec = cbm_find_child_by_kind(cls, "inheritance_specifier");
4374+
char *base = ts_node_is_null(spec) ? NULL : cbm_node_text(ctx->arena, spec, ctx->source);
4375+
if (!base || base[0] < 'A' || base[0] > 'Z') {
4376+
return NULL;
4377+
}
4378+
size_t len = strcspn(base, "<"); /* Base<T> names Base */
4379+
for (size_t i = 0; i < len; i++) {
4380+
if (!isalnum((unsigned char)base[i]) && base[i] != '_' && base[i] != '.') {
4381+
return NULL;
4382+
}
4383+
}
4384+
base[len] = '\0';
4385+
return cbm_arena_sprintf(ctx->arena, "%s.init", base);
4386+
}
4387+
4388+
/* True for a Swift initializer callee `T.init` (T non-empty). */
4389+
static bool swift_callee_is_initializer(const char *callee) {
4390+
static const char suffix[] = ".init";
4391+
size_t n = callee ? strlen(callee) : 0;
4392+
return n > sizeof(suffix) - 1 && strcmp(callee + n - (sizeof(suffix) - 1), suffix) == 0;
4393+
}
4394+
4395+
/* Swift initializer delegation (#2379). `super.init(...)` and `self.init(...)`
4396+
* name no type, and a lower-case receiver passes the registry's receiver-chain
4397+
* check, so a short-name match binds them to whichever project `init` is
4398+
* nearest: a nested type's, a sibling type's, or the caller's own. On
4399+
* Alamofire all 8 such edges were false.
4400+
*
4401+
* `super.init` is rewritten to name the superclass (`Base.init`), so the
4402+
* qualified strategies place it on the project's Base.init. When Base is not
4403+
* in the project (UIViewController, NSObject) nothing places it, and the
4404+
* initializer guard in handle_calls keeps a short-name guess from doing so.
4405+
*
4406+
* `self.init` delegates to another initializer of the caller's own type.
4407+
* Initializer overloads share one node (#2061), so the only edge it could form
4408+
* is a self-loop, and that one is false whenever the overload it calls lives
4409+
* outside the project (`extension Notification { init(...) { self.init(...) } }`
4410+
* calls Foundation's). It forms no CALLS edge. */
4411+
static const char *resolve_swift_delegating_init(CBMExtractCtx *ctx, const WalkState *state,
4412+
const char *callee) {
4413+
if (ctx->language != CBM_LANG_SWIFT || !callee) {
4414+
return callee;
4415+
}
4416+
if (strcmp(callee, "self.init") == 0) {
4417+
return NULL;
4418+
}
4419+
if (strcmp(callee, "super.init") == 0) {
4420+
return swift_superclass_init_callee(ctx, state);
4421+
}
4422+
return callee;
4423+
}
4424+
43614425
// Produce the textual callee and the AST occurrence from one operation. The
43624426
// generic fallback retains the former field/terminal behavior, while grammars
43634427
// whose extractor uses a sibling, a special field, or a synthetic literal are
@@ -4368,6 +4432,7 @@ static CBMPrimaryCalleeSelection select_primary_callee(CBMExtractCtx *ctx, TSNod
43684432
CBMPrimaryCalleeSelection selection = {0};
43694433
selection.name = extract_callee_name(ctx->arena, node, ctx->source, ctx->language);
43704434
selection.name = resolve_objectscript_callee(ctx, node, state, selection.name);
4435+
selection.name = resolve_swift_delegating_init(ctx, state, selection.name);
43714436
if (!selection.name || !selection.name[0]) {
43724437
return selection;
43734438
}
@@ -4591,6 +4656,16 @@ CBMInvocationDescriptor handle_calls(CBMExtractCtx *ctx, TSNode node, const CBML
45914656
call.is_method = true;
45924657
}
45934658
}
4659+
// Swift initializer guard (#2379; same direction). Flag `T.init(...)`,
4660+
// as written or as a rewritten super.init. Nearly every type has an
4661+
// `init`, so a short-name match onto one is a guess: with a project
4662+
// module imported, `UIViewController.init` bound an unrelated
4663+
// project initializer. The registry's qualified / same-module /
4664+
// import strategies still place T.init when T is a project type.
4665+
// No other Swift call carries the flag.
4666+
if (ctx->language == CBM_LANG_SWIFT && swift_callee_is_initializer(call.callee_name)) {
4667+
call.is_method = true;
4668+
}
45944669

45954670
TSNode args = ts_node_child_by_field_name(node, TS_FIELD("arguments"));
45964671
/* tree-sitter-elixir attaches NO field name to a call's arguments

‎internal/cbm/extract_defs.c‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,15 @@ TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang) {
800800
return cbm_find_child_by_kind(node, "query_name");
801801
}
802802

803+
/* Swift init/deinit (#2379) have no name node at all. The declaration
804+
* itself stands in for it, and cbm_func_name_node_text maps that
805+
* node kind to "init" / "deinit". Gated on Swift: Slang's grammar
806+
* also has an init_declaration node. */
807+
if (lang == CBM_LANG_SWIFT &&
808+
(strcmp(kind, "init_declaration") == 0 || strcmp(kind, "deinit_declaration") == 0)) {
809+
return node;
810+
}
811+
803812
TSNode name = func_name_node(node);
804813

805814
if (lang == CBM_LANG_R && strcmp(kind, "function_definition") == 0) {
@@ -9205,6 +9214,12 @@ static TSNode resolve_method_name(TSNode child, CBMLanguage lang) {
92059214
return cbm_find_child_by_kind(child, "simple_identifier");
92069215
}
92079216

9217+
// Swift init/deinit: named in one place, cbm_resolve_func_name (#2379).
9218+
if (lang == CBM_LANG_SWIFT &&
9219+
(strcmp(ck, "init_declaration") == 0 || strcmp(ck, "deinit_declaration") == 0)) {
9220+
return cbm_resolve_func_name(child, lang);
9221+
}
9222+
92089223
// Squirrel: function_declaration's name is a plain `identifier` child.
92099224
if (lang == CBM_LANG_SQUIRREL && strcmp(ck, "function_declaration") == 0) {
92109225
return cbm_find_child_by_kind(child, "identifier");

‎internal/cbm/extract_unified.c‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ static bool push_scope(WalkState *state, uint8_t kind, uint32_t depth, const cha
324324
* CBMWalkScope comment for the quadratic recompute this replaces. */
325325
f->prev_enclosing_func_qn = state->enclosing_func_qn;
326326
f->prev_enclosing_class_qn = state->enclosing_class_qn;
327+
f->prev_enclosing_class_node = state->enclosing_class_node;
327328
f->prev_invocation_kind = state->invocation_kind;
328329
f->prev_callee_expr = state->callee_expr;
329330
f->prev_callee_leaf = state->callee_leaf;
@@ -337,6 +338,8 @@ static bool push_scope(WalkState *state, uint8_t kind, uint32_t depth, const cha
337338
case SCOPE_CLASS:
338339
case SCOPE_NAMESPACE:
339340
state->enclosing_class_qn = qn;
341+
/* push_lexical_scope records the node; a node-less push has none. */
342+
state->enclosing_class_node = (TSNode){0};
340343
break;
341344
case SCOPE_CALL:
342345
/* The invocation triple is applied by push_call_scope once the caller
@@ -364,6 +367,9 @@ static bool push_lexical_scope(WalkState *state, uint8_t walk_kind, uint32_t dep
364367
state->lexical_binding_tracking_failed = true;
365368
return false;
366369
}
370+
if (walk_kind == SCOPE_CLASS || walk_kind == SCOPE_NAMESPACE) {
371+
state->enclosing_class_node = node;
372+
}
367373
state->scopes[state->scope_top - SKIP_ONE].lexical_scope_id =
368374
add_lexical_scope(state, node, lexical_kind);
369375
return true;
@@ -442,6 +448,7 @@ static void pop_expired_scopes(WalkState *state, uint32_t cur_depth) {
442448
const CBMWalkScope *f = &state->scopes[--state->scope_top];
443449
state->enclosing_func_qn = f->prev_enclosing_func_qn;
444450
state->enclosing_class_qn = f->prev_enclosing_class_qn;
451+
state->enclosing_class_node = f->prev_enclosing_class_node;
445452
state->invocation_kind = f->prev_invocation_kind;
446453
state->callee_expr = f->prev_callee_expr;
447454
state->callee_leaf = f->prev_callee_leaf;
@@ -2823,6 +2830,7 @@ void cbm_extract_unified(CBMExtractCtx *ctx) {
28232830
* recompute_state call): module scope, nothing else active. */
28242831
state.enclosing_func_qn = ctx->module_qn;
28252832
state.enclosing_class_qn = NULL;
2833+
state.enclosing_class_node = (TSNode){0};
28262834
state.invocation_kind = CBM_INVOCATION_NONE;
28272835
state.callee_expr = (TSNode){0};
28282836
state.callee_leaf = (TSNode){0};

‎internal/cbm/extract_unified.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ typedef struct {
7878
* suite-budget kills on every non-M4 venue). */
7979
const char *prev_enclosing_func_qn;
8080
const char *prev_enclosing_class_qn;
81+
TSNode prev_enclosing_class_node;
8182
CBMInvocationKind prev_invocation_kind;
8283
TSNode prev_callee_expr;
8384
TSNode prev_callee_leaf;
@@ -133,6 +134,7 @@ typedef struct {
133134
typedef struct {
134135
const char *enclosing_func_qn; // current function QN (module_qn at top level)
135136
const char *enclosing_class_qn; // current class QN (NULL outside class)
137+
TSNode enclosing_class_node; // that class scope's node (null when not known)
136138
const TSTreeCursor *current_cursor; // unified walk cursor at the current node
137139
TSTreeCursor *occurrence_cursor; // reusable parent-preserving classifier cursor
138140
CBMInvocationKind invocation_kind; // exact active invocation/reference role

‎internal/cbm/helpers.c‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ bool cbm_is_keyword(const char *name, CBMLanguage lang) {
301301
case CBM_LANG_PUPPET:
302302
keywords = puppet_keywords;
303303
break;
304+
case CBM_LANG_SWIFT:
305+
/* `init` and `deinit` are Swift keywords. A reference to one
306+
* (`.init(...)`, `Foo.init`) names an initializer of a type the bare
307+
* name does not carry, so resolving it by name can only guess among
308+
* every type's initializer (#2379). Otherwise the generic set. */
309+
if (strcmp(name, "init") == 0 || strcmp(name, "deinit") == 0) {
310+
return true;
311+
}
312+
keywords = generic_keywords;
313+
break;
304314
default:
305315
keywords = generic_keywords;
306316
break;
@@ -1187,6 +1197,16 @@ bool cbm_is_c_preprocessor_lang(CBMLanguage lang) {
11871197
// const", and a member lookup for "operator bool" (the implicit call in
11881198
// `if (obj)`) misses.
11891199
char *cbm_func_name_node_text(CBMArena *a, TSNode name_node, const char *source, CBMLanguage lang) {
1200+
if (lang == CBM_LANG_SWIFT) {
1201+
const char *kind = ts_node_type(name_node);
1202+
if (strcmp(kind, "init_declaration") == 0) {
1203+
return cbm_arena_strdup(a, "init");
1204+
}
1205+
if (strcmp(kind, "deinit_declaration") == 0) {
1206+
return cbm_arena_strdup(a, "deinit");
1207+
}
1208+
}
1209+
11901210
char *text = cbm_node_text(a, name_node, source);
11911211
/* A C keyword is never a function name: the definition is an error-recovery
11921212
* artifact. No name means no def and no call scope (calls inside fall back

‎internal/cbm/lang_specs.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,8 @@ static const char *objc_assign_types[] = {"assignment_expression", NULL};
571571
// well as in the name resolvers — extract_class_methods gates on this set, so
572572
// without the entry the requirement is walked and then discarded.
573573
static const char *swift_func_types[] = {"function_declaration", "protocol_function_declaration",
574-
"macro_declaration", NULL};
574+
"macro_declaration", "init_declaration",
575+
"deinit_declaration", NULL};
575576
// KNOWN GAP: struct_declaration and enum_declaration are not node types in the
576577
// vendored tree-sitter-swift grammar — it models both as class_declaration — so
577578
// these two entries never match anything, and a bare Swift `enum` is labeled

‎src/pipeline/pass_calls.c‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1274,7 +1274,11 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call, const CBM
12741274
* to a docs bundle (2026-09-16 probe: 4,207 junk
12751275
* edges on JetBrains/Exposed). */
12761276
lang == CBM_LANG_HTML || lang == CBM_LANG_VUE ||
1277-
lang == CBM_LANG_SVELTE || lang == CBM_LANG_ASTRO;
1277+
lang == CBM_LANG_SVELTE || lang == CBM_LANG_ASTRO ||
1278+
/* Swift flags only initializer calls `T.init(...)`
1279+
* (#2379, extract_calls.c); every other Swift call
1280+
* keeps is_method=false and is unaffected. */
1281+
lang == CBM_LANG_SWIFT;
12781282
/* Bare-call local-binding suppression. A member call has a receiver the
12791283
* guard above can reason about; a bare `run()` has none, so that guard
12801284
* cannot see this class at all. Python-only today because the extraction

‎src/pipeline/pass_parallel.c‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3510,7 +3510,9 @@ static void resolve_file_calls(resolve_ctx_t *rc, resolve_worker_state_t *ws, CB
35103510
lang == CBM_LANG_ARKTS ||
35113511
/* embedded-script hosts — see pass_calls.c */
35123512
lang == CBM_LANG_HTML || lang == CBM_LANG_VUE ||
3513-
lang == CBM_LANG_SVELTE || lang == CBM_LANG_ASTRO;
3513+
lang == CBM_LANG_SVELTE || lang == CBM_LANG_ASTRO ||
3514+
/* Swift initializer calls only — see pass_calls.c */
3515+
lang == CBM_LANG_SWIFT;
35143516
/* Bare-call local-binding suppression — see the note in pass_calls.c.
35153517
* This gate MUST stay identical to the one there. */
35163518
bool suppress_weak_local_binding = lang == CBM_LANG_PYTHON;

0 commit comments

Comments
 (0)