@@ -223,6 +223,12 @@ static const uint8_t *qn_test_flags(const qn_array_t *arr) {
223223 return arr -> is_test && arr -> is_test_cap >= arr -> count ? arr -> is_test : NULL ;
224224}
225225
226+ /* The cached candidate languages, under the same coverage rule: both side
227+ * arrays grow together and share is_test_cap. */
228+ static const uint8_t * qn_lang_flags (const qn_array_t * arr ) {
229+ return arr -> lang && arr -> is_test_cap >= arr -> count ? arr -> lang : NULL ;
230+ }
231+
226232/* Score a candidate for tiebreaking. Higher = better.
227233 * Layer 1: Non-test code preferred over test code (+1000)
228234 * Layer 2: Namespace proximity via common prefix length (+plen)
@@ -237,16 +243,37 @@ static int candidate_score(const char *candidate_qn, const char *module_qn, int
237243 return score ;
238244}
239245
240- /* Number of '.'-separated segments in a QN: how deeply the definition is
241- * nested (file depth + enclosing types). */
242- static int qn_depth (const char * qn ) {
243- int depth = 1 ;
244- for (const char * p = qn ; * p ; p ++ ) {
245- if (* p == '.' ) {
246- depth ++ ;
247- }
246+ /* How plainly a candidate is something a call can target, lower = better: a
247+ * function or method; then any other definition (a class for a constructor
248+ * call, a macro, a type); then a candidate whose label is unknown; last a
249+ * variable or field, which a call reaches only when it holds a function. */
250+ enum {
251+ REG_RANK_CALLABLE = 0 ,
252+ REG_RANK_OTHER_DEF = 1 ,
253+ REG_RANK_UNKNOWN = 2 ,
254+ REG_RANK_DATA = 3 ,
255+ };
256+
257+ static int callable_rank (const cbm_registry_t * r , const char * qn ) {
258+ const char * label = r ? cbm_registry_label_of (r , qn ) : NULL ;
259+ if (!label ) {
260+ return REG_RANK_UNKNOWN ;
261+ }
262+ if (strcmp (label , "Function" ) == 0 || strcmp (label , "Method" ) == 0 ) {
263+ return REG_RANK_CALLABLE ;
264+ }
265+ if (strcmp (label , "Variable" ) == 0 || strcmp (label , "Field" ) == 0 ) {
266+ return REG_RANK_DATA ;
248267 }
249- return depth ;
268+ return REG_RANK_OTHER_DEF ;
269+ }
270+
271+ /* A .NET reference assembly (`src/libraries/<Asm>/ref/<Asm>.cs`) declares an
272+ * assembly's public surface with `throw null` bodies in one shallow file; a
273+ * call that could equally mean the implementation means the implementation.
274+ * C# only: elsewhere a `ref` directory is an ordinary name. */
275+ static bool csharp_reference_stub (const char * qn , uint8_t lang ) {
276+ return lang == CBM_LANG_CSHARP && strstr (qn , ".ref." ) != NULL ;
250277}
251278
252279/* Total order among candidates that tie on candidate_score. The registry's
@@ -255,33 +282,49 @@ static int qn_depth(const char *qn) {
255282 * that order (kernel: 632 CALLS edges differed between two indexes of the
256283 * same tree, `dev_name` flipping between twenty same-named struct fields
257284 * and unresolved, `sg_set_buf` between include/linux and tools/virtio).
258- * The rule: the least nested definition wins (a top-level function over a
259- * same-named member two types deep, include/linux over tools/virtio/linux),
260- * then the lexicographically smaller QN — a pure function of the candidate
261- * set, never of the order it was built in (O9). */
262- static bool candidate_outranks_on_tie (const char * candidate , const char * best ) {
263- int cd = qn_depth (candidate );
264- int bd = qn_depth (best );
265- if (cd != bd ) {
266- return cd < bd ;
285+ * The rule, a pure function of the candidate set (O9):
286+ * 1. the more plainly callable candidate (`dev_name` the function over the
287+ * struct fields; Kotlin `where(...)` the method over the property);
288+ * 2. for C#, an implementation over a reference-assembly stub;
289+ * 3. the lexicographically smaller QN (include/linux over tools/virtio).
290+ * Nesting depth is deliberately not a criterion: "least nested wins" sent
291+ * django's `Model.objects.filter` to the template `Library.filter`, one
292+ * segment shallower than `QuerySet.filter` (2026-10-10 audit). */
293+ static bool candidate_outranks_on_tie (const cbm_registry_t * r , const char * candidate ,
294+ uint8_t candidate_lang , const char * best , uint8_t best_lang ) {
295+ int ck = callable_rank (r , candidate );
296+ int bk = callable_rank (r , best );
297+ if (ck != bk ) {
298+ return ck < bk ;
299+ }
300+ bool cs = csharp_reference_stub (candidate , candidate_lang );
301+ bool bs = csharp_reference_stub (best , best_lang );
302+ if (cs != bs ) {
303+ return !cs ;
267304 }
268305 return strcmp (candidate , best ) < 0 ;
269306}
270307
271308/* Pick candidate with highest composite score (test-deprioritization + namespace
272- * proximity). `is_test_flags` is the candidates' cached test verdicts, or NULL
273- * when the caller has none (a filtered subset carries its own copy). */
274- static const char * best_by_import_distance (const char * * candidates , const uint8_t * is_test_flags ,
309+ * proximity), ties settled by candidate_outranks_on_tie. `is_test_flags` and
310+ * `lang_flags` are the candidates' cached test verdicts and languages, or NULL
311+ * when the caller has none (a filtered subset carries its own copies). */
312+ static const char * best_by_import_distance (const cbm_registry_t * r , const char * * candidates ,
313+ const uint8_t * is_test_flags , const uint8_t * lang_flags ,
275314 int count , const char * module_qn ) {
276315 const char * best = NULL ;
316+ uint8_t best_lang = (uint8_t )CBM_LANG_COUNT ;
277317 int best_score = CBM_NOT_FOUND ;
278318 for (int i = 0 ; i < count ; i ++ ) {
279319 int score =
280320 candidate_score (candidates [i ], module_qn , is_test_flags ? (int )is_test_flags [i ] : -1 );
321+ uint8_t lang = lang_flags ? lang_flags [i ] : (uint8_t )CBM_LANG_COUNT ;
281322 if (score > best_score ||
282- (score == best_score && best && candidate_outranks_on_tie (candidates [i ], best ))) {
323+ (score == best_score && best &&
324+ candidate_outranks_on_tie (r , candidates [i ], lang , best , best_lang ))) {
283325 best_score = score ;
284326 best = candidates [i ];
327+ best_lang = lang ;
285328 }
286329 }
287330 return best ;
@@ -1409,17 +1452,23 @@ static cbm_resolution_t resolve_same_module(const cbm_registry_t *r, const char
14091452}
14101453
14111454/* Strategy 4: multiple candidates with import filtering. */
1412- static cbm_resolution_t resolve_multi_with_imports (const qn_array_t * arr , const char * module_qn ,
1413- const char * * import_vals , int import_count ) {
1455+ static cbm_resolution_t resolve_multi_with_imports (const cbm_registry_t * r , const qn_array_t * arr ,
1456+ const char * module_qn , const char * * import_vals ,
1457+ int import_count ) {
14141458 const char * filtered [CBM_SZ_256 ];
14151459 uint8_t filtered_test [CBM_SZ_256 ];
1460+ uint8_t filtered_lang [CBM_SZ_256 ];
14161461 const uint8_t * flags = qn_test_flags (arr );
1462+ const uint8_t * langs = qn_lang_flags (arr );
14171463 int fcount = 0 ;
14181464 for (int i = 0 ; i < arr -> count && fcount < CBM_SZ_256 ; i ++ ) {
14191465 if (is_import_reachable (arr -> items [i ], import_vals , import_count )) {
14201466 if (flags ) {
14211467 filtered_test [fcount ] = flags [i ];
14221468 }
1469+ if (langs ) {
1470+ filtered_lang [fcount ] = langs [i ];
1471+ }
14231472 filtered [fcount ] = arr -> items [i ];
14241473 fcount ++ ;
14251474 }
@@ -1429,16 +1478,16 @@ static cbm_resolution_t resolve_multi_with_imports(const qn_array_t *arr, const
14291478 return (cbm_resolution_t ){filtered [0 ], "suffix_match" , conf , arr -> count };
14301479 }
14311480 if (fcount > SKIP_ONE ) {
1432- const char * best =
1433- best_by_import_distance ( filtered , flags ? filtered_test : NULL , fcount , module_qn );
1481+ const char * best = best_by_import_distance ( r , filtered , flags ? filtered_test : NULL ,
1482+ langs ? filtered_lang : NULL , fcount , module_qn );
14341483 if (best ) {
14351484 double conf = candidate_count_penalty (CONF_SUFFIX_MATCH , fcount );
14361485 return (cbm_resolution_t ){best , "suffix_match" , conf , fcount };
14371486 }
14381487 }
14391488 /* No import-reachable — use all candidates with penalty */
1440- const char * best = best_by_import_distance ((const char * * )arr -> items , qn_test_flags (arr ),
1441- arr -> count , module_qn );
1489+ const char * best = best_by_import_distance (r , (const char * * )arr -> items , qn_test_flags (arr ),
1490+ qn_lang_flags ( arr ), arr -> count , module_qn );
14421491 if (best ) {
14431492 double conf = candidate_count_penalty (CONF_SUFFIX_MATCH * REG_HALF_PENALTY , arr -> count );
14441493 return (cbm_resolution_t ){best , "suffix_match" , conf , arr -> count };
@@ -1632,10 +1681,10 @@ static cbm_resolution_t resolve_name_lookup(const cbm_registry_t *r, const char
16321681
16331682 /* Strategy 4: multiple candidates */
16341683 if (import_vals && import_count > 0 ) {
1635- return resolve_multi_with_imports (arr , module_qn , import_vals , import_count );
1684+ return resolve_multi_with_imports (r , arr , module_qn , import_vals , import_count );
16361685 }
1637- const char * best = best_by_import_distance ((const char * * )arr -> items , qn_test_flags (arr ),
1638- arr -> count , module_qn );
1686+ const char * best = best_by_import_distance (r , (const char * * )arr -> items , qn_test_flags (arr ),
1687+ qn_lang_flags ( arr ), arr -> count , module_qn );
16391688 if (best ) {
16401689 if (!receiver_chain_admits (callee_name , best )) {
16411690 return empty_result ();
@@ -2011,8 +2060,8 @@ cbm_fuzzy_result_t cbm_registry_fuzzy_resolve(const cbm_registry_t *r, const cha
20112060
20122061 if (fcount == 0 ) {
20132062 /* No import-reachable — use originals with penalty */
2014- const char * best = best_by_import_distance ((const char * * )arr -> items , qn_test_flags (arr ),
2015- arr -> count , module_qn );
2063+ const char * best = best_by_import_distance (r , (const char * * )arr -> items , qn_test_flags (arr ),
2064+ qn_lang_flags ( arr ), arr -> count , module_qn );
20162065 if (!best ) {
20172066 return no_match ;
20182067 }
@@ -2026,8 +2075,9 @@ cbm_fuzzy_result_t cbm_registry_fuzzy_resolve(const cbm_registry_t *r, const cha
20262075 {fptr [0 ], "fuzzy" , candidate_count_penalty (CONF_FUZZY_SINGLE , arr -> count ), arr -> count },
20272076 true};
20282077 }
2029- const char * best = best_by_import_distance (
2030- fptr , fptr == (const char * * )arr -> items ? qn_test_flags (arr ) : NULL , fcount , module_qn );
2078+ const bool all = fptr == (const char * * )arr -> items ;
2079+ const char * best = best_by_import_distance (r , fptr , all ? qn_test_flags (arr ) : NULL ,
2080+ all ? qn_lang_flags (arr ) : NULL , fcount , module_qn );
20312081 if (!best ) {
20322082 return no_match ;
20332083 }
0 commit comments