mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
refactor: provider-routed function name extraction + remove countMethodParameters
Make ast-helpers.ts language-agnostic by moving all language-specific logic into per-language providers: extractFunctionName: - Add optional extractFunctionName hook to LanguageProvider interface - Move C/C++ declarator unwrapping to c-cpp.ts provider - Move Swift init/deinit to swift.ts provider - Move Rust impl_item to rust.ts provider - Move TS arrow_function/function_expression to typescript.ts provider - Move Ruby method/singleton_method to ruby.ts provider - Replace 250-line monolithic switch with thin dispatcher + generic fallback (name field lookup + label inference from node type) - Callers with provider available (parse-worker, call-processor) pass it for language-specific resolution; callers without (type-env) use generic fallback countMethodParameters: - Remove entirely from ast-helpers.ts (90 lines + PARAM_LIST_TYPES, VARIADIC_PARAM_TYPES constants) - Replace 4 call sites with MethodExtractor-based arity via getMethodInfo (parse-worker) / extractFromNode (call-processor) - When MethodExtractor doesn't produce arity, no #<N> suffix is appended (matches definition phase behavior) ast-helpers.ts: 897 → 518 lines (-42%)
This commit is contained in:
parent
f309328372
commit
16f752413a
10 changed files with 380 additions and 367 deletions
|
|
@ -13,7 +13,6 @@ import { yieldToEventLoop } from './utils/event-loop.js';
|
|||
import {
|
||||
FUNCTION_NODE_TYPES,
|
||||
extractFunctionName,
|
||||
countMethodParameters,
|
||||
findEnclosingClassId,
|
||||
findEnclosingClassInfo,
|
||||
} from './utils/ast-helpers.js';
|
||||
|
|
@ -235,7 +234,7 @@ const findEnclosingFunction = (
|
|||
|
||||
while (current) {
|
||||
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
||||
const { funcName, label } = extractFunctionName(current);
|
||||
const { funcName, label } = extractFunctionName(current, provider);
|
||||
|
||||
if (funcName) {
|
||||
const resolved = ctx.resolve(funcName, filePath);
|
||||
|
|
@ -265,9 +264,18 @@ const findEnclosingFunction = (
|
|||
}
|
||||
const classInfo = findEnclosingClassInfo(current, filePath);
|
||||
const qualifiedName = classInfo ? `${classInfo.className}.${funcName}` : funcName;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs
|
||||
const needsArity = finalLabel === 'Method' || finalLabel === 'Constructor';
|
||||
const arity = needsArity ? countMethodParameters(current) : undefined;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
||||
// Use provider.methodExtractor.extractFromNode — same extractor as definition phase.
|
||||
let arity: number | undefined;
|
||||
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
||||
const language = getLanguageFromFilename(filePath);
|
||||
const info = language
|
||||
? provider.methodExtractor?.extractFromNode?.(current, { filePath, language })
|
||||
: undefined;
|
||||
if (info) {
|
||||
arity = info.parameters.some((p) => p.isVariadic) ? undefined : info.parameters.length;
|
||||
}
|
||||
}
|
||||
const arityTag = arity !== undefined ? `#${arity}` : '';
|
||||
return generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
|
||||
}
|
||||
|
|
@ -304,10 +312,18 @@ const findEnclosingFunction = (
|
|||
const qualifiedName = classInfo
|
||||
? `${classInfo.className}.${customResult.funcName}`
|
||||
: customResult.funcName;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
||||
const sigNode = current.previousSibling ?? current;
|
||||
const needsArity2 = finalLabel === 'Method' || finalLabel === 'Constructor';
|
||||
const arity2 = needsArity2 ? countMethodParameters(sigNode) : undefined;
|
||||
let arity2: number | undefined;
|
||||
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
||||
const language = getLanguageFromFilename(filePath);
|
||||
const info = language
|
||||
? provider.methodExtractor?.extractFromNode?.(sigNode, { filePath, language })
|
||||
: undefined;
|
||||
if (info) {
|
||||
arity2 = info.parameters.some((p) => p.isVariadic) ? undefined : info.parameters.length;
|
||||
}
|
||||
}
|
||||
const arityTag2 = arity2 !== undefined ? `#${arity2}` : '';
|
||||
return generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
|
||||
}
|
||||
|
|
@ -850,7 +866,7 @@ export const processCalls = async (
|
|||
let p = callNode.parent;
|
||||
while (p) {
|
||||
if (FUNCTION_NODE_TYPES.has(p.type)) {
|
||||
const { funcName } = extractFunctionName(p);
|
||||
const { funcName } = extractFunctionName(p, provider);
|
||||
if (funcName) {
|
||||
scope = `${funcName}@${p.startIndex}`;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -104,6 +104,17 @@ interface LanguageProviderConfig {
|
|||
ancestorNode: SyntaxNode,
|
||||
) => { funcName: string; label: NodeLabel } | null;
|
||||
|
||||
// ── Function name extraction ──────────────────────────────────────
|
||||
/** Extract function name + label from an AST node during parent-walk.
|
||||
* Languages with non-standard AST structures (e.g. C/C++ declarator
|
||||
* unwrapping, Swift init/deinit, Rust impl_item) provide this hook
|
||||
* to replace the generic name-field lookup.
|
||||
* Return null to fall through to the generic extractor.
|
||||
* Default: undefined (generic extraction from 'name' field). */
|
||||
readonly extractFunctionName?: (
|
||||
node: SyntaxNode,
|
||||
) => { funcName: string | null; label: NodeLabel } | null;
|
||||
|
||||
// ── Labels ────────────────────────────────────────────────────────
|
||||
/** Override the default node label for definition.function captures.
|
||||
* Return null to skip (C/C++ duplicate), a different label to reclassify
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ import { cCppExportChecker } from '../export-detection.js';
|
|||
import { resolveCImport, resolveCppImport } from '../import-resolvers/standard.js';
|
||||
import { C_QUERIES, CPP_QUERIES } from '../tree-sitter-queries.js';
|
||||
|
||||
import { isCppInsideClassOrStruct } from '../utils/ast-helpers.js';
|
||||
import { isCppInsideClassOrStruct, FUNCTION_DECLARATION_TYPES } from '../utils/ast-helpers.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import type { NodeLabel } from 'gitnexus-shared';
|
||||
import type { LanguageProvider } from '../language-provider.js';
|
||||
import { createFieldExtractor } from '../field-extractors/generic.js';
|
||||
import {
|
||||
|
|
@ -132,6 +134,142 @@ const C_BUILT_INS: ReadonlySet<string> = new Set([
|
|||
'put',
|
||||
]);
|
||||
|
||||
/**
|
||||
* C/C++ function name extraction — unwraps pointer_declarator / reference_declarator /
|
||||
* function_declarator / qualified_identifier chains to find the actual function name.
|
||||
* Handles field_identifier (method inside class body) and parenthesized_declarator.
|
||||
*/
|
||||
const cCppExtractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
): { funcName: string | null; label: NodeLabel } | null => {
|
||||
if (!FUNCTION_DECLARATION_TYPES.has(node.type)) return null;
|
||||
|
||||
let funcName: string | null = null;
|
||||
let label: NodeLabel = 'Function';
|
||||
|
||||
// C/C++: function_definition -> [pointer_declarator ->] function_declarator -> qualified_identifier/identifier
|
||||
// Unwrap pointer_declarator / reference_declarator wrappers to reach function_declarator
|
||||
let declarator = node.childForFieldName?.('declarator');
|
||||
if (!declarator) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'function_declarator') {
|
||||
declarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (
|
||||
declarator &&
|
||||
(declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator')
|
||||
) {
|
||||
let nextDeclarator = declarator.childForFieldName?.('declarator');
|
||||
if (!nextDeclarator) {
|
||||
for (let i = 0; i < declarator.childCount; i++) {
|
||||
const c = declarator.child(i);
|
||||
if (
|
||||
c?.type === 'function_declarator' ||
|
||||
c?.type === 'pointer_declarator' ||
|
||||
c?.type === 'reference_declarator'
|
||||
) {
|
||||
nextDeclarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
declarator = nextDeclarator;
|
||||
}
|
||||
if (declarator) {
|
||||
let innerDeclarator = declarator.childForFieldName?.('declarator');
|
||||
if (!innerDeclarator) {
|
||||
for (let i = 0; i < declarator.childCount; i++) {
|
||||
const c = declarator.child(i);
|
||||
if (
|
||||
c?.type === 'qualified_identifier' ||
|
||||
c?.type === 'identifier' ||
|
||||
c?.type === 'field_identifier' ||
|
||||
c?.type === 'parenthesized_declarator'
|
||||
) {
|
||||
innerDeclarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (innerDeclarator?.type === 'qualified_identifier') {
|
||||
let nameNode = innerDeclarator.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
||||
const c = innerDeclarator.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameNode?.text) {
|
||||
funcName = nameNode.text;
|
||||
label = 'Method';
|
||||
}
|
||||
} else if (
|
||||
innerDeclarator?.type === 'identifier' ||
|
||||
innerDeclarator?.type === 'field_identifier'
|
||||
) {
|
||||
// field_identifier is used for method names inside C++ class bodies
|
||||
funcName = innerDeclarator.text;
|
||||
if (innerDeclarator.type === 'field_identifier') label = 'Method';
|
||||
} else if (innerDeclarator?.type === 'parenthesized_declarator') {
|
||||
let nestedId: SyntaxNode | null = null;
|
||||
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
||||
const c = innerDeclarator.child(i);
|
||||
if (c?.type === 'qualified_identifier' || c?.type === 'identifier') {
|
||||
nestedId = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nestedId?.type === 'qualified_identifier') {
|
||||
let nameNode = nestedId.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < nestedId.childCount; i++) {
|
||||
const c = nestedId.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameNode?.text) {
|
||||
funcName = nameNode.text;
|
||||
label = 'Method';
|
||||
}
|
||||
} else if (nestedId?.type === 'identifier') {
|
||||
funcName = nestedId.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for other node types in FUNCTION_DECLARATION_TYPES (e.g. function_item for Rust in C++ tree)
|
||||
if (!funcName) {
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (
|
||||
c?.type === 'identifier' ||
|
||||
c?.type === 'property_identifier' ||
|
||||
c?.type === 'simple_identifier'
|
||||
) {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text ?? null;
|
||||
}
|
||||
|
||||
return { funcName, label };
|
||||
};
|
||||
|
||||
/** Label override shared by C and C++: skip function_definition captures inside class/struct
|
||||
* bodies (they're duplicates of definition.method captures). */
|
||||
const cppLabelOverride: NonNullable<LanguageProvider['labelOverride']> = (
|
||||
|
|
@ -152,6 +290,7 @@ export const cProvider = defineLanguage({
|
|||
importSemantics: 'wildcard',
|
||||
fieldExtractor: createFieldExtractor(cFieldConfig),
|
||||
methodExtractor: createMethodExtractor(cMethodConfig),
|
||||
extractFunctionName: cCppExtractFunctionName,
|
||||
labelOverride: cppLabelOverride,
|
||||
builtInNames: C_BUILT_INS,
|
||||
});
|
||||
|
|
@ -167,6 +306,7 @@ export const cppProvider = defineLanguage({
|
|||
mroStrategy: 'leftmost-base',
|
||||
fieldExtractor: createFieldExtractor(cppFieldConfig),
|
||||
methodExtractor: createMethodExtractor(cppMethodConfig),
|
||||
extractFunctionName: cCppExtractFunctionName,
|
||||
labelOverride: cppLabelOverride,
|
||||
builtInNames: C_BUILT_INS,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { NodeLabel } from 'gitnexus-shared';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import { typeConfig as rubyConfig } from '../type-extractors/ruby.js';
|
||||
import { routeRubyCall } from '../call-routing.js';
|
||||
import { rubyExportChecker } from '../export-detection.js';
|
||||
|
|
@ -19,6 +21,25 @@ import { rubyConfig as rubyFieldConfig } from '../field-extractors/configs/ruby.
|
|||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
import { rubyMethodConfig } from '../method-extractors/configs/ruby.js';
|
||||
|
||||
/** Ruby method/singleton_method: extract name from 'name' field, label as Method. */
|
||||
const rubyExtractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
): { funcName: string | null; label: NodeLabel } | null => {
|
||||
if (node.type !== 'method' && node.type !== 'singleton_method') return null;
|
||||
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { funcName: nameNode?.text ?? null, label: 'Method' };
|
||||
};
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'puts',
|
||||
'p',
|
||||
|
|
@ -88,5 +109,6 @@ export const rubyProvider = defineLanguage({
|
|||
importSemantics: 'wildcard',
|
||||
fieldExtractor: createFieldExtractor(rubyFieldConfig),
|
||||
methodExtractor: createMethodExtractor(rubyMethodConfig),
|
||||
extractFunctionName: rubyExtractFunctionName,
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { NodeLabel } from 'gitnexus-shared';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import { typeConfig as rustConfig } from '../type-extractors/rust.js';
|
||||
import { rustExportChecker } from '../export-detection.js';
|
||||
import { resolveRustImport } from '../import-resolvers/rust.js';
|
||||
|
|
@ -22,6 +24,35 @@ import { rustConfig as rustFieldConfig } from '../field-extractors/configs/rust.
|
|||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
import { rustMethodConfig } from '../method-extractors/configs/rust.js';
|
||||
|
||||
/** Rust impl_item: find the function_item child and extract its name as a Method. */
|
||||
const rustExtractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
): { funcName: string | null; label: NodeLabel } | null => {
|
||||
if (node.type !== 'impl_item') return null;
|
||||
|
||||
let funcItem: SyntaxNode | null = null;
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'function_item') {
|
||||
funcItem = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!funcItem) return null;
|
||||
|
||||
let nameNode = funcItem.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < funcItem.childCount; i++) {
|
||||
const c = funcItem.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { funcName: nameNode?.text ?? null, label: 'Method' };
|
||||
};
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'unwrap',
|
||||
'expect',
|
||||
|
|
@ -90,5 +121,6 @@ export const rustProvider = defineLanguage({
|
|||
mroStrategy: 'qualified-syntax',
|
||||
fieldExtractor: createFieldExtractor(rustFieldConfig),
|
||||
methodExtractor: createMethodExtractor(rustMethodConfig),
|
||||
extractFunctionName: rustExtractFunctionName,
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@
|
|||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { NodeLabel } from 'gitnexus-shared';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import { typeConfig as swiftConfig } from '../type-extractors/swift.js';
|
||||
import { swiftExportChecker } from '../export-detection.js';
|
||||
import { resolveSwiftImport } from '../import-resolvers/swift.js';
|
||||
import { SWIFT_QUERIES } from '../tree-sitter-queries.js';
|
||||
import type { SwiftPackageConfig } from '../language-config.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import { createFieldExtractor } from '../field-extractors/generic.js';
|
||||
import { swiftConfig as swiftFieldConfig } from '../field-extractors/configs/swift.js';
|
||||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
|
|
@ -109,6 +111,15 @@ function wireSwiftImplicitImports(
|
|||
}
|
||||
}
|
||||
|
||||
/** Swift init/deinit declarations have special names and Constructor label. */
|
||||
const swiftExtractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
): { funcName: string | null; label: NodeLabel } | null => {
|
||||
if (node.type === 'init_declaration') return { funcName: 'init', label: 'Constructor' };
|
||||
if (node.type === 'deinit_declaration') return { funcName: 'deinit', label: 'Constructor' };
|
||||
return null; // fall through to generic
|
||||
};
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'print',
|
||||
'debugPrint',
|
||||
|
|
@ -230,6 +241,7 @@ export const swiftProvider = defineLanguage({
|
|||
heritageDefaultEdge: 'IMPLEMENTS',
|
||||
fieldExtractor: createFieldExtractor(swiftFieldConfig),
|
||||
methodExtractor: createMethodExtractor(swiftMethodConfig),
|
||||
extractFunctionName: swiftExtractFunctionName,
|
||||
implicitImportWirer: wireSwiftImplicitImports,
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@
|
|||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { NodeLabel } from 'gitnexus-shared';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
import { typeConfig as typescriptConfig } from '../type-extractors/typescript.js';
|
||||
import { tsExportChecker } from '../export-detection.js';
|
||||
import { resolveTypescriptImport, resolveJavascriptImport } from '../import-resolvers/standard.js';
|
||||
|
|
@ -23,6 +25,31 @@ import {
|
|||
javascriptMethodConfig,
|
||||
} from '../method-extractors/configs/typescript-javascript.js';
|
||||
|
||||
/**
|
||||
* TypeScript/JavaScript: arrow_function and function_expression get their name
|
||||
* from the parent variable_declarator (e.g. `const foo = () => {}`).
|
||||
*/
|
||||
const tsExtractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
): { funcName: string | null; label: NodeLabel } | null => {
|
||||
if (node.type !== 'arrow_function' && node.type !== 'function_expression') return null;
|
||||
|
||||
const parent = node.parent;
|
||||
if (parent?.type !== 'variable_declarator') return null;
|
||||
|
||||
let nameNode = parent.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < parent.childCount; i++) {
|
||||
const c = parent.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { funcName: nameNode?.text ?? null, label: 'Function' };
|
||||
};
|
||||
|
||||
export const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'console',
|
||||
'log',
|
||||
|
|
@ -130,6 +157,7 @@ export const typescriptProvider = defineLanguage({
|
|||
namedBindingExtractor: extractTsNamedBindings,
|
||||
fieldExtractor: typescriptFieldExtractor,
|
||||
methodExtractor: createMethodExtractor(typescriptMethodConfig),
|
||||
extractFunctionName: tsExtractFunctionName,
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
||||
|
|
@ -143,5 +171,6 @@ export const javascriptProvider = defineLanguage({
|
|||
namedBindingExtractor: extractTsNamedBindings,
|
||||
fieldExtractor: createFieldExtractor(javascriptConfig),
|
||||
methodExtractor: createMethodExtractor(javascriptMethodConfig),
|
||||
extractFunctionName: tsExtractFunctionName,
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -386,234 +386,45 @@ export const findSiblingChild = (
|
|||
|
||||
/**
|
||||
* Extract function name and label from a function_definition or similar AST node.
|
||||
* Handles C/C++ qualified_identifier (ClassName::MethodName) and other language patterns.
|
||||
*
|
||||
* Thin dispatcher: delegates to provider.extractFunctionName when available,
|
||||
* then falls back to generic name-field lookup for languages that don't need
|
||||
* custom AST unwrapping.
|
||||
*/
|
||||
export const extractFunctionName = (
|
||||
node: SyntaxNode,
|
||||
provider?: {
|
||||
extractFunctionName?: (
|
||||
node: SyntaxNode,
|
||||
) => { funcName: string | null; label: NodeLabel } | null;
|
||||
},
|
||||
): { funcName: string | null; label: NodeLabel } => {
|
||||
// Provider hook — language-specific extraction (C/C++ declarator unwrapping,
|
||||
// Swift init/deinit, Rust impl_item, TS arrow functions, Ruby methods)
|
||||
if (provider?.extractFunctionName) {
|
||||
const result = provider.extractFunctionName(node);
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
// Generic fallback: determine label from node type, then try 'name' field
|
||||
let funcName: string | null = null;
|
||||
let label: NodeLabel = 'Function';
|
||||
|
||||
// Swift init/deinit
|
||||
if (node.type === 'init_declaration' || node.type === 'deinit_declaration') {
|
||||
return {
|
||||
funcName: node.type === 'init_declaration' ? 'init' : 'deinit',
|
||||
label: 'Constructor',
|
||||
};
|
||||
if (
|
||||
node.type === 'method_definition' ||
|
||||
node.type === 'method_declaration' ||
|
||||
node.type === 'method' ||
|
||||
node.type === 'singleton_method'
|
||||
) {
|
||||
label = 'Method';
|
||||
}
|
||||
if (node.type === 'constructor_declaration' || node.type === 'compact_constructor_declaration') {
|
||||
label = 'Constructor';
|
||||
}
|
||||
|
||||
if (FUNCTION_DECLARATION_TYPES.has(node.type)) {
|
||||
// C/C++: function_definition -> [pointer_declarator ->] function_declarator -> qualified_identifier/identifier
|
||||
// Unwrap pointer_declarator / reference_declarator wrappers to reach function_declarator
|
||||
let declarator = node.childForFieldName?.('declarator');
|
||||
if (!declarator) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'function_declarator') {
|
||||
declarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (
|
||||
declarator &&
|
||||
(declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator')
|
||||
) {
|
||||
let nextDeclarator = declarator.childForFieldName?.('declarator');
|
||||
if (!nextDeclarator) {
|
||||
for (let i = 0; i < declarator.childCount; i++) {
|
||||
const c = declarator.child(i);
|
||||
if (
|
||||
c?.type === 'function_declarator' ||
|
||||
c?.type === 'pointer_declarator' ||
|
||||
c?.type === 'reference_declarator'
|
||||
) {
|
||||
nextDeclarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
declarator = nextDeclarator;
|
||||
}
|
||||
if (declarator) {
|
||||
let innerDeclarator = declarator.childForFieldName?.('declarator');
|
||||
if (!innerDeclarator) {
|
||||
for (let i = 0; i < declarator.childCount; i++) {
|
||||
const c = declarator.child(i);
|
||||
if (
|
||||
c?.type === 'qualified_identifier' ||
|
||||
c?.type === 'identifier' ||
|
||||
c?.type === 'field_identifier' ||
|
||||
c?.type === 'parenthesized_declarator'
|
||||
) {
|
||||
innerDeclarator = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (innerDeclarator?.type === 'qualified_identifier') {
|
||||
let nameNode = innerDeclarator.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
||||
const c = innerDeclarator.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameNode?.text) {
|
||||
funcName = nameNode.text;
|
||||
label = 'Method';
|
||||
}
|
||||
} else if (
|
||||
innerDeclarator?.type === 'identifier' ||
|
||||
innerDeclarator?.type === 'field_identifier'
|
||||
) {
|
||||
// field_identifier is used for method names inside C++ class bodies
|
||||
funcName = innerDeclarator.text;
|
||||
if (innerDeclarator.type === 'field_identifier') label = 'Method';
|
||||
} else if (innerDeclarator?.type === 'parenthesized_declarator') {
|
||||
let nestedId: SyntaxNode | null = null;
|
||||
for (let i = 0; i < innerDeclarator.childCount; i++) {
|
||||
const c = innerDeclarator.child(i);
|
||||
if (c?.type === 'qualified_identifier' || c?.type === 'identifier') {
|
||||
nestedId = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nestedId?.type === 'qualified_identifier') {
|
||||
let nameNode = nestedId.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < nestedId.childCount; i++) {
|
||||
const c = nestedId.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nameNode?.text) {
|
||||
funcName = nameNode.text;
|
||||
label = 'Method';
|
||||
}
|
||||
} else if (nestedId?.type === 'identifier') {
|
||||
funcName = nestedId.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for other languages (Kotlin uses simple_identifier, Swift uses simple_identifier)
|
||||
if (!funcName) {
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (
|
||||
c?.type === 'identifier' ||
|
||||
c?.type === 'property_identifier' ||
|
||||
c?.type === 'simple_identifier'
|
||||
) {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
}
|
||||
} else if (node.type === 'impl_item') {
|
||||
let funcItem: SyntaxNode | null = null;
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'function_item') {
|
||||
funcItem = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (funcItem) {
|
||||
let nameNode = funcItem.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < funcItem.childCount; i++) {
|
||||
const c = funcItem.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
label = 'Method';
|
||||
}
|
||||
} else if (node.type === 'method_definition') {
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'property_identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
// Dart: method_signature wraps function_signature — unwrap to reach the name
|
||||
if (node.type === 'method_signature') {
|
||||
label = 'Method';
|
||||
} else if (node.type === 'method_declaration' || node.type === 'constructor_declaration') {
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
label = 'Method';
|
||||
} else if (node.type === 'arrow_function' || node.type === 'function_expression') {
|
||||
const parent = node.parent;
|
||||
if (parent?.type === 'variable_declarator') {
|
||||
let nameNode = parent.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < parent.childCount; i++) {
|
||||
const c = parent.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
}
|
||||
} else if (node.type === 'method' || node.type === 'singleton_method') {
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text;
|
||||
label = 'Method';
|
||||
} else if (node.type === 'function_signature') {
|
||||
// Dart: top-level function signatures
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (c?.type === 'identifier') {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text ?? null;
|
||||
} else if (node.type === 'method_signature') {
|
||||
// Dart: method_signature wraps function_signature
|
||||
let funcSig: SyntaxNode | null = null;
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
|
|
@ -635,129 +446,34 @@ export const extractFunctionName = (
|
|||
}
|
||||
funcName = nameNode?.text ?? null;
|
||||
}
|
||||
label = 'Method';
|
||||
return { funcName, label };
|
||||
}
|
||||
|
||||
// Try 'name' field first (works for most languages: Go, Python, PHP, Java, Kotlin,
|
||||
// C#, Dart function_signature, Swift simple_identifier, generic function_declaration)
|
||||
let nameNode = node.childForFieldName?.('name');
|
||||
if (!nameNode) {
|
||||
// Fallback: scan for common identifier child types
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const c = node.child(i);
|
||||
if (
|
||||
c?.type === 'identifier' ||
|
||||
c?.type === 'property_identifier' ||
|
||||
c?.type === 'simple_identifier'
|
||||
) {
|
||||
nameNode = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
funcName = nameNode?.text ?? null;
|
||||
|
||||
return { funcName, label };
|
||||
};
|
||||
|
||||
/** Argument list node types shared between countCallArguments and call-resolution helpers. */
|
||||
export const CALL_ARGUMENT_LIST_TYPES = new Set(['arguments', 'argument_list', 'value_arguments']);
|
||||
|
||||
/** Parameter list node types used for arity counting. */
|
||||
const PARAM_LIST_TYPES = new Set([
|
||||
'formal_parameters',
|
||||
'parameters',
|
||||
'parameter_list',
|
||||
'function_parameters',
|
||||
'method_parameters',
|
||||
'function_value_parameters',
|
||||
'formal_parameter_list', // Dart
|
||||
]);
|
||||
|
||||
/** Node types that indicate variadic/rest parameters. */
|
||||
const VARIADIC_PARAM_TYPES = new Set([
|
||||
'variadic_parameter_declaration', // Go
|
||||
'variadic_parameter', // Rust
|
||||
'spread_parameter', // Java
|
||||
'list_splat_pattern', // Python
|
||||
'dictionary_splat_pattern', // Python
|
||||
]);
|
||||
|
||||
/**
|
||||
* Count the number of parameters on a method/function AST node.
|
||||
* Returns undefined for variadic signatures (same convention as MethodExtractor).
|
||||
* Used by call-resolution to compute arity suffixes for overload disambiguation.
|
||||
*/
|
||||
export const countMethodParameters = (node: SyntaxNode | null | undefined): number | undefined => {
|
||||
if (!node) return 0;
|
||||
|
||||
const findParamList = (current: SyntaxNode): SyntaxNode | null => {
|
||||
for (const child of current.children) {
|
||||
if (PARAM_LIST_TYPES.has(child.type)) return child;
|
||||
}
|
||||
for (const child of current.children) {
|
||||
const nested = findParamList(child);
|
||||
if (nested) return nested;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parameterList = PARAM_LIST_TYPES.has(node.type)
|
||||
? node
|
||||
: (node.childForFieldName?.('parameters') ?? findParamList(node));
|
||||
|
||||
let count = 0;
|
||||
let isVariadic = false;
|
||||
|
||||
if (parameterList && PARAM_LIST_TYPES.has(parameterList.type)) {
|
||||
for (const param of parameterList.namedChildren) {
|
||||
if (param.type === 'comment') continue;
|
||||
if (
|
||||
param.text === 'self' ||
|
||||
param.text === '&self' ||
|
||||
param.text === '&mut self' ||
|
||||
param.type === 'self_parameter'
|
||||
)
|
||||
continue;
|
||||
// TypeScript: `this` parameter is a type constraint, not a real param
|
||||
if (param.type === 'required_parameter') {
|
||||
const patternNode = param.childForFieldName('pattern');
|
||||
if (patternNode?.type === 'this') continue;
|
||||
}
|
||||
// Skip Kotlin default-value siblings that appear as named children
|
||||
if (
|
||||
param.type.endsWith('_literal') ||
|
||||
param.type === 'call_expression' ||
|
||||
param.type === 'navigation_expression' ||
|
||||
param.type === 'prefix_expression' ||
|
||||
param.type === 'parenthesized_expression'
|
||||
)
|
||||
continue;
|
||||
if (VARIADIC_PARAM_TYPES.has(param.type)) {
|
||||
isVariadic = true;
|
||||
continue;
|
||||
}
|
||||
// TS/JS rest parameter
|
||||
if (param.type === 'required_parameter' || param.type === 'optional_parameter') {
|
||||
for (const child of param.children) {
|
||||
if (child.type === 'rest_pattern') {
|
||||
isVariadic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isVariadic) continue;
|
||||
}
|
||||
// Kotlin vararg
|
||||
if (param.type === 'parameter' || param.type === 'formal_parameter') {
|
||||
const prev = param.previousSibling;
|
||||
if (prev?.type === 'parameter_modifiers' && prev.text.includes('vararg')) {
|
||||
isVariadic = true;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
// C/C++: bare `...` token
|
||||
if (!isVariadic) {
|
||||
for (const child of parameterList.children) {
|
||||
if (!child.isNamed && child.text === '...') {
|
||||
isVariadic = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Swift fallback: parameter nodes as direct children of function_declaration
|
||||
if (!parameterList && count === 0) {
|
||||
for (const child of node.namedChildren) {
|
||||
if (child.type === 'parameter') count++;
|
||||
}
|
||||
}
|
||||
|
||||
return isVariadic ? undefined : count;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Generic AST traversal helpers (shared by parse-worker + php-helpers)
|
||||
// ============================================================================
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ import {
|
|||
findEnclosingClassInfo,
|
||||
type EnclosingClassInfo,
|
||||
getLabelFromCaptures,
|
||||
countMethodParameters,
|
||||
findDescendant,
|
||||
extractStringContent,
|
||||
type SyntaxNode,
|
||||
|
|
@ -526,7 +525,7 @@ const findEnclosingFunctionId = (
|
|||
let current = node.parent;
|
||||
while (current) {
|
||||
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
||||
const { funcName, label } = extractFunctionName(current);
|
||||
const { funcName, label } = extractFunctionName(current, provider);
|
||||
if (funcName) {
|
||||
// Apply labelOverride so label matches definition phase (e.g., Kotlin Function→Method).
|
||||
// null means "skip as definition" — keep original label for scope identification.
|
||||
|
|
@ -538,9 +537,26 @@ const findEnclosingFunctionId = (
|
|||
// Qualify with enclosing class to match definition-phase node IDs
|
||||
const classInfo = cachedFindEnclosingClassInfo(current, filePath);
|
||||
const qualifiedName = classInfo ? `${classInfo.className}.${funcName}` : funcName;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs
|
||||
const needsArity = finalLabel === 'Method' || finalLabel === 'Constructor';
|
||||
const arity = needsArity ? countMethodParameters(current) : undefined;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
||||
// Use the same MethodExtractor (getMethodInfo) as the definition phase.
|
||||
let arity: number | undefined;
|
||||
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
||||
const classNode =
|
||||
findEnclosingClassNode(current) ?? findClassNodeByQualifiedName(current);
|
||||
if (classNode) {
|
||||
const methodMap = getMethodInfo(classNode, provider, {
|
||||
filePath,
|
||||
language: getLanguageFromFilename(filePath),
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
const arityTag = arity !== undefined ? `#${arity}` : '';
|
||||
const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag}`);
|
||||
functionIdCache.set(node, result);
|
||||
|
|
@ -566,10 +582,26 @@ const findEnclosingFunctionId = (
|
|||
const qualifiedName = classInfo
|
||||
? `${classInfo.className}.${customResult.funcName}`
|
||||
: customResult.funcName;
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs
|
||||
// Include #<arity> suffix to match definition-phase Method/Constructor IDs.
|
||||
const sigNode = current.previousSibling ?? current;
|
||||
const needsArity2 = finalLabel === 'Method' || finalLabel === 'Constructor';
|
||||
const arity2 = needsArity2 ? countMethodParameters(sigNode) : undefined;
|
||||
let arity2: number | undefined;
|
||||
if (finalLabel === 'Method' || finalLabel === 'Constructor') {
|
||||
const classNode2 =
|
||||
findEnclosingClassNode(sigNode) ?? findClassNodeByQualifiedName(sigNode);
|
||||
if (classNode2) {
|
||||
const methodMap2 = getMethodInfo(classNode2, provider, {
|
||||
filePath,
|
||||
language: getLanguageFromFilename(filePath),
|
||||
});
|
||||
const defLine2 = sigNode.startPosition.row + 1;
|
||||
const info2 = methodMap2?.get(`${customResult.funcName}:${defLine2}`);
|
||||
if (info2) {
|
||||
arity2 = info2.parameters.some((p) => p.isVariadic)
|
||||
? undefined
|
||||
: info2.parameters.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
const arityTag2 = arity2 !== undefined ? `#${arity2}` : '';
|
||||
const result = generateId(finalLabel, `${filePath}:${qualifiedName}${arityTag2}`);
|
||||
functionIdCache.set(node, result);
|
||||
|
|
|
|||
|
|
@ -343,6 +343,9 @@ describe('isBuiltInOrNoise', () => {
|
|||
|
||||
describe('extractFunctionName', () => {
|
||||
const parser = new Parser();
|
||||
const cProvider = getProvider(SupportedLanguages.C);
|
||||
const cppProvider = getProvider(SupportedLanguages.CPlusPlus);
|
||||
const tsProvider = getProvider(SupportedLanguages.TypeScript);
|
||||
|
||||
describe('C', () => {
|
||||
it('extracts function name from C function definition', () => {
|
||||
|
|
@ -351,7 +354,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cProvider);
|
||||
|
||||
expect(result.funcName).toBe('main');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -363,7 +366,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cProvider);
|
||||
|
||||
expect(result.funcName).toBe('helper');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -377,7 +380,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('OnEncryptData');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -389,7 +392,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('OnDataOprEvent');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -401,7 +404,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('standalone_function');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -413,7 +416,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('handler');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -427,7 +430,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cProvider);
|
||||
|
||||
expect(result.funcName).toBe('get_data');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -439,7 +442,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cProvider);
|
||||
|
||||
expect(result.funcName).toBe('get_strings');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -451,7 +454,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cProvider);
|
||||
|
||||
expect(result.funcName).toBe('create_node');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -465,7 +468,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('getData');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -477,7 +480,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('get_name');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -489,7 +492,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('at');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -501,7 +504,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
expect(result.funcName).toBe('getName');
|
||||
expect(result.label).toBe('Method');
|
||||
|
|
@ -515,7 +518,7 @@ describe('extractFunctionName', () => {
|
|||
const tree = parser.parse(code);
|
||||
const funcNode = tree.rootNode.child(0);
|
||||
|
||||
const result = extractFunctionName(funcNode);
|
||||
const result = extractFunctionName(funcNode, cppProvider);
|
||||
|
||||
// destructor_name includes the ~ prefix
|
||||
expect(result.funcName).toBe('~MyClass');
|
||||
|
|
@ -533,7 +536,7 @@ describe('extractFunctionName', () => {
|
|||
const declarator = varDecl!.namedChild(0);
|
||||
const arrowFunc = declarator!.namedChild(1);
|
||||
|
||||
const result = extractFunctionName(arrowFunc);
|
||||
const result = extractFunctionName(arrowFunc, tsProvider);
|
||||
|
||||
expect(result.funcName).toBe('myHandler');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
@ -548,7 +551,7 @@ describe('extractFunctionName', () => {
|
|||
const declarator = varDecl!.namedChild(0);
|
||||
const funcExpr = declarator!.namedChild(1);
|
||||
|
||||
const result = extractFunctionName(funcExpr);
|
||||
const result = extractFunctionName(funcExpr, tsProvider);
|
||||
|
||||
expect(result.funcName).toBe('processItem');
|
||||
expect(result.label).toBe('Function');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue