mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-15 00:01:39 +08:00
fix(resolution): C, C++ and Objective-C rank as one language, and a lone candidate is judged by family (#3052)
Name matching compared a candidate's raw language tag with the reference's: the single-candidate confidence in matchByExactName and matchFuzzy, and findBestMatch's same-language skip and +50/-80 score. So a call whose name had several definitions in another of C, C++ and Objective-C scored -80 for each and linked to nothing (an Objective-C file's _glfwPlatformSetTls(), a C++ call into Dear ImGui's imgui.h, which reads as C), and the one definition of a name across TSX and TS, or C and C++, was a 0.5 cross-language guess. The single-candidate confidence now compares language families (sameLanguageFamily). Ranking treats C, C++ and Objective-C as one language (sameRankingLanguage), the very language worth a point that only breaks a tie; other families keep the language itself, because ranking them by family moved excalidraw's Store::scheduleCapture calls to the App.tsx method beside them and gave okhttp 83 wrong Java-to-Kotlin Builder picks. Validation: - __tests__/name-match-language-family.test.ts, 8 tests (default and wasm): 6 fail on the base main; the pin that a .ts call keeps its .ts target over a .tsx namesake beside it passes on both; all pass with the fix. 24 related test files (cross-language, React Native, Expo, Fabric, frameworks, mixed TS/JSX, Objective-C, resolution): 746 pass. - A/B, main vs fix: glfw +4 calls from Objective-C into C, imgui +93 from C++ into imgui.h (read as C) and imstb_rectpack.h, all to the definitions meant; excalidraw, okhttp, leveldb and jq gain or lose no edge (43, 9, 97 and 30 unique-name links go from 0.5 to 0.9 with the same targets); flask and gin byte-identical. - Index time, median of 3 interleaved: imgui 7.9 s main, 7.7 s fix; excalidraw 9.2 s main, 9.4 s fix. Reimplements PR #1662 by ferrine. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
19b74d10f3
commit
6d3804cf57
@@ -175,6 +175,21 @@ export function sameLanguageFamily(a: string, b: string): boolean {
|
||||
const fa = LANGUAGE_FAMILY[a];
|
||||
return fa !== undefined && fa === LANGUAGE_FAMILY[b];
|
||||
}
|
||||
|
||||
/**
|
||||
* The languages whose files share one table of names: a C function is called
|
||||
* by its bare name from C++ and Objective-C alike, and a `.h` file is C, C++
|
||||
* or Objective-C only as far as sniffing its text can tell (Dear ImGui's
|
||||
* C++ `imgui.h` reads as C), so name matching ranks them as one language.
|
||||
* Other families keep the language itself: their files reach each other's
|
||||
* names through imports, and a name-only pick across TypeScript and TSX, or
|
||||
* Java and Kotlin, went to a same-named declaration in the next file more
|
||||
* often than to the one meant (#1662).
|
||||
*/
|
||||
const C_LINKED_LANGUAGES: ReadonlySet<string> = new Set(['c', 'cpp', 'objc']);
|
||||
function sameRankingLanguage(a: string, b: string): boolean {
|
||||
return a === b || (C_LINKED_LANGUAGES.has(a) && C_LINKED_LANGUAGES.has(b));
|
||||
}
|
||||
/** Config/markup transitions stay open; every other code language has a family. */
|
||||
const CODE_FAMILY: Record<string, string> = {
|
||||
...LANGUAGE_FAMILY,
|
||||
@@ -13283,10 +13298,11 @@ export function matchByExactName(
|
||||
return null;
|
||||
}
|
||||
|
||||
// If only one match, use it — but penalize cross-language matches
|
||||
// If only one match, use it — but penalize a match in another language
|
||||
// family (a `.tsx` component calling a `.ts` helper is not one, #1662)
|
||||
if (candidates.length === 1) {
|
||||
if (!isCrossFileReachable(candidates[0]!, ref, context)) return null;
|
||||
const isCrossLanguage = candidates[0]!.language !== ref.language;
|
||||
const isCrossLanguage = !sameLanguageFamily(candidates[0]!.language, ref.language);
|
||||
return {
|
||||
original: ref,
|
||||
targetNodeId: candidates[0]!.id,
|
||||
@@ -13334,10 +13350,11 @@ export function matchByExactName(
|
||||
// above a proximity guess, but below a unique name or the file's own
|
||||
// definition (0.9). The candidates are those findBestMatch weighed: a
|
||||
// namesake in another language never wins while one in the reference's
|
||||
// own language exists. The caller's own copy being the caller itself is
|
||||
// no evidence, as for a same-file match (isDecisiveSameFileMatch).
|
||||
const weighed = candidates.some((c) => c.language === ref.language)
|
||||
? candidates.filter((c) => c.language === ref.language)
|
||||
// own language (sameRankingLanguage) exists. The caller's own copy being
|
||||
// the caller itself is no evidence, as for a same-file match
|
||||
// (isDecisiveSameFileMatch).
|
||||
const weighed = candidates.some((c) => sameRankingLanguage(c.language, ref.language))
|
||||
? candidates.filter((c) => sameRankingLanguage(c.language, ref.language))
|
||||
: candidates;
|
||||
const confidence = isDecisiveSameFileMatch(bestMatch, ref, candidates, context)
|
||||
? 0.9
|
||||
@@ -29385,7 +29402,8 @@ function findBestMatch(
|
||||
// Prioritization rules:
|
||||
// 1. Same file > different file
|
||||
// 2. Directory proximity (same module/package > different module)
|
||||
// 3. Same language > different language
|
||||
// 3. Same language > different language (C, C++ and Objective-C count as
|
||||
// one: sameRankingLanguage), and the very language as a tie-break
|
||||
// 4. Functions/methods > classes/types (for call references)
|
||||
// 5. Exported > non-exported
|
||||
|
||||
@@ -29406,10 +29424,11 @@ function findBestMatch(
|
||||
// scoring. Cuts the candidate set to same-language size on mixed front-end +
|
||||
// back-end repos (#915). When ALL candidates are cross-language (a legitimate
|
||||
// cross-language `calls` bridge), none are skipped and behavior is unchanged.
|
||||
const hasSameLanguage = candidates.some((c) => c.language === ref.language);
|
||||
const hasSameLanguage = candidates.some((c) => sameRankingLanguage(c.language, ref.language));
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (hasSameLanguage && candidate.language !== ref.language) continue;
|
||||
const sameLanguage = sameRankingLanguage(candidate.language, ref.language);
|
||||
if (hasSameLanguage && !sameLanguage) continue;
|
||||
|
||||
let score = 0;
|
||||
|
||||
@@ -29428,9 +29447,11 @@ function findBestMatch(
|
||||
score += 80;
|
||||
}
|
||||
|
||||
// Language matching: strongly prefer same language, penalize cross-language
|
||||
if (candidate.language === ref.language) {
|
||||
score += 50;
|
||||
// Language matching: strongly prefer same language, penalize cross-language.
|
||||
// Between C, C++ and Objective-C the very language adds one point, which
|
||||
// only breaks a tie: a directory nearer counts 15.
|
||||
if (sameLanguage) {
|
||||
score += candidate.language === ref.language ? 51 : 50;
|
||||
} else {
|
||||
score -= 80;
|
||||
}
|
||||
@@ -29662,7 +29683,7 @@ export function matchFuzzy(
|
||||
!(finalCandidates[0]!.kind !== 'function' && isBarePhpCall(ref, context)) &&
|
||||
isLexicallyReachable(finalCandidates[0]!, ref, context)
|
||||
) {
|
||||
const isCrossLanguage = finalCandidates[0]!.language !== ref.language;
|
||||
const isCrossLanguage = !sameLanguageFamily(finalCandidates[0]!.language, ref.language);
|
||||
return {
|
||||
original: ref,
|
||||
targetNodeId: finalCandidates[0]!.id,
|
||||
|
||||
Reference in New Issue
Block a user