@@ -605,20 +605,48 @@ static cbm_resolution_t resolve_import_map(const cbm_registry_t *r, const char *
605605 return empty_result ();
606606}
607607
608+ static bool is_same_module_receiver (const char * prefix , const char * module_qn ) {
609+ if (!prefix || !prefix [0 ]) {
610+ return true; /* Bare names are always candidates for same-module resolution */
611+ }
612+ /* 1. Check known self-receivers */
613+ static const char * const self_receivers [] = {"self" , "this" , "cls" , "@self" , NULL };
614+ for (int i = 0 ; self_receivers [i ]; i ++ ) {
615+ if (strcmp (prefix , self_receivers [i ]) == 0 ) {
616+ return true;
617+ }
618+ }
619+ /* 2. Check if prefix matches the module name or its last segment (namespace qualified) */
620+ size_t plen = strlen (prefix );
621+ size_t mlen = strlen (module_qn );
622+ if (mlen == plen && strcmp (module_qn , prefix ) == 0 ) {
623+ return true;
624+ }
625+ if (mlen > plen && module_qn [mlen - plen - 1 ] == '.' &&
626+ strcmp (module_qn + (mlen - plen ), prefix ) == 0 ) {
627+ return true;
628+ }
629+ return false;
630+ }
631+
608632/* Strategy 2: Same-module match */
609633static cbm_resolution_t resolve_same_module (const cbm_registry_t * r , const char * callee_name ,
610- const char * suffix , const char * module_qn ) {
634+ const char * prefix , const char * suffix ,
635+ const char * module_qn ) {
611636 char candidate [CBM_SZ_512 ];
612637 snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , callee_name );
613638 const char * stored_key = cbm_ht_get_key (r -> exact , candidate );
614639 if (stored_key ) {
615640 return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
616641 }
617642 if (suffix && suffix [0 ]) {
618- snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
619- stored_key = cbm_ht_get_key (r -> exact , candidate );
620- if (stored_key ) {
621- return (cbm_resolution_t ){stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
643+ if (is_same_module_receiver (prefix , module_qn )) {
644+ snprintf (candidate , sizeof (candidate ), "%s.%s" , module_qn , suffix );
645+ stored_key = cbm_ht_get_key (r -> exact , candidate );
646+ if (stored_key ) {
647+ return (cbm_resolution_t ){
648+ stored_key , "same_module" , CONF_SAME_MODULE , REG_RESOLVED };
649+ }
622650 }
623651 }
624652 return empty_result ();
@@ -710,6 +738,66 @@ static const char *qualified_suffix_match(const qn_array_t *arr, const char *cal
710738 }
711739 return match ;
712740}
741+ static bool qn_ends_with_qualified (const char * qn , const char * callee_name ) {
742+ char dotted [CBM_SZ_512 ];
743+ size_t w = 0 ;
744+ for (const char * s = callee_name ; * s && w + 1 < sizeof (dotted );) {
745+ if (s [0 ] == ':' && s [1 ] == ':' ) {
746+ dotted [w ++ ] = '.' ;
747+ s += 2 ;
748+ } else {
749+ dotted [w ++ ] = * s ++ ;
750+ }
751+ }
752+ dotted [w ] = '\0' ;
753+
754+ size_t qlen = strlen (qn );
755+ if (qlen < w ) {
756+ return false;
757+ }
758+ const char * tail = qn + (qlen - w );
759+ if (strcmp (tail , dotted ) != 0 ) {
760+ return false;
761+ }
762+ if (tail != qn && tail [-1 ] != '.' ) {
763+ return false;
764+ }
765+ return true;
766+ }
767+ static bool is_type_like_label (const char * label ) {
768+ if (!label ) {
769+ return false;
770+ }
771+ return strcmp (label , "Class" ) == 0 || strcmp (label , "Struct" ) == 0 ||
772+ strcmp (label , "Interface" ) == 0 || strcmp (label , "Enum" ) == 0 ||
773+ strcmp (label , "Type" ) == 0 || strcmp (label , "Trait" ) == 0 ;
774+ }
775+
776+ static bool is_candidate_method (const cbm_registry_t * r , const char * qn ) {
777+ const char * label = cbm_registry_label_of (r , qn );
778+ if (label && strcmp (label , "Method" ) == 0 ) {
779+ return true;
780+ }
781+
782+ char parent_qn [CBM_SZ_512 ];
783+ size_t len = strlen (qn );
784+ if (len >= sizeof (parent_qn )) {
785+ return false;
786+ }
787+ strcpy (parent_qn , qn );
788+ char * last_dot = strrchr (parent_qn , '.' );
789+ if (!last_dot ) {
790+ return false;
791+ }
792+ * last_dot = '\0' ;
793+
794+ const char * parent_label = cbm_registry_label_of (r , parent_qn );
795+ return is_type_like_label (parent_label );
796+ }
797+
798+ static bool is_qualified_callee (const char * callee_name ) {
799+ return strchr (callee_name , '.' ) != NULL || strstr (callee_name , "::" ) != NULL ;
800+ }
713801
714802/* Strategy 3+4: Name lookup + suffix match */
715803static cbm_resolution_t resolve_name_lookup (const cbm_registry_t * r , const char * callee_name ,
@@ -724,36 +812,52 @@ static cbm_resolution_t resolve_name_lookup(const cbm_registry_t *r, const char
724812 return empty_result (); /* unresolvably ambiguous — see REG_MAX_CANDIDATES */
725813 }
726814
815+ cbm_resolution_t res = empty_result ();
816+
727817 /* Strategy 3.5: a qualified callee disambiguates among multiple same-name
728818 * candidates by full qualified tail, before bare-name scoring collapses
729819 * them onto a single winner. */
730820 if (arr -> count > 1 ) {
731821 const char * q = qualified_suffix_match (arr , callee_name );
732822 if (q ) {
733- return (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
823+ res = (cbm_resolution_t ){q , "qualified_suffix" , CONF_QUALIFIED_SUFFIX , REG_RESOLVED };
734824 }
735825 }
736826
737- /* Strategy 3: unique name */
738- if (arr -> count == SKIP_ONE ) {
739- double conf = CONF_UNIQUE_NAME ;
740- if (import_vals && import_count > 0 &&
741- !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
742- conf *= DEFAULT_CONFIDENCE ;
827+ if (!(res .qualified_name && res .qualified_name [0 ])) {
828+ /* Strategy 3: unique name */
829+ if (arr -> count == 1 ) {
830+ double conf = CONF_UNIQUE_NAME ;
831+ if (import_vals && import_count > 0 &&
832+ !is_import_reachable (arr -> items [0 ], import_vals , import_count )) {
833+ conf *= DEFAULT_CONFIDENCE ;
834+ }
835+ res = (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
743836 }
744- return (cbm_resolution_t ){arr -> items [0 ], "unique_name" , conf , REG_RESOLVED };
745837 }
746838
747- /* Strategy 4: multiple candidates */
748- if (import_vals && import_count > 0 ) {
749- return resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
839+ if (!(res .qualified_name && res .qualified_name [0 ])) {
840+ /* Strategy 4: multiple candidates */
841+ if (import_vals && import_count > 0 ) {
842+ res = resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
843+ } else {
844+ const char * best = best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
845+ if (best ) {
846+ double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
847+ res = (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
848+ }
849+ }
750850 }
751- const char * best = best_by_import_distance ((const char * * )arr -> items , arr -> count , module_qn );
752- if (best ) {
753- double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , arr -> count );
754- return (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
851+
852+ if (res .qualified_name && is_qualified_callee (callee_name )) {
853+ if (!is_candidate_method (r , res .qualified_name )) {
854+ if (!qn_ends_with_qualified (res .qualified_name , callee_name )) {
855+ return empty_result ();
856+ }
857+ }
755858 }
756- return empty_result ();
859+
860+ return res ;
757861}
758862
759863cbm_resolution_t cbm_registry_resolve (const cbm_registry_t * r , const char * callee_name ,
@@ -806,7 +910,7 @@ cbm_resolution_t cbm_registry_resolve(const cbm_registry_t *r, const char *calle
806910 resolve_import_map (r , prefix , suffix , import_map_keys , import_map_vals , import_map_count );
807911 if (!(res .qualified_name && res .qualified_name [0 ])) {
808912 /* Strategy 2: same module */
809- res = resolve_same_module (r , callee_name , suffix , module_qn );
913+ res = resolve_same_module (r , callee_name , prefix , suffix , module_qn );
810914 }
811915 if (!(res .qualified_name && res .qualified_name [0 ])) {
812916 /* Strategy 3+4: name lookup */
0 commit comments