@@ -673,20 +673,48 @@ static cbm_resolution_t resolve_import_map(const cbm_registry_t *r, const char *
673673 return empty_result ();
674674}
675675
676+ static bool is_same_module_receiver (const char * prefix , const char * module_qn ) {
677+ if (!prefix || !prefix [0 ]) {
678+ return true; /* Bare names are always candidates for same-module resolution */
679+ }
680+ /* 1. Check known self-receivers */
681+ static const char * const self_receivers [] = {"self" , "this" , "cls" , "@self" , NULL };
682+ for (int i = 0 ; self_receivers [i ]; i ++ ) {
683+ if (strcmp (prefix , self_receivers [i ]) == 0 ) {
684+ return true;
685+ }
686+ }
687+ /* 2. Check if prefix matches the module name or its last segment (namespace qualified) */
688+ size_t plen = strlen (prefix );
689+ size_t mlen = strlen (module_qn );
690+ if (mlen == plen && strcmp (module_qn , prefix ) == 0 ) {
691+ return true;
692+ }
693+ if (mlen > plen && module_qn [mlen - plen - 1 ] == '.' &&
694+ strcmp (module_qn + (mlen - plen ), prefix ) == 0 ) {
695+ return true;
696+ }
697+ return false;
698+ }
699+
676700/* Strategy 2: Same-module match */
677701static cbm_resolution_t resolve_same_module (const cbm_registry_t * r , const char * callee_name ,
678- const char * suffix , const char * module_qn ) {
702+ const char * prefix , const char * suffix ,
703+ const char * module_qn ) {
679704 char candidate [CBM_SZ_512 ];
680705 snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , callee_name );
681706 const char * stored_key = cbm_ht_get_key (r -> exact , candidate );
682707 if (stored_key ) {
683708 return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
684709 }
685710 if (suffix && suffix [0 ]) {
686- snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
687- stored_key = cbm_ht_get_key (r -> exact , candidate );
688- if (stored_key ) {
689- return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
711+ if (is_same_module_receiver (prefix , module_qn )) {
712+ snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
713+ stored_key = cbm_ht_get_key (r -> exact , candidate );
714+ if (stored_key ) {
715+ return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE ,
716+ REG_RESOLVED };
717+ }
690718 }
691719 }
692720 return empty_result ();
@@ -778,6 +806,66 @@ static const char *qualified_suffix_match(const qn_array_t *arr, const char *cal
778806 }
779807 return match ;
780808}
809+ static bool qn_ends_with_qualified (const char * qn , const char * callee_name ) {
810+ char dotted [CBM_SZ_512 ];
811+ size_t w = 0 ;
812+ for (const char * s = callee_name ; * s && w + 1 < sizeof (dotted );) {
813+ if (s [0 ] == ':' && s [1 ] == ':' ) {
814+ dotted [w ++ ] = '.' ;
815+ s += 2 ;
816+ } else {
817+ dotted [w ++ ] = * s ++ ;
818+ }
819+ }
820+ dotted [w ] = '\0' ;
821+
822+ size_t qlen = strlen (qn );
823+ if (qlen < w ) {
824+ return false;
825+ }
826+ const char * tail = qn + (qlen - w );
827+ if (strcmp (tail , dotted ) != 0 ) {
828+ return false;
829+ }
830+ if (tail != qn && tail [-1 ] != '.' ) {
831+ return false;
832+ }
833+ return true;
834+ }
835+ static bool is_type_like_label (const char * label ) {
836+ if (!label ) {
837+ return false;
838+ }
839+ return strcmp (label , "Class" ) == 0 || strcmp (label , "Struct" ) == 0 ||
840+ strcmp (label , "Interface" ) == 0 || strcmp (label , "Enum" ) == 0 ||
841+ strcmp (label , "Type" ) == 0 || strcmp (label , "Trait" ) == 0 ;
842+ }
843+
844+ static bool is_candidate_method (const cbm_registry_t * r , const char * qn ) {
845+ const char * label = cbm_registry_label_of (r , qn );
846+ if (label && strcmp (label , "Method" ) == 0 ) {
847+ return true;
848+ }
849+
850+ char parent_qn [CBM_SZ_512 ];
851+ size_t len = strlen (qn );
852+ if (len >= sizeof (parent_qn )) {
853+ return false;
854+ }
855+ strcpy (parent_qn , qn );
856+ char * last_dot = strrchr (parent_qn , '.' );
857+ if (!last_dot ) {
858+ return false;
859+ }
860+ * last_dot = '\0' ;
861+
862+ const char * parent_label = cbm_registry_label_of (r , parent_qn );
863+ return is_type_like_label (parent_label );
864+ }
865+
866+ static bool is_qualified_callee (const char * callee_name ) {
867+ return strchr (callee_name , '.' ) != NULL || strstr (callee_name , "::" ) != NULL ;
868+ }
781869
782870/* Strategy 3+4: Name lookup + suffix match */
783871static cbm_resolution_t resolve_name_lookup (const cbm_registry_t * r , const char * callee_name ,
@@ -792,36 +880,53 @@ static cbm_resolution_t resolve_name_lookup(const cbm_registry_t *r, const char
792880 return empty_result (); /* unresolvably ambiguous — see REG_MAX_CANDIDATES */
793881 }
794882
883+ cbm_resolution_t res = empty_result ();
884+
795885 /* Strategy 3.5: a qualified callee disambiguates among multiple same-name
796886 * candidates by full qualified tail, before bare-name scoring collapses
797887 * them onto a single winner. */
798888 if (arr -> count > 1 ) {
799889 const char * q = qualified_suffix_match (arr , callee_name );
800890 if (q ) {
801- return (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
891+ res = (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
802892 }
803893 }
804894
805- /* Strategy 3: unique name */
806- if (arr -> count == SKIP_ONE ) {
807- double conf = CONF_UNIQUE_NAME ;
808- if (import_vals && import_count > 0 &&
809- !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
810- conf *= DEFAULT_CONFIDENCE ;
895+ if (!(res .qualified_name && res .qualified_name [0 ])) {
896+ /* Strategy 3: unique name */
897+ if (arr -> count == 1 ) {
898+ double conf = CONF_UNIQUE_NAME ;
899+ if (import_vals && import_count > 0 &&
900+ !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
901+ conf *= DEFAULT_CONFIDENCE ;
902+ }
903+ res = (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
811904 }
812- return (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
813905 }
814906
815- /* Strategy 4: multiple candidates */
816- if (import_vals && import_count > 0 ) {
817- return resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
907+ if (!(res .qualified_name && res .qualified_name [0 ])) {
908+ /* Strategy 4: multiple candidates */
909+ if (import_vals && import_count > 0 ) {
910+ res = resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
911+ } else {
912+ const char * best =
913+ best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
914+ if (best ) {
915+ double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
916+ res = (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
917+ }
918+ }
818919 }
819- const char * best = best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
820- if (best ) {
821- double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
822- return (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
920+
921+ if (res .qualified_name && is_qualified_callee (callee_name )) {
922+ if (!is_candidate_method (r , res .qualified_name )) {
923+ if (!qn_ends_with_qualified (res .qualified_name , callee_name )) {
924+ return empty_result ();
925+ }
926+ }
823927 }
824- return empty_result ();
928+
929+ return res ;
825930}
826931
827932cbm_resolution_t cbm_registry_resolve (const cbm_registry_t * r , const char * callee_name ,
@@ -874,7 +979,7 @@ cbm_resolution_t cbm_registry_resolve(const cbm_registry_t *r, const char *calle
874979 resolve_import_map (r , prefix , suffix , import_map_keys , import_map_vals , import_map_count );
875980 if (!(res .qualified_name && res .qualified_name [0 ])) {
876981 /* Strategy 2: same module */
877- res = resolve_same_module (r , callee_name , suffix , module_qn );
982+ res = resolve_same_module (r , callee_name , prefix , suffix , module_qn );
878983 }
879984 if (!(res .qualified_name && res .qualified_name [0 ])) {
880985 /* Strategy 3+4: name lookup */
0 commit comments