@@ -663,20 +663,48 @@ static cbm_resolution_t resolve_import_map(const cbm_registry_t *r, const char *
663663 return empty_result ();
664664}
665665
666+ static bool is_same_module_receiver (const char * prefix , const char * module_qn ) {
667+ if (!prefix || !prefix [0 ]) {
668+ return true; /* Bare names are always candidates for same-module resolution */
669+ }
670+ /* 1. Check known self-receivers */
671+ static const char * const self_receivers [] = {"self" , "this" , "cls" , "@self" , NULL };
672+ for (int i = 0 ; self_receivers [i ]; i ++ ) {
673+ if (strcmp (prefix , self_receivers [i ]) == 0 ) {
674+ return true;
675+ }
676+ }
677+ /* 2. Check if prefix matches the module name or its last segment (namespace qualified) */
678+ size_t plen = strlen (prefix );
679+ size_t mlen = strlen (module_qn );
680+ if (mlen == plen && strcmp (module_qn , prefix ) == 0 ) {
681+ return true;
682+ }
683+ if (mlen > plen && module_qn [mlen - plen - 1 ] == '.' &&
684+ strcmp (module_qn + (mlen - plen ), prefix ) == 0 ) {
685+ return true;
686+ }
687+ return false;
688+ }
689+
666690/* Strategy 2: Same-module match */
667691static cbm_resolution_t resolve_same_module (const cbm_registry_t * r , const char * callee_name ,
668- const char * suffix , const char * module_qn ) {
692+ const char * prefix , const char * suffix ,
693+ const char * module_qn ) {
669694 char candidate [CBM_SZ_512 ];
670695 snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , callee_name );
671696 const char * stored_key = cbm_ht_get_key (r -> exact , candidate );
672697 if (stored_key ) {
673698 return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
674699 }
675700 if (suffix && suffix [0 ]) {
676- snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
677- stored_key = cbm_ht_get_key (r -> exact , candidate );
678- if (stored_key ) {
679- return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
701+ if (is_same_module_receiver (prefix , module_qn )) {
702+ snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
703+ stored_key = cbm_ht_get_key (r -> exact , candidate );
704+ if (stored_key ) {
705+ return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE ,
706+ REG_RESOLVED };
707+ }
680708 }
681709 }
682710 return empty_result ();
@@ -768,6 +796,66 @@ static const char *qualified_suffix_match(const qn_array_t *arr, const char *cal
768796 }
769797 return match ;
770798}
799+ static bool qn_ends_with_qualified (const char * qn , const char * callee_name ) {
800+ char dotted [CBM_SZ_512 ];
801+ size_t w = 0 ;
802+ for (const char * s = callee_name ; * s && w + 1 < sizeof (dotted );) {
803+ if (s [0 ] == ':' && s [1 ] == ':' ) {
804+ dotted [w ++ ] = '.' ;
805+ s += 2 ;
806+ } else {
807+ dotted [w ++ ] = * s ++ ;
808+ }
809+ }
810+ dotted [w ] = '\0' ;
811+
812+ size_t qlen = strlen (qn );
813+ if (qlen < w ) {
814+ return false;
815+ }
816+ const char * tail = qn + (qlen - w );
817+ if (strcmp (tail , dotted ) != 0 ) {
818+ return false;
819+ }
820+ if (tail != qn && tail [-1 ] != '.' ) {
821+ return false;
822+ }
823+ return true;
824+ }
825+ static bool is_type_like_label (const char * label ) {
826+ if (!label ) {
827+ return false;
828+ }
829+ return strcmp (label , "Class" ) == 0 || strcmp (label , "Struct" ) == 0 ||
830+ strcmp (label , "Interface" ) == 0 || strcmp (label , "Enum" ) == 0 ||
831+ strcmp (label , "Type" ) == 0 || strcmp (label , "Trait" ) == 0 ;
832+ }
833+
834+ static bool is_candidate_method (const cbm_registry_t * r , const char * qn ) {
835+ const char * label = cbm_registry_label_of (r , qn );
836+ if (label && strcmp (label , "Method" ) == 0 ) {
837+ return true;
838+ }
839+
840+ char parent_qn [CBM_SZ_512 ];
841+ size_t len = strlen (qn );
842+ if (len >= sizeof (parent_qn )) {
843+ return false;
844+ }
845+ strcpy (parent_qn , qn );
846+ char * last_dot = strrchr (parent_qn , '.' );
847+ if (!last_dot ) {
848+ return false;
849+ }
850+ * last_dot = '\0' ;
851+
852+ const char * parent_label = cbm_registry_label_of (r , parent_qn );
853+ return is_type_like_label (parent_label );
854+ }
855+
856+ static bool is_qualified_callee (const char * callee_name ) {
857+ return strchr (callee_name , '.' ) != NULL || strstr (callee_name , "::" ) != NULL ;
858+ }
771859
772860/* Strategy 3+4: Name lookup + suffix match */
773861static cbm_resolution_t resolve_name_lookup (const cbm_registry_t * r , const char * callee_name ,
@@ -782,36 +870,53 @@ static cbm_resolution_t resolve_name_lookup(const cbm_registry_t *r, const char
782870 return empty_result (); /* unresolvably ambiguous — see REG_MAX_CANDIDATES */
783871 }
784872
873+ cbm_resolution_t res = empty_result ();
874+
785875 /* Strategy 3.5: a qualified callee disambiguates among multiple same-name
786876 * candidates by full qualified tail, before bare-name scoring collapses
787877 * them onto a single winner. */
788878 if (arr -> count > 1 ) {
789879 const char * q = qualified_suffix_match (arr , callee_name );
790880 if (q ) {
791- return (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
881+ res = (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
792882 }
793883 }
794884
795- /* Strategy 3: unique name */
796- if (arr -> count == SKIP_ONE ) {
797- double conf = CONF_UNIQUE_NAME ;
798- if (import_vals && import_count > 0 &&
799- !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
800- conf *= DEFAULT_CONFIDENCE ;
885+ if (!(res .qualified_name && res .qualified_name [0 ])) {
886+ /* Strategy 3: unique name */
887+ if (arr -> count == 1 ) {
888+ double conf = CONF_UNIQUE_NAME ;
889+ if (import_vals && import_count > 0 &&
890+ !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
891+ conf *= DEFAULT_CONFIDENCE ;
892+ }
893+ res = (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
801894 }
802- return (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
803895 }
804896
805- /* Strategy 4: multiple candidates */
806- if (import_vals && import_count > 0 ) {
807- return resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
897+ if (!(res .qualified_name && res .qualified_name [0 ])) {
898+ /* Strategy 4: multiple candidates */
899+ if (import_vals && import_count > 0 ) {
900+ res = resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
901+ } else {
902+ const char * best =
903+ best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
904+ if (best ) {
905+ double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
906+ res = (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
907+ }
908+ }
808909 }
809- const char * best = best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
810- if (best ) {
811- double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
812- return (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
910+
911+ if (res .qualified_name && is_qualified_callee (callee_name )) {
912+ if (!is_candidate_method (r , res .qualified_name )) {
913+ if (!qn_ends_with_qualified (res .qualified_name , callee_name )) {
914+ return empty_result ();
915+ }
916+ }
813917 }
814- return empty_result ();
918+
919+ return res ;
815920}
816921
817922cbm_resolution_t cbm_registry_resolve (const cbm_registry_t * r , const char * callee_name ,
@@ -864,7 +969,7 @@ cbm_resolution_t cbm_registry_resolve(const cbm_registry_t *r, const char *calle
864969 resolve_import_map (r , prefix , suffix , import_map_keys , import_map_vals , import_map_count );
865970 if (!(res .qualified_name && res .qualified_name [0 ])) {
866971 /* Strategy 2: same module */
867- res = resolve_same_module (r , callee_name , suffix , module_qn );
972+ res = resolve_same_module (r , callee_name , prefix , suffix , module_qn );
868973 }
869974 if (!(res .qualified_name && res .qualified_name [0 ])) {
870975 /* Strategy 3+4: name lookup */
0 commit comments