@@ -866,11 +866,53 @@ const CBMType* go_eval_builtin_call(GoLSPContext* ctx, const char* name, TSNode
866866
867867// --- go_lookup_field: struct field lookup with embedding recursion ---
868868
869+ // --- Import-alias re-qualification ------------------------------------
870+ //
871+ // parse_field_defs_into_type qualifies struct field type texts as
872+ // "<def_module>.<text>". When the author wrote the text through an import
873+ // alias ("Svc:svc.Svc" in module test.main), that yields a QN
874+ // ("test.main.svc.Svc") that exists nowhere in the project-wide registry —
875+ // the real QN is "<import_qn>.Svc" and only the calling file's import map
876+ // can say so. On an exact-QN miss this rewrites the alias segment through
877+ // the file's imports; returns NULL when no alias segment is involved.
878+
879+ static const char * go_requalify_via_imports (GoLSPContext * ctx , const char * type_qn ) {
880+ if (!ctx || !type_qn || !type_qn [0 ] || ctx -> import_count <= 0 ) return NULL ;
881+ for (int j = 0 ; j < ctx -> import_count ; j ++ ) {
882+ const char * alias = ctx -> import_local_names [j ];
883+ const char * alias_qn = ctx -> import_package_qns [j ];
884+ if (!alias || !alias [0 ] || !alias_qn || strchr (alias , '.' )) continue ;
885+ size_t alias_len = strlen (alias );
886+ /* Last occurrence of a "<dot>alias<dot>" segment in type_qn. */
887+ const char * hit = NULL ;
888+ for (const char * p = type_qn ;;) {
889+ p = strstr (p , "." );
890+ if (!p ) break ;
891+ p ++ ;
892+ if (strncmp (p , alias , alias_len ) == 0 && p [alias_len ] == '.' ) {
893+ hit = p ;
894+ p += alias_len ;
895+ }
896+ }
897+ if (hit ) {
898+ const char * rest = hit + alias_len + 1 ; /* past "<alias>." */
899+ return cbm_arena_sprintf (ctx -> arena , "%s.%s" , alias_qn , rest );
900+ }
901+ }
902+ return NULL ;
903+ }
904+
869905static const CBMType * go_lookup_field (GoLSPContext * ctx ,
870906 const char * type_qn , const char * field_name , int depth ) {
871907 if (!type_qn || !field_name || depth > 5 ) return NULL ;
872908
873909 const CBMRegisteredType * rt = cbm_registry_lookup_type (ctx -> registry , type_qn );
910+ if (!rt && depth == 0 ) {
911+ /* Import-alias re-qualification: field texts from cross-package defs
912+ * may embed an alias segment only this file's import map resolves. */
913+ const char * alt_qn = go_requalify_via_imports (ctx , type_qn );
914+ if (alt_qn ) rt = cbm_registry_lookup_type (ctx -> registry , alt_qn );
915+ }
874916 if (!rt ) return NULL ;
875917
876918 // Follow alias chain
@@ -907,6 +949,18 @@ static const CBMRegisteredFunc* go_lookup_field_or_method_depth(GoLSPContext* ct
907949 const CBMRegisteredFunc * f = cbm_registry_lookup_method (ctx -> registry , type_qn , member_name );
908950 if (f ) return f ;
909951
952+ /* Import-alias re-qualification fallback: NAMED receivers built from
953+ * cross-package field type texts can carry a "<module>.svc.Svc" QN;
954+ * retry the method set on the import-resolved QN (see
955+ * go_requalify_via_imports). */
956+ if (depth == 0 ) {
957+ const char * alt_qn = go_requalify_via_imports (ctx , type_qn );
958+ if (alt_qn ) {
959+ f = go_lookup_field_or_method_depth (ctx , alt_qn , member_name , depth + 1 );
960+ if (f ) return f ;
961+ }
962+ }
963+
910964 const CBMRegisteredType * rt = cbm_registry_lookup_type (ctx -> registry , type_qn );
911965 if (rt ) {
912966 // Follow type alias chain
@@ -1430,20 +1484,32 @@ static void resolve_calls_in_node_inner(GoLSPContext* ctx, TSNode node) {
14301484 if (base && base -> kind == CBM_TYPE_POINTER ) base = cbm_type_deref (base );
14311485
14321486 if (base && base -> kind == CBM_TYPE_NAMED ) {
1487+ const char * recv_qn = base -> data .named .qualified_name ;
14331488 const CBMRegisteredType * receiver_type = cbm_registry_lookup_type (
1434- ctx -> registry , base -> data .named .qualified_name );
1489+ ctx -> registry , recv_qn );
1490+ const char * alt_qn = NULL ;
1491+ /* Re-qualify NAMED receivers that embed an import
1492+ * alias segment (cross-package field type texts) —
1493+ * the real type only exists under the import QN. */
1494+ if (!receiver_type ) {
1495+ alt_qn = go_requalify_via_imports (ctx , recv_qn );
1496+ if (alt_qn ) {
1497+ receiver_type = cbm_registry_lookup_type (ctx -> registry , alt_qn );
1498+ if (receiver_type ) recv_qn = alt_qn ;
1499+ }
1500+ }
14351501 /* Registered interface receivers must reach the
14361502 * interface-resolution branch below. Their semantic
14371503 * method registrations are signatures, not concrete
14381504 * dispatch targets. */
14391505 if (!receiver_type || !receiver_type -> is_interface ) {
14401506 const CBMRegisteredFunc * method = go_lookup_field_or_method (
1441- ctx , base -> data . named . qualified_name , field_name );
1507+ ctx , recv_qn , field_name );
14421508 if (method ) {
14431509 const char * strategy = "lsp_type_dispatch" ;
14441510 if (method -> receiver_type &&
14451511 strcmp (method -> receiver_type ,
1446- base -> data . named . qualified_name ) != 0 ) {
1512+ recv_qn ) != 0 ) {
14471513 strategy = "lsp_embed_dispatch" ;
14481514 }
14491515 emit_resolved_call (ctx , method -> qualified_name , strategy , 0.95f ,
@@ -1460,9 +1526,15 @@ static void resolve_calls_in_node_inner(GoLSPContext* ctx, TSNode node) {
14601526 if (!is_iface && base -> kind == CBM_TYPE_NAMED ) {
14611527 const CBMRegisteredType * rt = cbm_registry_lookup_type (ctx -> registry ,
14621528 base -> data .named .qualified_name );
1529+ if (!rt ) {
1530+ const char * alt_qn = go_requalify_via_imports (
1531+ ctx , base -> data .named .qualified_name );
1532+ if (alt_qn )
1533+ rt = cbm_registry_lookup_type (ctx -> registry , alt_qn );
1534+ }
14631535 if (rt && rt -> is_interface ) {
14641536 is_iface = true;
1465- iface_qn = base -> data . named . qualified_name ;
1537+ iface_qn = rt -> qualified_name ;
14661538 }
14671539 }
14681540 if (is_iface ) {
@@ -1787,42 +1859,15 @@ static void process_function(GoLSPContext* ctx, TSNode func_node) {
17871859 char * func_name = lsp_node_text (ctx , name_node );
17881860 if (!func_name || !func_name [0 ]) return ;
17891861
1790- // For methods, the enclosing-function QN must include the receiver type
1791- // (package.Type.Method), matching how the textual extractor and the
1792- // registry qualify the method. Building it as package.Method (no receiver)
1793- // here made the LSP-resolved call's caller_qn disagree with the textual
1794- // call's enclosing_func_qn, so cbm_pipeline_find_lsp_resolution never
1795- // joined them — every call inside a method body silently lost its
1796- // type-aware LSP strategy. Derive the bare receiver type name the same way
1797- // the receiver binding below does.
1798- char * recv_type_name = NULL ;
1799- {
1800- TSNode recv0 = ts_node_child_by_field_name (func_node , "receiver" , 8 );
1801- if (!ts_node_is_null (recv0 )) {
1802- uint32_t rnc0 = ts_node_child_count (recv0 );
1803- for (uint32_t i = 0 ; i < rnc0 && !recv_type_name ; i ++ ) {
1804- TSNode rp = ts_node_child (recv0 , i );
1805- if (ts_node_is_null (rp ) || !ts_node_is_named (rp )) continue ;
1806- if (strcmp (ts_node_type (rp ), "parameter_declaration" ) != 0 ) continue ;
1807- TSNode rtype = ts_node_child_by_field_name (rp , "type" , 4 );
1808- if (ts_node_is_null (rtype )) continue ;
1809- // Unwrap a pointer receiver (*Type) to the bare type identifier.
1810- const char * rtk = ts_node_type (rtype );
1811- if (strcmp (rtk , "pointer_type" ) == 0 && ts_node_named_child_count (rtype ) > 0 ) {
1812- rtype = ts_node_named_child (rtype , 0 );
1813- }
1814- char * tn = lsp_node_text (ctx , rtype );
1815- if (tn && tn [0 ]) recv_type_name = tn ;
1816- }
1817- }
1818- }
1819-
1820- if (recv_type_name ) {
1821- ctx -> enclosing_func_qn =
1822- cbm_arena_sprintf (ctx -> arena , "%s.%s.%s" , ctx -> package_qn , recv_type_name , func_name );
1823- } else {
1824- ctx -> enclosing_func_qn = cbm_arena_sprintf (ctx -> arena , "%s.%s" , ctx -> package_qn , func_name );
1825- }
1862+ // Enclosing-function QN must be the BARE package.Func form (no receiver
1863+ // type segment). The textual call events (extract_unified.c) source calls
1864+ // as package_qn.func_name — methods included — and the defs pass creates
1865+ // the graph Method node under the same QN, so any other form breaks the
1866+ // caller-QN join in cbm_pipeline_find_lsp_resolution and the LSP-resolved
1867+ // call silently falls back to the registry short-name resolver. The
1868+ // receiver type still reaches the registry via the def's parent_class /
1869+ // method->receiver_type; it just does not appear in the caller QN.
1870+ ctx -> enclosing_func_qn = cbm_arena_sprintf (ctx -> arena , "%s.%s" , ctx -> package_qn , func_name );
18261871
18271872 // Push function scope
18281873 CBMScope * saved_scope = ctx -> current_scope ;
0 commit comments