diff --git a/gitnexus/src/core/ingestion/utils/call-analysis.ts b/gitnexus/src/core/ingestion/utils/call-analysis.ts index a95cb955a..19a612259 100644 --- a/gitnexus/src/core/ingestion/utils/call-analysis.ts +++ b/gitnexus/src/core/ingestion/utils/call-analysis.ts @@ -362,7 +362,9 @@ export const extractReceiverNode = (nameNode: SyntaxNode): SyntaxNode | undefine // Lua: the receiver is the `table` field of the call's `function` variable. if (!receiver && callNode.type === 'call') { const callee = callNode.childForFieldName('function'); - if (callee?.type === 'variable') receiver = callNode.childForFieldName('table'); + if (callee?.type === 'variable') { + receiver = callNode.childForFieldName('table') ?? callee.childForFieldName('table'); + } } if ( diff --git a/gitnexus/test/unit/call-form.test.ts b/gitnexus/test/unit/call-form.test.ts index 38d97e9ed..edf6f4900 100644 --- a/gitnexus/test/unit/call-form.test.ts +++ b/gitnexus/test/unit/call-form.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect } from 'vitest'; import { inferCallForm, extractReceiverName, + extractReceiverNode, } from '../../src/core/ingestion/utils/call-analysis.js'; import type { SyntaxNode } from '../../src/core/ingestion/utils/ast-helpers.js'; import { createSymbolTable } from '../../src/core/ingestion/model/symbol-table.js'; @@ -116,6 +117,33 @@ describe('inferCallForm', () => { expect(inferCallForm(match!.callNode, match!.nameNode)).toBe('member'); expect(extractReceiverName(match!.nameNode)).toBe('util'); }); + + it('captures a complex receiver node for deferred chain resolution', () => { + parser.setLanguage(Lua); + const captures = extractCallCaptures(parser, 'getService().run()', SupportedLanguages.Lua); + const match = captures.find((c) => c.calledName === 'run'); + expect(match).toBeDefined(); + expect(inferCallForm(match!.callNode, match!.nameNode)).toBe('member'); + expect(extractReceiverName(match!.nameNode)).toBeUndefined(); + expect(extractReceiverNode(match!.nameNode)?.text).toBe('getService()'); + }); + + it('captures a bare middleclass class binding', () => { + parser.setLanguage(Lua); + const provider = getProvider(SupportedLanguages.Lua); + const tree = parser.parse('local Foo = class("Foo")'); + const query = new Parser.Query(parser.getLanguage(), provider.treeSitterQueries); + const matches = query.matches(tree.rootNode); + expect( + matches.some( + (match) => + match.captures.some((capture) => capture.name === 'definition.class') && + match.captures.some( + (capture) => capture.name === 'name' && capture.node.text === 'Foo', + ), + ), + ).toBe(true); + }); }); describe('Java', () => { diff --git a/gitnexus/test/unit/parser-loader-abi.test.ts b/gitnexus/test/unit/parser-loader-abi.test.ts index 994da9f9d..ae624da44 100644 --- a/gitnexus/test/unit/parser-loader-abi.test.ts +++ b/gitnexus/test/unit/parser-loader-abi.test.ts @@ -203,8 +203,11 @@ describe('parser-loader ABI load-smoke (#1922)', () => { return; } + const lf = String.fromCharCode(10); const crlf = String.fromCharCode(13, 10); const snippets = [ + `local value = "line\\${lf}continued"${lf}`, + `local value = 'line\\${lf}continued'${lf}`, `local value = "line\\${crlf}continued"${crlf}`, `local value = 'line\\${crlf}continued'${crlf}`, ]; diff --git a/gitnexus/vendor/tree-sitter-lua/bindings/node/index.d.ts b/gitnexus/vendor/tree-sitter-lua/bindings/node/index.d.ts index 7c7e628a4..f9d0c22eb 100644 --- a/gitnexus/vendor/tree-sitter-lua/bindings/node/index.d.ts +++ b/gitnexus/vendor/tree-sitter-lua/bindings/node/index.d.ts @@ -1,5 +1,31 @@ // Type stub mirroring gitnexus/vendor/tree-sitter-c/bindings/node/index.d.ts. // The runtime binding is a native .node loaded by node-gyp-build; this stub // gives TypeScript a Language-shaped module to import. -declare const language: unknown; +type BaseNode = { + type: string; + named: boolean; +}; + +type ChildNode = { + multiple: boolean; + required: boolean; + types: BaseNode[]; +}; + +type NodeInfo = + | (BaseNode & { + subtypes: BaseNode[]; + }) + | (BaseNode & { + fields: { [name: string]: ChildNode }; + children: ChildNode[]; + }); + +type Language = { + name: string; + language: unknown; + nodeTypeInfo: NodeInfo[]; +}; + +declare const language: Language; export = language;