gitnexus 1.6.2-rc.14 → 1.6.2-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/ingestion/call-extractors/configs/c-cpp.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/c-cpp.js +8 -0
- package/dist/core/ingestion/call-extractors/configs/csharp.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/csharp.js +6 -0
- package/dist/core/ingestion/call-extractors/configs/dart.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/dart.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/go.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/go.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/jvm.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/jvm.js +51 -0
- package/dist/core/ingestion/call-extractors/configs/php.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/php.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/python.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/python.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/ruby.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/ruby.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/rust.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/rust.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/swift.d.ts +2 -0
- package/dist/core/ingestion/call-extractors/configs/swift.js +5 -0
- package/dist/core/ingestion/call-extractors/configs/typescript-javascript.d.ts +3 -0
- package/dist/core/ingestion/call-extractors/configs/typescript-javascript.js +8 -0
- package/dist/core/ingestion/call-extractors/generic.d.ts +5 -0
- package/dist/core/ingestion/call-extractors/generic.js +59 -0
- package/dist/core/ingestion/call-processor.js +48 -45
- package/dist/core/ingestion/call-types.d.ts +60 -0
- package/dist/core/ingestion/call-types.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +7 -0
- package/dist/core/ingestion/languages/c-cpp.js +4 -0
- package/dist/core/ingestion/languages/csharp.js +3 -0
- package/dist/core/ingestion/languages/dart.js +3 -0
- package/dist/core/ingestion/languages/go.js +3 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin.js +3 -0
- package/dist/core/ingestion/languages/php.js +3 -0
- package/dist/core/ingestion/languages/python.js +3 -0
- package/dist/core/ingestion/languages/ruby.js +3 -0
- package/dist/core/ingestion/languages/rust.js +3 -0
- package/dist/core/ingestion/languages/swift.js +3 -0
- package/dist/core/ingestion/languages/typescript.js +4 -0
- package/dist/core/ingestion/languages/vue.js +3 -0
- package/dist/core/ingestion/workers/parse-worker.js +162 -166
- package/package.json +1 -1
- package/dist/core/ingestion/call-sites/extract-language-call-site.d.ts +0 -10
- package/dist/core/ingestion/call-sites/extract-language-call-site.js +0 -22
- package/dist/core/ingestion/call-sites/java.d.ts +0 -9
- package/dist/core/ingestion/call-sites/java.js +0 -30
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/call-extractors/configs/c-cpp.ts
|
|
2
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
3
|
+
export const cCallConfig = {
|
|
4
|
+
language: SupportedLanguages.C,
|
|
5
|
+
};
|
|
6
|
+
export const cppCallConfig = {
|
|
7
|
+
language: SupportedLanguages.CPlusPlus,
|
|
8
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/call-extractors/configs/jvm.ts
|
|
2
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Java method_reference (::) parsing — absorbs call-sites/java.ts
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
/**
|
|
7
|
+
* Parse Java `method_reference` nodes (`expr::method`, `Type::new`,
|
|
8
|
+
* `this::m`, `super::m`).
|
|
9
|
+
*/
|
|
10
|
+
function parseJavaMethodReference(callNode) {
|
|
11
|
+
if (callNode.type !== 'method_reference')
|
|
12
|
+
return null;
|
|
13
|
+
const recv = callNode.namedChild(0);
|
|
14
|
+
if (!recv)
|
|
15
|
+
return null;
|
|
16
|
+
// Type::new → constructor call
|
|
17
|
+
for (const c of callNode.children) {
|
|
18
|
+
if (c.type === 'new') {
|
|
19
|
+
if (recv.type !== 'identifier')
|
|
20
|
+
return null;
|
|
21
|
+
return { calledName: recv.text, callForm: 'constructor' };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// expr::method → member call with receiver
|
|
25
|
+
const rhs = callNode.child(callNode.childCount - 1);
|
|
26
|
+
if (!rhs || rhs.type !== 'identifier')
|
|
27
|
+
return null;
|
|
28
|
+
const methodName = rhs.text;
|
|
29
|
+
if (recv.type === 'identifier') {
|
|
30
|
+
return { calledName: methodName, callForm: 'member', receiverName: recv.text };
|
|
31
|
+
}
|
|
32
|
+
if (recv.type === 'this') {
|
|
33
|
+
return { calledName: methodName, callForm: 'member', receiverName: 'this' };
|
|
34
|
+
}
|
|
35
|
+
if (recv.type === 'super') {
|
|
36
|
+
return { calledName: methodName, callForm: 'member', receiverName: 'super' };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Configs
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
export const javaCallConfig = {
|
|
44
|
+
language: SupportedLanguages.Java,
|
|
45
|
+
extractLanguageCallSite: parseJavaMethodReference,
|
|
46
|
+
typeAsReceiverHeuristic: true,
|
|
47
|
+
};
|
|
48
|
+
export const kotlinCallConfig = {
|
|
49
|
+
language: SupportedLanguages.Kotlin,
|
|
50
|
+
typeAsReceiverHeuristic: true,
|
|
51
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/call-extractors/configs/typescript-javascript.ts
|
|
2
|
+
import { SupportedLanguages } from '../../../../_shared/index.js';
|
|
3
|
+
export const typescriptCallConfig = {
|
|
4
|
+
language: SupportedLanguages.TypeScript,
|
|
5
|
+
};
|
|
6
|
+
export const javascriptCallConfig = {
|
|
7
|
+
language: SupportedLanguages.JavaScript,
|
|
8
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// gitnexus/src/core/ingestion/call-extractors/generic.ts
|
|
2
|
+
import { inferCallForm, extractReceiverName, extractReceiverNode, extractMixedChain, countCallArguments, } from '../utils/call-analysis.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create a CallExtractor from a declarative config.
|
|
5
|
+
*/
|
|
6
|
+
export function createCallExtractor(config) {
|
|
7
|
+
return {
|
|
8
|
+
language: config.language,
|
|
9
|
+
extract(callNode, callNameNode) {
|
|
10
|
+
// ── Path 1: Language-specific call site ──────────────────────────
|
|
11
|
+
// Non-standard call shapes (e.g. Java `::` method references) are
|
|
12
|
+
// handled entirely by the config hook. When it returns a result,
|
|
13
|
+
// the generic path is skipped — no argCount, no mixed chain.
|
|
14
|
+
//
|
|
15
|
+
// Note: `extractLanguageCallSite` is called on every `extract()`
|
|
16
|
+
// invocation — both `extract(callNode, undefined)` (parse-worker
|
|
17
|
+
// Path 1) and `extract(callNode, callNameNode)` (Path 2).
|
|
18
|
+
// Language hooks must therefore be idempotent and cheap (e.g. a
|
|
19
|
+
// single node-type check).
|
|
20
|
+
if (config.extractLanguageCallSite) {
|
|
21
|
+
const seed = config.extractLanguageCallSite(callNode);
|
|
22
|
+
if (seed) {
|
|
23
|
+
return {
|
|
24
|
+
...seed,
|
|
25
|
+
...(config.typeAsReceiverHeuristic ? { typeAsReceiverHeuristic: true } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// ── Path 2: Generic extraction via @call.name ────────────────────
|
|
30
|
+
if (!callNameNode)
|
|
31
|
+
return null;
|
|
32
|
+
const calledName = callNameNode.text;
|
|
33
|
+
const callForm = inferCallForm(callNode, callNameNode);
|
|
34
|
+
let receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
35
|
+
let receiverMixedChain;
|
|
36
|
+
// When the receiver is a complex expression (call chain, field chain,
|
|
37
|
+
// or mixed), extractReceiverName returns undefined. Walk the receiver
|
|
38
|
+
// node to build a unified mixed chain for deferred resolution.
|
|
39
|
+
if (callForm === 'member' && receiverName === undefined) {
|
|
40
|
+
const receiverNode = extractReceiverNode(callNameNode);
|
|
41
|
+
if (receiverNode) {
|
|
42
|
+
const extracted = extractMixedChain(receiverNode);
|
|
43
|
+
if (extracted && extracted.chain.length > 0) {
|
|
44
|
+
receiverMixedChain = extracted.chain;
|
|
45
|
+
receiverName = extracted.baseReceiverName;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
calledName,
|
|
51
|
+
...(callForm !== undefined ? { callForm } : {}),
|
|
52
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
53
|
+
argCount: countCallArguments(callNode),
|
|
54
|
+
...(receiverMixedChain !== undefined ? { receiverMixedChain } : {}),
|
|
55
|
+
...(config.typeAsReceiverHeuristic ? { typeAsReceiverHeuristic: true } : {}),
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -15,7 +15,6 @@ import { getTreeSitterBufferSize } from './constants.js';
|
|
|
15
15
|
import { normalizeFetchURL, routeMatches } from './route-extractors/nextjs.js';
|
|
16
16
|
import { extractTemplateComponents } from './vue-sfc-extractor.js';
|
|
17
17
|
import { extractReturnTypeName, stripNullable } from './type-extractors/shared.js';
|
|
18
|
-
import { extractParsedCallSite } from './call-sites/extract-language-call-site.js';
|
|
19
18
|
import { lookupMethodByOwnerWithMRO } from './model/resolve.js';
|
|
20
19
|
/**
|
|
21
20
|
* Type labels treated as class-like **method-dispatch receivers** by the call
|
|
@@ -733,52 +732,56 @@ importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
|
|
|
733
732
|
if (!captureMap['call'])
|
|
734
733
|
return;
|
|
735
734
|
const callNode = captureMap['call'];
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
(
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
callForm: languageSeed.callForm,
|
|
755
|
-
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
756
|
-
...(receiverName !== undefined ? { receiverName } : {}),
|
|
757
|
-
}, file.path, ctx, undefined, widenCache, undefined, heritageMap);
|
|
758
|
-
if (!resolved)
|
|
759
|
-
return;
|
|
760
|
-
graph.addRelationship({
|
|
761
|
-
id: generateId('CALLS', `${sourceId}:${languageSeed.calledName}->${resolved.nodeId}`),
|
|
762
|
-
sourceId,
|
|
763
|
-
targetId: resolved.nodeId,
|
|
764
|
-
type: 'CALLS',
|
|
765
|
-
confidence: resolved.confidence,
|
|
766
|
-
reason: resolved.reason,
|
|
767
|
-
});
|
|
768
|
-
if (heritageMap && languageSeed.callForm === 'member' && receiverTypeName) {
|
|
769
|
-
const implTargets = findInterfaceDispatchTargets(languageSeed.calledName, receiverTypeName, file.path, ctx, heritageMap, resolved.nodeId);
|
|
770
|
-
for (const impl of implTargets) {
|
|
771
|
-
graph.addRelationship({
|
|
772
|
-
id: generateId('CALLS', `${sourceId}:${languageSeed.calledName}->${impl.nodeId}`),
|
|
773
|
-
sourceId,
|
|
774
|
-
targetId: impl.nodeId,
|
|
775
|
-
type: 'CALLS',
|
|
776
|
-
confidence: impl.confidence,
|
|
777
|
-
reason: impl.reason,
|
|
778
|
-
});
|
|
735
|
+
const callExtractor = provider.callExtractor;
|
|
736
|
+
// ── Language-specific call site (e.g. Java :: method references) ──
|
|
737
|
+
if (callExtractor) {
|
|
738
|
+
const langCallSite = callExtractor.extract(callNode, undefined);
|
|
739
|
+
if (langCallSite) {
|
|
740
|
+
if (provider.isBuiltInName(langCallSite.calledName))
|
|
741
|
+
return;
|
|
742
|
+
const sourceId = findEnclosingFunction(callNode, file.path, ctx, provider) ||
|
|
743
|
+
generateId('File', file.path);
|
|
744
|
+
const receiverName = langCallSite.callForm === 'member' ? langCallSite.receiverName : undefined;
|
|
745
|
+
let receiverTypeName = receiverName && typeEnv ? typeEnv.lookup(receiverName, callNode) : undefined;
|
|
746
|
+
if (langCallSite.typeAsReceiverHeuristic &&
|
|
747
|
+
receiverName !== undefined &&
|
|
748
|
+
receiverTypeName === undefined &&
|
|
749
|
+
langCallSite.callForm === 'member') {
|
|
750
|
+
const c0 = receiverName.charCodeAt(0);
|
|
751
|
+
if (c0 >= 65 && c0 <= 90)
|
|
752
|
+
receiverTypeName = receiverName;
|
|
779
753
|
}
|
|
754
|
+
const resolved = resolveCallTarget({
|
|
755
|
+
calledName: langCallSite.calledName,
|
|
756
|
+
callForm: langCallSite.callForm,
|
|
757
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
758
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
759
|
+
}, file.path, ctx, undefined, widenCache, undefined, heritageMap);
|
|
760
|
+
if (!resolved)
|
|
761
|
+
return;
|
|
762
|
+
graph.addRelationship({
|
|
763
|
+
id: generateId('CALLS', `${sourceId}:${langCallSite.calledName}->${resolved.nodeId}`),
|
|
764
|
+
sourceId,
|
|
765
|
+
targetId: resolved.nodeId,
|
|
766
|
+
type: 'CALLS',
|
|
767
|
+
confidence: resolved.confidence,
|
|
768
|
+
reason: resolved.reason,
|
|
769
|
+
});
|
|
770
|
+
if (heritageMap && langCallSite.callForm === 'member' && receiverTypeName) {
|
|
771
|
+
const implTargets = findInterfaceDispatchTargets(langCallSite.calledName, receiverTypeName, file.path, ctx, heritageMap, resolved.nodeId);
|
|
772
|
+
for (const impl of implTargets) {
|
|
773
|
+
graph.addRelationship({
|
|
774
|
+
id: generateId('CALLS', `${sourceId}:${langCallSite.calledName}->${impl.nodeId}`),
|
|
775
|
+
sourceId,
|
|
776
|
+
targetId: impl.nodeId,
|
|
777
|
+
type: 'CALLS',
|
|
778
|
+
confidence: impl.confidence,
|
|
779
|
+
reason: impl.reason,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return;
|
|
780
784
|
}
|
|
781
|
-
return;
|
|
782
785
|
}
|
|
783
786
|
const nameNode = captureMap['call.name'];
|
|
784
787
|
if (!nameNode)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the language-agnostic call extraction pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors method-types.ts / field-types.ts: defines the domain interfaces
|
|
5
|
+
* consumed by createCallExtractor() and the per-language configs.
|
|
6
|
+
*/
|
|
7
|
+
import type { SupportedLanguages } from '../../_shared/index.js';
|
|
8
|
+
import type { SyntaxNode } from './utils/ast-helpers.js';
|
|
9
|
+
import type { MixedChainStep } from './utils/call-analysis.js';
|
|
10
|
+
/**
|
|
11
|
+
* Per-node call extraction result. The parse worker enriches this with
|
|
12
|
+
* file-level context (filePath, sourceId, TypeEnv lookups, arg types) to
|
|
13
|
+
* produce the final `ExtractedCall` that enters the resolution pipeline.
|
|
14
|
+
*/
|
|
15
|
+
export interface ExtractedCallSite {
|
|
16
|
+
calledName: string;
|
|
17
|
+
callForm?: 'free' | 'member' | 'constructor';
|
|
18
|
+
receiverName?: string;
|
|
19
|
+
argCount?: number;
|
|
20
|
+
/** Unified mixed chain for complex receivers (field + call chains). */
|
|
21
|
+
receiverMixedChain?: MixedChainStep[];
|
|
22
|
+
/** When true, the type-as-receiver heuristic applies: if receiverName
|
|
23
|
+
* starts with an uppercase letter and has no TypeEnv binding, treat it
|
|
24
|
+
* as a type name (e.g. Java `User::getName`). */
|
|
25
|
+
typeAsReceiverHeuristic?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface CallExtractor {
|
|
28
|
+
readonly language: SupportedLanguages;
|
|
29
|
+
/**
|
|
30
|
+
* Extract a call site from captured AST nodes.
|
|
31
|
+
*
|
|
32
|
+
* @param callNode The @call capture (call_expression, method_invocation, …)
|
|
33
|
+
* @param callNameNode The @call.name capture (identifier inside the call).
|
|
34
|
+
* May be undefined when the call shape has no name capture
|
|
35
|
+
* (e.g. Java method_reference via `::`).
|
|
36
|
+
* @returns Extracted call site, or null when no call can be derived.
|
|
37
|
+
*/
|
|
38
|
+
extract(callNode: SyntaxNode, callNameNode: SyntaxNode | undefined): ExtractedCallSite | null;
|
|
39
|
+
}
|
|
40
|
+
export interface CallExtractionConfig {
|
|
41
|
+
language: SupportedLanguages;
|
|
42
|
+
/**
|
|
43
|
+
* Language-specific call site extraction. Called **before** the generic
|
|
44
|
+
* path. If it returns non-null, the generic `inferCallForm` /
|
|
45
|
+
* `extractReceiverName` path is skipped entirely.
|
|
46
|
+
*
|
|
47
|
+
* Use this for call shapes that don't follow the standard `@call` /
|
|
48
|
+
* `@call.name` pattern (e.g. Java `method_reference` via `::`).
|
|
49
|
+
*/
|
|
50
|
+
extractLanguageCallSite?: (callNode: SyntaxNode) => ExtractedCallSite | null;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the type-as-receiver heuristic applies for this language.
|
|
53
|
+
* When true and the receiver name starts with an uppercase letter,
|
|
54
|
+
* the receiver is treated as a type name when no TypeEnv binding exists.
|
|
55
|
+
*
|
|
56
|
+
* Applies to JVM and C# languages where `Type.method()` and `Type::method`
|
|
57
|
+
* are common patterns.
|
|
58
|
+
*/
|
|
59
|
+
typeAsReceiverHeuristic?: boolean;
|
|
60
|
+
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { SupportedLanguages, MroStrategy } from '../../_shared/index.js';
|
|
12
12
|
import type { LanguageTypeConfig } from './type-extractors/types.js';
|
|
13
13
|
import type { CallRouter } from './call-routing.js';
|
|
14
|
+
import type { CallExtractor } from './call-types.js';
|
|
14
15
|
import type { ClassExtractor } from './class-types.js';
|
|
15
16
|
import type { ExportChecker } from './export-detection.js';
|
|
16
17
|
import type { FieldExtractor } from './field-extractor.js';
|
|
@@ -119,6 +120,12 @@ interface LanguageProviderConfig {
|
|
|
119
120
|
/** MRO strategy for multiple inheritance resolution.
|
|
120
121
|
* Default: 'first-wins'. */
|
|
121
122
|
readonly mroStrategy?: MroStrategy;
|
|
123
|
+
/** Call extractor for extracting call site information (calledName, callForm,
|
|
124
|
+
* receiverName, argCount, mixed chains) from @call / @call.name captures.
|
|
125
|
+
* Produced by createCallExtractor() with a per-language CallExtractionConfig.
|
|
126
|
+
* Default: undefined — if unset, no calls are extracted for this language.
|
|
127
|
+
* All tree-sitter providers MUST supply this. */
|
|
128
|
+
readonly callExtractor?: CallExtractor;
|
|
122
129
|
/** Field extractor for extracting field/property definitions from class/struct
|
|
123
130
|
* declarations. Produces FieldInfo[] with name, type, visibility, static,
|
|
124
131
|
* readonly metadata. Default: undefined (no field extraction). */
|
|
@@ -30,6 +30,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
30
30
|
import { cConfig as cFieldConfig, cppConfig as cppFieldConfig, } from '../field-extractors/configs/c-cpp.js';
|
|
31
31
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
32
32
|
import { cMethodConfig, cppMethodConfig } from '../method-extractors/configs/c-cpp.js';
|
|
33
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
34
|
+
import { cCallConfig, cppCallConfig } from '../call-extractors/configs/c-cpp.js';
|
|
33
35
|
const C_BUILT_INS = new Set([
|
|
34
36
|
'printf',
|
|
35
37
|
'fprintf',
|
|
@@ -288,6 +290,7 @@ export const cProvider = defineLanguage({
|
|
|
288
290
|
exportChecker: cCppExportChecker,
|
|
289
291
|
importResolver: resolveCImport,
|
|
290
292
|
importSemantics: 'wildcard-transitive',
|
|
293
|
+
callExtractor: createCallExtractor(cCallConfig),
|
|
291
294
|
fieldExtractor: createFieldExtractor(cFieldConfig),
|
|
292
295
|
methodExtractor: createMethodExtractor({
|
|
293
296
|
...cMethodConfig,
|
|
@@ -306,6 +309,7 @@ export const cppProvider = defineLanguage({
|
|
|
306
309
|
importResolver: resolveCppImport,
|
|
307
310
|
importSemantics: 'wildcard-transitive',
|
|
308
311
|
mroStrategy: 'leftmost-base',
|
|
312
|
+
callExtractor: createCallExtractor(cppCallConfig),
|
|
309
313
|
fieldExtractor: createFieldExtractor(cppFieldConfig),
|
|
310
314
|
methodExtractor: createMethodExtractor({
|
|
311
315
|
...cppMethodConfig,
|
|
@@ -14,6 +14,8 @@ import { csharpExportChecker } from '../export-detection.js';
|
|
|
14
14
|
import { resolveCSharpImport } from '../import-resolvers/csharp.js';
|
|
15
15
|
import { extractCSharpNamedBindings } from '../named-bindings/csharp.js';
|
|
16
16
|
import { CSHARP_QUERIES } from '../tree-sitter-queries.js';
|
|
17
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
18
|
+
import { csharpCallConfig } from '../call-extractors/configs/csharp.js';
|
|
17
19
|
import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
18
20
|
import { csharpConfig as csharpFieldConfig } from '../field-extractors/configs/csharp.js';
|
|
19
21
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
@@ -122,6 +124,7 @@ export const csharpProvider = defineLanguage({
|
|
|
122
124
|
namedBindingExtractor: extractCSharpNamedBindings,
|
|
123
125
|
interfaceNamePattern: /^I[A-Z]/,
|
|
124
126
|
mroStrategy: 'implements-split',
|
|
127
|
+
callExtractor: createCallExtractor(csharpCallConfig),
|
|
125
128
|
fieldExtractor: createFieldExtractor(csharpFieldConfig),
|
|
126
129
|
methodExtractor: createMethodExtractor(csharpMethodConfig),
|
|
127
130
|
classExtractor: createClassExtractor(csharpClassConfig),
|
|
@@ -22,6 +22,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
22
22
|
import { dartConfig as dartFieldConfig } from '../field-extractors/configs/dart.js';
|
|
23
23
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
24
24
|
import { dartMethodConfig } from '../method-extractors/configs/dart.js';
|
|
25
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
26
|
+
import { dartCallConfig } from '../call-extractors/configs/dart.js';
|
|
25
27
|
/**
|
|
26
28
|
* Resolve the enclosing function from a `function_body` node by looking at its
|
|
27
29
|
* previous sibling. In Dart's tree-sitter grammar, function_signature and
|
|
@@ -85,6 +87,7 @@ export const dartProvider = defineLanguage({
|
|
|
85
87
|
exportChecker: dartExportChecker,
|
|
86
88
|
importResolver: resolveDartImport,
|
|
87
89
|
importSemantics: 'wildcard-leaf',
|
|
90
|
+
callExtractor: createCallExtractor(dartCallConfig),
|
|
88
91
|
fieldExtractor: createFieldExtractor(dartFieldConfig),
|
|
89
92
|
methodExtractor: createMethodExtractor(dartMethodConfig),
|
|
90
93
|
classExtractor: createClassExtractor(dartClassConfig),
|
|
@@ -20,6 +20,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
20
20
|
import { goConfig as goFieldConfig } from '../field-extractors/configs/go.js';
|
|
21
21
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
22
22
|
import { goMethodConfig } from '../method-extractors/configs/go.js';
|
|
23
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
24
|
+
import { goCallConfig } from '../call-extractors/configs/go.js';
|
|
23
25
|
export const goProvider = defineLanguage({
|
|
24
26
|
id: SupportedLanguages.Go,
|
|
25
27
|
extensions: ['.go'],
|
|
@@ -28,6 +30,7 @@ export const goProvider = defineLanguage({
|
|
|
28
30
|
exportChecker: goExportChecker,
|
|
29
31
|
importResolver: resolveGoImport,
|
|
30
32
|
importSemantics: 'wildcard-leaf',
|
|
33
|
+
callExtractor: createCallExtractor(goCallConfig),
|
|
31
34
|
fieldExtractor: createFieldExtractor(goFieldConfig),
|
|
32
35
|
methodExtractor: createMethodExtractor(goMethodConfig),
|
|
33
36
|
classExtractor: createClassExtractor(goClassConfig),
|
|
@@ -15,6 +15,8 @@ import { javaExportChecker } from '../export-detection.js';
|
|
|
15
15
|
import { resolveJavaImport } from '../import-resolvers/jvm.js';
|
|
16
16
|
import { extractJavaNamedBindings } from '../named-bindings/java.js';
|
|
17
17
|
import { JAVA_QUERIES } from '../tree-sitter-queries.js';
|
|
18
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
19
|
+
import { javaCallConfig } from '../call-extractors/configs/jvm.js';
|
|
18
20
|
import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
19
21
|
import { javaConfig } from '../field-extractors/configs/jvm.js';
|
|
20
22
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
@@ -29,6 +31,7 @@ export const javaProvider = defineLanguage({
|
|
|
29
31
|
namedBindingExtractor: extractJavaNamedBindings,
|
|
30
32
|
interfaceNamePattern: /^I[A-Z]/,
|
|
31
33
|
mroStrategy: 'implements-split',
|
|
34
|
+
callExtractor: createCallExtractor(javaCallConfig),
|
|
32
35
|
fieldExtractor: createFieldExtractor(javaConfig),
|
|
33
36
|
methodExtractor: createMethodExtractor(javaMethodConfig),
|
|
34
37
|
classExtractor: createClassExtractor(javaClassConfig),
|
|
@@ -16,6 +16,8 @@ import { resolveKotlinImport } from '../import-resolvers/jvm.js';
|
|
|
16
16
|
import { extractKotlinNamedBindings } from '../named-bindings/kotlin.js';
|
|
17
17
|
import { appendKotlinWildcard } from '../import-resolvers/jvm.js';
|
|
18
18
|
import { KOTLIN_QUERIES } from '../tree-sitter-queries.js';
|
|
19
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
20
|
+
import { kotlinCallConfig } from '../call-extractors/configs/jvm.js';
|
|
19
21
|
import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
20
22
|
import { kotlinConfig } from '../field-extractors/configs/jvm.js';
|
|
21
23
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
@@ -100,6 +102,7 @@ export const kotlinProvider = defineLanguage({
|
|
|
100
102
|
namedBindingExtractor: extractKotlinNamedBindings,
|
|
101
103
|
importPathPreprocessor: appendKotlinWildcard,
|
|
102
104
|
mroStrategy: 'implements-split',
|
|
105
|
+
callExtractor: createCallExtractor(kotlinCallConfig),
|
|
103
106
|
fieldExtractor: createFieldExtractor(kotlinConfig),
|
|
104
107
|
methodExtractor: createMethodExtractor(kotlinMethodConfig),
|
|
105
108
|
classExtractor: createClassExtractor(kotlinClassConfig),
|
|
@@ -19,6 +19,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
19
19
|
import { phpConfig as phpFieldConfig } from '../field-extractors/configs/php.js';
|
|
20
20
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
21
21
|
import { phpMethodConfig } from '../method-extractors/configs/php.js';
|
|
22
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
23
|
+
import { phpCallConfig } from '../call-extractors/configs/php.js';
|
|
22
24
|
const BUILT_INS = new Set([
|
|
23
25
|
'echo',
|
|
24
26
|
'isset',
|
|
@@ -222,6 +224,7 @@ export const phpProvider = defineLanguage({
|
|
|
222
224
|
exportChecker: phpExportChecker,
|
|
223
225
|
importResolver: resolvePhpImport,
|
|
224
226
|
namedBindingExtractor: extractPhpNamedBindings,
|
|
227
|
+
callExtractor: createCallExtractor(phpCallConfig),
|
|
225
228
|
fieldExtractor: createFieldExtractor(phpFieldConfig),
|
|
226
229
|
methodExtractor: createMethodExtractor(phpMethodConfig),
|
|
227
230
|
classExtractor: createClassExtractor(phpClassConfig),
|
|
@@ -22,6 +22,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
22
22
|
import { pythonConfig as pythonFieldConfig } from '../field-extractors/configs/python.js';
|
|
23
23
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
24
24
|
import { pythonMethodConfig } from '../method-extractors/configs/python.js';
|
|
25
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
26
|
+
import { pythonCallConfig } from '../call-extractors/configs/python.js';
|
|
25
27
|
const BUILT_INS = new Set([
|
|
26
28
|
'print',
|
|
27
29
|
'len',
|
|
@@ -61,6 +63,7 @@ export const pythonProvider = defineLanguage({
|
|
|
61
63
|
namedBindingExtractor: extractPythonNamedBindings,
|
|
62
64
|
importSemantics: 'namespace',
|
|
63
65
|
mroStrategy: 'c3',
|
|
66
|
+
callExtractor: createCallExtractor(pythonCallConfig),
|
|
64
67
|
fieldExtractor: createFieldExtractor(pythonFieldConfig),
|
|
65
68
|
methodExtractor: createMethodExtractor(pythonMethodConfig),
|
|
66
69
|
classExtractor: createClassExtractor(pythonClassConfig),
|
|
@@ -19,6 +19,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
19
19
|
import { rubyConfig as rubyFieldConfig } from '../field-extractors/configs/ruby.js';
|
|
20
20
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
21
21
|
import { rubyMethodConfig } from '../method-extractors/configs/ruby.js';
|
|
22
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
23
|
+
import { rubyCallConfig } from '../call-extractors/configs/ruby.js';
|
|
22
24
|
/** Ruby method/singleton_method: extract name from 'name' field, label as Method. */
|
|
23
25
|
const rubyExtractFunctionName = (node) => {
|
|
24
26
|
if (node.type !== 'method' && node.type !== 'singleton_method')
|
|
@@ -101,6 +103,7 @@ export const rubyProvider = defineLanguage({
|
|
|
101
103
|
importResolver: resolveRubyImport,
|
|
102
104
|
callRouter: routeRubyCall,
|
|
103
105
|
importSemantics: 'wildcard-leaf',
|
|
106
|
+
callExtractor: createCallExtractor(rubyCallConfig),
|
|
104
107
|
resolveEnclosingOwner(node) {
|
|
105
108
|
// Ruby singleton_class (class << self) should resolve to the enclosing
|
|
106
109
|
// class or module for owner/container resolution (HAS_METHOD edges, class IDs).
|
|
@@ -22,6 +22,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
22
22
|
import { rustConfig as rustFieldConfig } from '../field-extractors/configs/rust.js';
|
|
23
23
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
24
24
|
import { rustMethodConfig } from '../method-extractors/configs/rust.js';
|
|
25
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
26
|
+
import { rustCallConfig } from '../call-extractors/configs/rust.js';
|
|
25
27
|
/** Rust impl_item: find the function_item child and extract its name as a Method. */
|
|
26
28
|
const rustExtractFunctionName = (node) => {
|
|
27
29
|
if (node.type !== 'impl_item')
|
|
@@ -113,6 +115,7 @@ export const rustProvider = defineLanguage({
|
|
|
113
115
|
importResolver: resolveRustImport,
|
|
114
116
|
namedBindingExtractor: extractRustNamedBindings,
|
|
115
117
|
mroStrategy: 'qualified-syntax',
|
|
118
|
+
callExtractor: createCallExtractor(rustCallConfig),
|
|
116
119
|
fieldExtractor: createFieldExtractor(rustFieldConfig),
|
|
117
120
|
methodExtractor: createMethodExtractor({
|
|
118
121
|
...rustMethodConfig,
|
|
@@ -21,6 +21,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
21
21
|
import { swiftConfig as swiftFieldConfig } from '../field-extractors/configs/swift.js';
|
|
22
22
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
23
23
|
import { swiftMethodConfig } from '../method-extractors/configs/swift.js';
|
|
24
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
25
|
+
import { swiftCallConfig } from '../call-extractors/configs/swift.js';
|
|
24
26
|
/**
|
|
25
27
|
* Group Swift files by SPM target for implicit module visibility.
|
|
26
28
|
* If SwiftPackageConfig is available, use target -> directory mappings.
|
|
@@ -224,6 +226,7 @@ export const swiftProvider = defineLanguage({
|
|
|
224
226
|
importResolver: resolveSwiftImport,
|
|
225
227
|
importSemantics: 'wildcard-leaf',
|
|
226
228
|
heritageDefaultEdge: 'IMPLEMENTS',
|
|
229
|
+
callExtractor: createCallExtractor(swiftCallConfig),
|
|
227
230
|
fieldExtractor: createFieldExtractor(swiftFieldConfig),
|
|
228
231
|
methodExtractor: createMethodExtractor({
|
|
229
232
|
...swiftMethodConfig,
|
|
@@ -20,6 +20,8 @@ import { createFieldExtractor } from '../field-extractors/generic.js';
|
|
|
20
20
|
import { javascriptConfig } from '../field-extractors/configs/typescript-javascript.js';
|
|
21
21
|
import { createMethodExtractor } from '../method-extractors/generic.js';
|
|
22
22
|
import { typescriptMethodConfig, javascriptMethodConfig, } from '../method-extractors/configs/typescript-javascript.js';
|
|
23
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
24
|
+
import { typescriptCallConfig, javascriptCallConfig, } from '../call-extractors/configs/typescript-javascript.js';
|
|
23
25
|
/**
|
|
24
26
|
* TypeScript/JavaScript: arrow_function and function_expression get their name
|
|
25
27
|
* from the parent variable_declarator (e.g. `const foo = () => {}`).
|
|
@@ -146,6 +148,7 @@ export const typescriptProvider = defineLanguage({
|
|
|
146
148
|
exportChecker: tsExportChecker,
|
|
147
149
|
importResolver: resolveTypescriptImport,
|
|
148
150
|
namedBindingExtractor: extractTsNamedBindings,
|
|
151
|
+
callExtractor: createCallExtractor(typescriptCallConfig),
|
|
149
152
|
fieldExtractor: typescriptFieldExtractor,
|
|
150
153
|
methodExtractor: createMethodExtractor({
|
|
151
154
|
...typescriptMethodConfig,
|
|
@@ -162,6 +165,7 @@ export const javascriptProvider = defineLanguage({
|
|
|
162
165
|
exportChecker: tsExportChecker,
|
|
163
166
|
importResolver: resolveJavascriptImport,
|
|
164
167
|
namedBindingExtractor: extractTsNamedBindings,
|
|
168
|
+
callExtractor: createCallExtractor(javascriptCallConfig),
|
|
165
169
|
fieldExtractor: createFieldExtractor(javascriptConfig),
|
|
166
170
|
methodExtractor: createMethodExtractor({
|
|
167
171
|
...javascriptMethodConfig,
|
|
@@ -21,6 +21,8 @@ import { extractTsNamedBindings } from '../named-bindings/typescript.js';
|
|
|
21
21
|
import { TYPESCRIPT_QUERIES } from '../tree-sitter-queries.js';
|
|
22
22
|
import { typescriptFieldExtractor } from '../field-extractors/typescript.js';
|
|
23
23
|
import { BUILT_INS as TS_BUILT_INS } from './typescript.js';
|
|
24
|
+
import { createCallExtractor } from '../call-extractors/generic.js';
|
|
25
|
+
import { typescriptCallConfig } from '../call-extractors/configs/typescript-javascript.js';
|
|
24
26
|
const VUE_SPECIFIC_BUILT_INS = [
|
|
25
27
|
'ref',
|
|
26
28
|
'reactive',
|
|
@@ -62,6 +64,7 @@ export const vueProvider = defineLanguage({
|
|
|
62
64
|
exportChecker: tsExportChecker,
|
|
63
65
|
importResolver: resolveVueImport,
|
|
64
66
|
namedBindingExtractor: extractTsNamedBindings,
|
|
67
|
+
callExtractor: createCallExtractor(typescriptCallConfig),
|
|
65
68
|
fieldExtractor: typescriptFieldExtractor,
|
|
66
69
|
classExtractor: vueClassExtractor,
|
|
67
70
|
builtInNames: VUE_BUILT_INS,
|
|
@@ -36,8 +36,7 @@ try {
|
|
|
36
36
|
catch { }
|
|
37
37
|
import { getLanguageFromFilename } from '../../../_shared/index.js';
|
|
38
38
|
import { FUNCTION_NODE_TYPES, getDefinitionNodeFromCaptures, findEnclosingClassInfo, getLabelFromCaptures, findDescendant, extractStringContent, genericFuncName, inferFunctionLabel, CLASS_CONTAINER_TYPES, } from '../utils/ast-helpers.js';
|
|
39
|
-
import {
|
|
40
|
-
import { extractParsedCallSite } from '../call-sites/extract-language-call-site.js';
|
|
39
|
+
import { extractCallArgTypes } from '../utils/call-analysis.js';
|
|
41
40
|
import { buildTypeEnv } from '../type-env.js';
|
|
42
41
|
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
43
42
|
import { generateId } from '../../../lib/utils.js';
|
|
@@ -1248,98 +1247,125 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1248
1247
|
}
|
|
1249
1248
|
// Extract call sites
|
|
1250
1249
|
if (captureMap['call']) {
|
|
1251
|
-
const
|
|
1252
|
-
const languageSeed = extractParsedCallSite(language, callNode0);
|
|
1253
|
-
if (languageSeed) {
|
|
1254
|
-
if (!provider.isBuiltInName(languageSeed.calledName)) {
|
|
1255
|
-
const sourceId = findEnclosingFunctionId(callNode0, file.path, provider) ||
|
|
1256
|
-
generateId('File', file.path);
|
|
1257
|
-
const receiverName = languageSeed.callForm === 'member' ? languageSeed.receiverName : undefined;
|
|
1258
|
-
let receiverTypeName = receiverName
|
|
1259
|
-
? typeEnv.lookup(receiverName, callNode0)
|
|
1260
|
-
: undefined;
|
|
1261
|
-
// Type-as-receiver (e.g. Java `User::getName`): no TypeEnv binding for the class name
|
|
1262
|
-
if (receiverName !== undefined &&
|
|
1263
|
-
receiverTypeName === undefined &&
|
|
1264
|
-
languageSeed.callForm === 'member' &&
|
|
1265
|
-
(language === SupportedLanguages.Java ||
|
|
1266
|
-
language === SupportedLanguages.CSharp ||
|
|
1267
|
-
language === SupportedLanguages.Kotlin)) {
|
|
1268
|
-
const c0 = receiverName.charCodeAt(0);
|
|
1269
|
-
if (c0 >= 65 && c0 <= 90)
|
|
1270
|
-
receiverTypeName = receiverName;
|
|
1271
|
-
}
|
|
1272
|
-
result.calls.push({
|
|
1273
|
-
filePath: file.path,
|
|
1274
|
-
calledName: languageSeed.calledName,
|
|
1275
|
-
sourceId,
|
|
1276
|
-
callForm: languageSeed.callForm,
|
|
1277
|
-
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1278
|
-
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1279
|
-
});
|
|
1280
|
-
}
|
|
1281
|
-
continue;
|
|
1282
|
-
}
|
|
1250
|
+
const callNode = captureMap['call'];
|
|
1283
1251
|
const callNameNode = captureMap['call.name'];
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
//
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1252
|
+
const callExtractor = provider.callExtractor;
|
|
1253
|
+
if (callExtractor) {
|
|
1254
|
+
// ── Path 1: Language-specific call site (bypasses routing) ────
|
|
1255
|
+
// Try language-specific extraction (e.g. Java `::` method references)
|
|
1256
|
+
// without callNameNode. If successful, skip routing and the generic
|
|
1257
|
+
// path entirely.
|
|
1258
|
+
const langCallSite = callExtractor.extract(callNode, undefined);
|
|
1259
|
+
if (langCallSite) {
|
|
1260
|
+
if (!provider.isBuiltInName(langCallSite.calledName)) {
|
|
1261
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1262
|
+
generateId('File', file.path);
|
|
1263
|
+
const receiverName = langCallSite.callForm === 'member' ? langCallSite.receiverName : undefined;
|
|
1264
|
+
let receiverTypeName = receiverName
|
|
1265
|
+
? typeEnv.lookup(receiverName, callNode)
|
|
1266
|
+
: undefined;
|
|
1267
|
+
// Type-as-receiver heuristic (e.g. Java `User::getName`)
|
|
1268
|
+
if (langCallSite.typeAsReceiverHeuristic &&
|
|
1269
|
+
receiverName !== undefined &&
|
|
1270
|
+
receiverTypeName === undefined &&
|
|
1271
|
+
langCallSite.callForm === 'member') {
|
|
1272
|
+
const c0 = receiverName.charCodeAt(0);
|
|
1273
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1274
|
+
receiverTypeName = receiverName;
|
|
1275
|
+
}
|
|
1276
|
+
result.calls.push({
|
|
1293
1277
|
filePath: file.path,
|
|
1294
|
-
|
|
1295
|
-
|
|
1278
|
+
calledName: langCallSite.calledName,
|
|
1279
|
+
sourceId,
|
|
1280
|
+
callForm: langCallSite.callForm,
|
|
1281
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1282
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1296
1283
|
});
|
|
1297
|
-
continue;
|
|
1298
1284
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
// ── Path 2: Generic extraction via @call.name ────────────────
|
|
1288
|
+
if (callNameNode) {
|
|
1289
|
+
const calledName = callNameNode.text;
|
|
1290
|
+
// Dispatch: route language-specific calls (heritage, properties, imports)
|
|
1291
|
+
const routed = callRouter?.(calledName, captureMap['call']);
|
|
1292
|
+
if (routed) {
|
|
1293
|
+
if (routed.kind === 'skip')
|
|
1294
|
+
continue;
|
|
1295
|
+
if (routed.kind === 'import') {
|
|
1296
|
+
result.imports.push({
|
|
1302
1297
|
filePath: file.path,
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
kind: item.heritageKind,
|
|
1298
|
+
rawImportPath: routed.importPath,
|
|
1299
|
+
language,
|
|
1306
1300
|
});
|
|
1301
|
+
continue;
|
|
1307
1302
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
const propEnclosingInfo = cachedFindEnclosingClassInfo(captureMap['call'], file.path, provider.resolveEnclosingOwner);
|
|
1312
|
-
const propEnclosingClassId = propEnclosingInfo?.classId ?? null;
|
|
1313
|
-
// Enrich routed properties with FieldExtractor metadata
|
|
1314
|
-
let routedFieldMap;
|
|
1315
|
-
if (provider.fieldExtractor && typeEnv) {
|
|
1316
|
-
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
1317
|
-
if (classNode) {
|
|
1318
|
-
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
1319
|
-
typeEnv,
|
|
1320
|
-
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1303
|
+
if (routed.kind === 'heritage') {
|
|
1304
|
+
for (const item of routed.items) {
|
|
1305
|
+
result.heritage.push({
|
|
1321
1306
|
filePath: file.path,
|
|
1322
|
-
|
|
1307
|
+
className: item.enclosingClass,
|
|
1308
|
+
parentName: item.mixinName,
|
|
1309
|
+
kind: item.heritageKind,
|
|
1323
1310
|
});
|
|
1324
1311
|
}
|
|
1312
|
+
continue;
|
|
1325
1313
|
}
|
|
1326
|
-
|
|
1327
|
-
const
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1314
|
+
if (routed.kind === 'properties') {
|
|
1315
|
+
const propEnclosingInfo = cachedFindEnclosingClassInfo(captureMap['call'], file.path, provider.resolveEnclosingOwner);
|
|
1316
|
+
const propEnclosingClassId = propEnclosingInfo?.classId ?? null;
|
|
1317
|
+
// Enrich routed properties with FieldExtractor metadata
|
|
1318
|
+
let routedFieldMap;
|
|
1319
|
+
if (provider.fieldExtractor && typeEnv) {
|
|
1320
|
+
const classNode = findEnclosingClassNode(captureMap['call']);
|
|
1321
|
+
if (classNode) {
|
|
1322
|
+
routedFieldMap = getFieldInfo(classNode, provider, {
|
|
1323
|
+
typeEnv,
|
|
1324
|
+
symbolTable: NOOP_SYMBOL_TABLE,
|
|
1325
|
+
filePath: file.path,
|
|
1326
|
+
language,
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
for (const item of routed.items) {
|
|
1331
|
+
const routedFieldInfo = routedFieldMap?.get(item.propName);
|
|
1332
|
+
const propQualifiedName = propEnclosingInfo
|
|
1333
|
+
? `${propEnclosingInfo.className}.${item.propName}`
|
|
1334
|
+
: item.propName;
|
|
1335
|
+
const nodeId = generateId('Property', `${file.path}:${propQualifiedName}`);
|
|
1336
|
+
result.nodes.push({
|
|
1337
|
+
id: nodeId,
|
|
1338
|
+
label: 'Property',
|
|
1339
|
+
properties: {
|
|
1340
|
+
name: item.propName,
|
|
1341
|
+
filePath: file.path,
|
|
1342
|
+
startLine: item.startLine,
|
|
1343
|
+
endLine: item.endLine,
|
|
1344
|
+
language,
|
|
1345
|
+
isExported: true,
|
|
1346
|
+
description: item.accessorType,
|
|
1347
|
+
...(item.declaredType
|
|
1348
|
+
? { declaredType: item.declaredType }
|
|
1349
|
+
: routedFieldInfo?.type
|
|
1350
|
+
? { declaredType: routedFieldInfo.type }
|
|
1351
|
+
: {}),
|
|
1352
|
+
...(routedFieldInfo?.visibility !== undefined
|
|
1353
|
+
? { visibility: routedFieldInfo.visibility }
|
|
1354
|
+
: {}),
|
|
1355
|
+
...(routedFieldInfo?.isStatic !== undefined
|
|
1356
|
+
? { isStatic: routedFieldInfo.isStatic }
|
|
1357
|
+
: {}),
|
|
1358
|
+
...(routedFieldInfo?.isReadonly !== undefined
|
|
1359
|
+
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1360
|
+
: {}),
|
|
1361
|
+
},
|
|
1362
|
+
});
|
|
1363
|
+
result.symbols.push({
|
|
1337
1364
|
filePath: file.path,
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
description: item.accessorType,
|
|
1365
|
+
name: item.propName,
|
|
1366
|
+
nodeId,
|
|
1367
|
+
type: 'Property',
|
|
1368
|
+
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
1343
1369
|
...(item.declaredType
|
|
1344
1370
|
? { declaredType: item.declaredType }
|
|
1345
1371
|
: routedFieldInfo?.type
|
|
@@ -1354,101 +1380,71 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1354
1380
|
...(routedFieldInfo?.isReadonly !== undefined
|
|
1355
1381
|
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1356
1382
|
: {}),
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
filePath: file.path,
|
|
1361
|
-
name: item.propName,
|
|
1362
|
-
nodeId,
|
|
1363
|
-
type: 'Property',
|
|
1364
|
-
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
1365
|
-
...(item.declaredType
|
|
1366
|
-
? { declaredType: item.declaredType }
|
|
1367
|
-
: routedFieldInfo?.type
|
|
1368
|
-
? { declaredType: routedFieldInfo.type }
|
|
1369
|
-
: {}),
|
|
1370
|
-
...(routedFieldInfo?.visibility !== undefined
|
|
1371
|
-
? { visibility: routedFieldInfo.visibility }
|
|
1372
|
-
: {}),
|
|
1373
|
-
...(routedFieldInfo?.isStatic !== undefined
|
|
1374
|
-
? { isStatic: routedFieldInfo.isStatic }
|
|
1375
|
-
: {}),
|
|
1376
|
-
...(routedFieldInfo?.isReadonly !== undefined
|
|
1377
|
-
? { isReadonly: routedFieldInfo.isReadonly }
|
|
1378
|
-
: {}),
|
|
1379
|
-
});
|
|
1380
|
-
const fileId = generateId('File', file.path);
|
|
1381
|
-
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
1382
|
-
result.relationships.push({
|
|
1383
|
-
id: relId,
|
|
1384
|
-
sourceId: fileId,
|
|
1385
|
-
targetId: nodeId,
|
|
1386
|
-
type: 'DEFINES',
|
|
1387
|
-
confidence: 1.0,
|
|
1388
|
-
reason: '',
|
|
1389
|
-
});
|
|
1390
|
-
if (propEnclosingClassId) {
|
|
1383
|
+
});
|
|
1384
|
+
const fileId = generateId('File', file.path);
|
|
1385
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
1391
1386
|
result.relationships.push({
|
|
1392
|
-
id:
|
|
1393
|
-
sourceId:
|
|
1387
|
+
id: relId,
|
|
1388
|
+
sourceId: fileId,
|
|
1394
1389
|
targetId: nodeId,
|
|
1395
|
-
type: '
|
|
1390
|
+
type: 'DEFINES',
|
|
1396
1391
|
confidence: 1.0,
|
|
1397
1392
|
reason: '',
|
|
1398
1393
|
});
|
|
1394
|
+
if (propEnclosingClassId) {
|
|
1395
|
+
result.relationships.push({
|
|
1396
|
+
id: generateId('HAS_PROPERTY', `${propEnclosingClassId}->${nodeId}`),
|
|
1397
|
+
sourceId: propEnclosingClassId,
|
|
1398
|
+
targetId: nodeId,
|
|
1399
|
+
type: 'HAS_PROPERTY',
|
|
1400
|
+
confidence: 1.0,
|
|
1401
|
+
reason: '',
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1399
1404
|
}
|
|
1405
|
+
continue;
|
|
1400
1406
|
}
|
|
1401
|
-
|
|
1407
|
+
// kind === 'call' — fall through to normal call processing below
|
|
1402
1408
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
const receiverNode = extractReceiverNode(callNameNode);
|
|
1420
|
-
if (receiverNode) {
|
|
1421
|
-
const extracted = extractMixedChain(receiverNode);
|
|
1422
|
-
if (extracted && extracted.chain.length > 0) {
|
|
1423
|
-
receiverMixedChain = extracted.chain;
|
|
1424
|
-
receiverName = extracted.baseReceiverName;
|
|
1425
|
-
// Try the type environment immediately for the base receiver
|
|
1426
|
-
// (covers explicitly-typed locals and annotated parameters).
|
|
1427
|
-
if (receiverName) {
|
|
1428
|
-
receiverTypeName = typeEnv.lookup(receiverName, callNode);
|
|
1429
|
-
}
|
|
1409
|
+
if (!provider.isBuiltInName(calledName)) {
|
|
1410
|
+
const callSite = callExtractor.extract(callNode, callNameNode);
|
|
1411
|
+
if (callSite) {
|
|
1412
|
+
const sourceId = findEnclosingFunctionId(callNode, file.path, provider) ||
|
|
1413
|
+
generateId('File', file.path);
|
|
1414
|
+
let receiverTypeName = callSite.receiverName
|
|
1415
|
+
? typeEnv.lookup(callSite.receiverName, callNode)
|
|
1416
|
+
: undefined;
|
|
1417
|
+
// Type-as-receiver heuristic
|
|
1418
|
+
if (callSite.typeAsReceiverHeuristic &&
|
|
1419
|
+
callSite.receiverName !== undefined &&
|
|
1420
|
+
receiverTypeName === undefined &&
|
|
1421
|
+
callSite.callForm === 'member') {
|
|
1422
|
+
const c0 = callSite.receiverName.charCodeAt(0);
|
|
1423
|
+
if (c0 >= 65 && c0 <= 90)
|
|
1424
|
+
receiverTypeName = callSite.receiverName;
|
|
1430
1425
|
}
|
|
1426
|
+
const inferLiteralType = provider.typeConfig?.inferLiteralType;
|
|
1427
|
+
// Skip when no arg list / zero args: nothing to infer for overload typing
|
|
1428
|
+
const argTypes = inferLiteralType && callSite.argCount !== undefined && callSite.argCount > 0
|
|
1429
|
+
? extractCallArgTypes(callNode, inferLiteralType, (varName, cn) => typeEnv.lookup(varName, cn))
|
|
1430
|
+
: undefined;
|
|
1431
|
+
result.calls.push({
|
|
1432
|
+
filePath: file.path,
|
|
1433
|
+
calledName: callSite.calledName,
|
|
1434
|
+
sourceId,
|
|
1435
|
+
...(callSite.argCount !== undefined ? { argCount: callSite.argCount } : {}),
|
|
1436
|
+
...(callSite.callForm !== undefined ? { callForm: callSite.callForm } : {}),
|
|
1437
|
+
...(callSite.receiverName !== undefined
|
|
1438
|
+
? { receiverName: callSite.receiverName }
|
|
1439
|
+
: {}),
|
|
1440
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1441
|
+
...(callSite.receiverMixedChain !== undefined
|
|
1442
|
+
? { receiverMixedChain: callSite.receiverMixedChain }
|
|
1443
|
+
: {}),
|
|
1444
|
+
...(argTypes !== undefined ? { argTypes } : {}),
|
|
1445
|
+
});
|
|
1431
1446
|
}
|
|
1432
1447
|
}
|
|
1433
|
-
const inferLiteralType = provider.typeConfig?.inferLiteralType;
|
|
1434
|
-
const argCountForOverloadHints = countCallArguments(callNode);
|
|
1435
|
-
// Skip when no arg list / zero args: nothing to infer for overload typing; saves AST walks + payload size.
|
|
1436
|
-
const argTypes = inferLiteralType &&
|
|
1437
|
-
argCountForOverloadHints !== undefined &&
|
|
1438
|
-
argCountForOverloadHints > 0
|
|
1439
|
-
? extractCallArgTypes(callNode, inferLiteralType, (varName, cn) => typeEnv.lookup(varName, cn))
|
|
1440
|
-
: undefined;
|
|
1441
|
-
result.calls.push({
|
|
1442
|
-
filePath: file.path,
|
|
1443
|
-
calledName,
|
|
1444
|
-
sourceId,
|
|
1445
|
-
argCount: countCallArguments(callNode),
|
|
1446
|
-
...(callForm !== undefined ? { callForm } : {}),
|
|
1447
|
-
...(receiverName !== undefined ? { receiverName } : {}),
|
|
1448
|
-
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
1449
|
-
...(receiverMixedChain !== undefined ? { receiverMixedChain } : {}),
|
|
1450
|
-
...(argTypes !== undefined ? { argTypes } : {}),
|
|
1451
|
-
});
|
|
1452
1448
|
}
|
|
1453
1449
|
}
|
|
1454
1450
|
continue;
|
package/package.json
CHANGED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/** Non-generic @call shapes → { calledName, callForm, receiverName? } (used from call-processor / parse-worker). */
|
|
2
|
-
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
3
|
-
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
|
4
|
-
export type ParsedCallSite = {
|
|
5
|
-
calledName: string;
|
|
6
|
-
callForm: 'free' | 'member' | 'constructor';
|
|
7
|
-
receiverName?: string;
|
|
8
|
-
};
|
|
9
|
-
/** Non-null → seed replaces @call.name; null → use @call.name + inferCallForm / extractReceiverName. */
|
|
10
|
-
export declare function extractParsedCallSite(language: SupportedLanguages, callNode: SyntaxNode): ParsedCallSite | null;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** Non-generic @call shapes → { calledName, callForm, receiverName? } (used from call-processor / parse-worker). */
|
|
2
|
-
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
3
|
-
import { parseJavaMethodReference } from './java.js';
|
|
4
|
-
/** Non-null → seed replaces @call.name; null → use @call.name + inferCallForm / extractReceiverName. */
|
|
5
|
-
export function extractParsedCallSite(language, callNode) {
|
|
6
|
-
switch (language) {
|
|
7
|
-
case SupportedLanguages.Java:
|
|
8
|
-
if (callNode.type === 'method_reference') {
|
|
9
|
-
const parsed = parseJavaMethodReference(callNode);
|
|
10
|
-
if (!parsed)
|
|
11
|
-
return null;
|
|
12
|
-
return {
|
|
13
|
-
calledName: parsed.calledName,
|
|
14
|
-
callForm: parsed.callForm,
|
|
15
|
-
...(parsed.receiverName !== undefined ? { receiverName: parsed.receiverName } : {}),
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
return null;
|
|
19
|
-
default:
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/** Java `method_reference` (`::`) nodes (tree-sitter-java). `super::` still lacks TypeEnv receiver typing. */
|
|
2
|
-
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
|
3
|
-
export type ParsedJavaMethodReference = {
|
|
4
|
-
calledName: string;
|
|
5
|
-
callForm: 'member' | 'constructor';
|
|
6
|
-
receiverName?: string;
|
|
7
|
-
};
|
|
8
|
-
/** Parse `expr::method`, `Type::new`, `this::m`, `super::m`. */
|
|
9
|
-
export declare const parseJavaMethodReference: (callNode: SyntaxNode) => ParsedJavaMethodReference | null;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/** Java `method_reference` (`::`) nodes (tree-sitter-java). `super::` still lacks TypeEnv receiver typing. */
|
|
2
|
-
/** Parse `expr::method`, `Type::new`, `this::m`, `super::m`. */
|
|
3
|
-
export const parseJavaMethodReference = (callNode) => {
|
|
4
|
-
if (callNode.type !== 'method_reference')
|
|
5
|
-
return null;
|
|
6
|
-
const recv = callNode.namedChild(0);
|
|
7
|
-
if (!recv)
|
|
8
|
-
return null;
|
|
9
|
-
for (const c of callNode.children) {
|
|
10
|
-
if (c.type === 'new') {
|
|
11
|
-
if (recv.type !== 'identifier')
|
|
12
|
-
return null;
|
|
13
|
-
return { calledName: recv.text, callForm: 'constructor' };
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const rhs = callNode.child(callNode.childCount - 1);
|
|
17
|
-
if (!rhs || rhs.type !== 'identifier')
|
|
18
|
-
return null;
|
|
19
|
-
const methodName = rhs.text;
|
|
20
|
-
if (recv.type === 'identifier') {
|
|
21
|
-
return { calledName: methodName, callForm: 'member', receiverName: recv.text };
|
|
22
|
-
}
|
|
23
|
-
if (recv.type === 'this') {
|
|
24
|
-
return { calledName: methodName, callForm: 'member', receiverName: 'this' };
|
|
25
|
-
}
|
|
26
|
-
if (recv.type === 'super') {
|
|
27
|
-
return { calledName: methodName, callForm: 'member', receiverName: 'super' };
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
};
|