@@ -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
0 commit comments