fix(go): align worker CALLS source IDs for receiver methods (#1043)

This commit is contained in:
evolution 2026-04-23 15:08:56 +08:00 • committed by GitHub
parent 6ef7991704
commit 09f87189cb
5 changed files with 69 additions and 20 deletions

View file

@ -568,32 +568,48 @@ const findEnclosingFunctionId = (
filePath,
provider.resolveEnclosingOwner,
);
const qualifiedName = classInfo ? `${classInfo.className}.${funcName}` : funcName;
const encLang = getLanguageFromFilename(filePath);
const standaloneMethodInfo =
(finalLabel === 'Method' || finalLabel === 'Constructor') &&
encLang === SupportedLanguages.Go &&
provider.methodExtractor?.extractFromNode
? provider.methodExtractor.extractFromNode(current, {
filePath,
language: encLang,
})
: null;
const ownerName = classInfo?.className ?? standaloneMethodInfo?.receiverType ?? undefined;
const qualifiedName = ownerName ? `${ownerName}.${funcName}` : funcName;
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
// Use the same MethodExtractor (getMethodInfo) as the definition phase.
// When same-arity collisions exist, also append ~type1,type2.
let arity: number | undefined;
let encTypeTag = '';
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
const encLang = getLanguageFromFilename(filePath);
const classNode =
findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
if (classNode && encLang) {
const methodMap = getMethodInfo(classNode, provider, {
filePath,
language: encLang,
});
const defLine = current.startPosition.row + 1;
const info = methodMap?.get(`${funcName}:${defLine}`);
if (info) {
arity = info.parameters.some((p) => p.isVariadic)
? undefined
: info.parameters.length;
if (methodMap && arity !== undefined) {
const g = buildCollisionGroups(methodMap);
encTypeTag =
typeTagForId(methodMap, funcName, arity, info, encLang, g) +
constTagForId(methodMap, funcName, arity, info, g);
if (standaloneMethodInfo) {
arity = standaloneMethodInfo.parameters.some((p) => p.isVariadic)
? undefined
: standaloneMethodInfo.parameters.length;
} else {
const classNode =
findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
if (classNode && encLang) {
const methodMap = getMethodInfo(classNode, provider, {
filePath,
language: encLang,
});
const defLine = current.startPosition.row + 1;
const info = methodMap?.get(`${funcName}:${defLine}`);
if (info) {
arity = info.parameters.some((p) => p.isVariadic)
? undefined
: info.parameters.length;
if (methodMap && arity !== undefined) {
const g = buildCollisionGroups(methodMap);
encTypeTag =
typeTagForId(methodMap, funcName, arity, info, encLang, g) +
constTagForId(methodMap, funcName, arity, info, g);
}
}
}
}

View file

@ -0,0 +1,7 @@
package example
type Example struct{}
func (e *Example) Caller() {
callee()
}

View file

@ -0,0 +1,3 @@
module example.com/go-receiver-method-free-call
go 1.22

View file

@ -0,0 +1,3 @@
package example
func callee() {}

View file

@ -172,6 +172,26 @@ describe('Go member-call resolution', () => {
});
});
describe('Go receiver method free-call resolution', () => {
let result: PipelineResult;
beforeAll(async () => {
result = await runPipelineFromRepo(
path.join(FIXTURES, 'go-receiver-method-free-call'),
() => {},
{ workerThresholdsForTest: { minFiles: 1, minBytes: 0 } },
);
}, 60000);
it('resolves Caller -> callee when a receiver method calls a package-level function', () => {
const calls = getRelationships(result, 'CALLS');
const calleeCall = calls.find((c) => c.source === 'Caller' && c.target === 'callee');
expect(calleeCall).toBeDefined();
expect(calleeCall!.targetLabel).toBe('Function');
expect(calleeCall!.targetFilePath).toBe('util.go');
});
});
// ---------------------------------------------------------------------------
// Struct literal resolution: User{...} resolves to Struct node
// ---------------------------------------------------------------------------