mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-07 02:58:02 +00:00
fix(lua): clear stale heritage on reanalysis; wire Lua prebuild workflow
Three paths flagged in review (azizur100389): 1. clearLuaHeritageFacts() had no caller, and captures.ts only set heritage facts when non-empty — a re-capture of a file that lost its middleclass class left prior EXTENDS/HAS_METHOD facts for collectLuaCaptureSideChannel to snapshot as stale edges. Add a per-file delete on empty recapture (clearLuaHeritageFactsForFile) and wire clearLuaHeritageFacts() into loadResolutionConfig (mirrors Java/Kotlin). 2. Lua was absent from build-tree-sitter-prebuilds.yml despite the PR vendoring tree-sitter-lua source-only (parser.c/scanner.c/binding.gyp) — toolchain-less install failed on platforms the upstream npm package doesn't cover. Add lua to the REGISTRY (kind 'vendored') + a parse snippet for the validate step; commit-fork-prebuilds.yml is generic. 3. Add a lifecycle regression: emitLuaScopeCaptures with no heritage after a prior heritage capture must leave collectLuaCaptureSideChannel undefined (verified the test fails without the per-file delete).
This commit is contained in:
parent
d983e87e7b
commit
52b74d02b3
5 changed files with 79 additions and 2 deletions
|
|
@ -7,7 +7,7 @@ name: Build tree-sitter prebuilds
|
|||
#
|
||||
# Grammars covered here (the at-risk set — everything else already ships 6
|
||||
# upstream prebuilds AND stays dependency-review-tracked, so it is left alone).
|
||||
# All five are vendored under gitnexus/vendor/; `kind` (below) only picks where
|
||||
# All six are vendored under gitnexus/vendor/; `kind` (below) only picks where
|
||||
# the build job fetches the C source to compile:
|
||||
# - tree-sitter-c (vendored prebuild-only; built from the published npm
|
||||
# package — closes upstream's 4/6 ARM gap #2116 for a
|
||||
|
|
@ -20,6 +20,9 @@ name: Build tree-sitter prebuilds
|
|||
# - tree-sitter-swift (vendored source; built from gitnexus/vendor/ — its
|
||||
# prebuilds were originally upstream-shipped, now
|
||||
# GitNexus-cross-built like the rest for uniformity)
|
||||
# - tree-sitter-lua (vendored source; built from gitnexus/vendor/ — the
|
||||
# upstream npm package ships source-only for the
|
||||
# relevant tuples, so all 6 are cross-built here)
|
||||
#
|
||||
# Output: gitnexus/vendor/<grammar>/prebuilds/<platform-arch>/<grammar>.node for
|
||||
# all 6 targets ({linux,darwin,win32}-{x64,arm64}). tree-sitter grammars are
|
||||
|
|
@ -55,7 +58,7 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
grammars:
|
||||
description: 'Comma-separated grammar shortnames to build (c,dart,proto,kotlin,swift), or "all".'
|
||||
description: 'Comma-separated grammar shortnames to build (c,dart,proto,kotlin,swift,lua), or "all".'
|
||||
required: false
|
||||
type: string
|
||||
default: 'all'
|
||||
|
|
@ -157,6 +160,11 @@ jobs:
|
|||
// so it builds from gitnexus/vendor/ like dart/proto. Its prebuilds
|
||||
// were originally upstream-shipped; rebuilding them here unifies it.
|
||||
swift: { name: 'tree-sitter-swift', kind: 'vendored' },
|
||||
// lua is vendored WITH its source (parser.c/scanner.c/binding.gyp);
|
||||
// the upstream npm package ships SOURCE ONLY for the relevant
|
||||
// platform tuples, so GitNexus cross-builds all 6 prebuilds here
|
||||
// like dart/proto/swift to keep install toolchain-free everywhere.
|
||||
lua: { name: 'tree-sitter-lua', kind: 'vendored' },
|
||||
};
|
||||
const PLATFORMS = [
|
||||
{ platform_arch: 'linux-x64', os: 'ubuntu-24.04' },
|
||||
|
|
@ -467,6 +475,7 @@ jobs:
|
|||
proto: "syntax = \"proto3\";\nmessage M { int32 id = 1; }",
|
||||
kotlin: "fun main() { println(\"hi\") }",
|
||||
swift: "func greet() { print(\"hi\") }",
|
||||
lua: "local x = 1\nreturn x",
|
||||
};
|
||||
const lang = require("node-gyp-build")(process.cwd());
|
||||
const Parser = require("tree-sitter");
|
||||
|
|
|
|||
|
|
@ -48,3 +48,10 @@ export function collectLuaCaptureSideChannel(filePath: string): LuaCaptureSideCh
|
|||
export function clearLuaHeritageFacts(): void {
|
||||
_facts.clear();
|
||||
}
|
||||
|
||||
/** Drop this file's facts so a re-capture that produces no heritage (the file
|
||||
* lost its middleclass class between passes) does not leave stale EXTENDS /
|
||||
* HAS_METHOD facts for `collectLuaCaptureSideChannel` to snapshot. */
|
||||
export function clearLuaHeritageFactsForFile(filePath: string): void {
|
||||
_facts.delete(filePath);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { nodeToCapture } from '../../utils/ast-helpers.js';
|
|||
import { getLuaParser, getLuaScopeQuery, getHeritageQuery, getMethodOwnerQuery } from './query.js';
|
||||
import {
|
||||
setLuaHeritageFacts,
|
||||
clearLuaHeritageFactsForFile,
|
||||
type LuaExtendsPair,
|
||||
type LuaMethodOwnerPair,
|
||||
} from './capture-side-channel.js';
|
||||
|
|
@ -95,6 +96,11 @@ export function emitLuaScopeCaptures(
|
|||
}
|
||||
if (extendsPairs.length > 0 || methodOwners.length > 0) {
|
||||
setLuaHeritageFacts(filePath, { kind: 'lua', extendsPairs, methodOwners });
|
||||
} else {
|
||||
// Re-capture produced no heritage — drop any prior facts for this file so
|
||||
// reanalysis of a file that lost its middleclass class does not emit stale
|
||||
// EXTENDS / HAS_METHOD edges from the previous pass.
|
||||
clearLuaHeritageFactsForFile(filePath);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { buildSuffixIndex, suffixResolve, type SuffixIndex } from '../../import-
|
|||
import type { ScopeResolver } from '../../scope-resolution/contract/scope-resolver.js';
|
||||
import { luaProvider } from '../lua.js';
|
||||
import { emitLuaHeritageEdges } from './heritage.js';
|
||||
import { clearLuaHeritageFacts } from './capture-side-channel.js';
|
||||
|
||||
// Cache the suffix index across calls within one analyze run — `allFilePaths`
|
||||
// is the same ReadonlySet for every Lua import in the run, so keying on its
|
||||
|
|
@ -32,6 +33,16 @@ const luaScopeResolver: ScopeResolver = {
|
|||
languageProvider: luaProvider,
|
||||
importEdgeReason: 'lua-scope: require',
|
||||
|
||||
// Worker capture facts are process-local and can outlive a single analysis in
|
||||
// server mode. Runs once before each Lua workspace pass, mirroring the
|
||||
// Java/Kotlin `loadResolutionConfig` clear. The per-file delete in
|
||||
// `captures.ts` is the operative fix for the empty-recapture stale path;
|
||||
// this clear-all is the belt-and-suspenders lifecycle hook.
|
||||
loadResolutionConfig: () => {
|
||||
clearLuaHeritageFacts();
|
||||
return undefined;
|
||||
},
|
||||
|
||||
// require("a.b.c") → module path a/b/c (+ EXTENSIONS: .lua / /init.lua).
|
||||
// targetRaw arrives quote-stripped (interpretLuaImport); Lua's module
|
||||
// separator is `.`, so split on it before joining to a path.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import {
|
|||
runPipelineFromRepo,
|
||||
type PipelineResult,
|
||||
} from './helpers.js';
|
||||
import { emitLuaScopeCaptures } from '../../../src/core/ingestion/languages/lua/index.js';
|
||||
import { collectLuaCaptureSideChannel } from '../../../src/core/ingestion/languages/lua/capture-side-channel.js';
|
||||
|
||||
function writeFixtureRepo(root: string, files: Record<string, string>): void {
|
||||
for (const [rel, content] of Object.entries(files)) {
|
||||
|
|
@ -133,3 +135,45 @@ return Dog
|
|||
expect(classes).toContain('Animal');
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// heritage lifecycle: re-capture with no middleclass must not retain stale
|
||||
// EXTENDS / HAS_METHOD facts from a prior pass (reanalysis regression)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('Lua scope: heritage lifecycle (no stale facts on reanalysis)', () => {
|
||||
// The capture side channel is a module-level map populated by
|
||||
// `emitLuaScopeCaptures` (worker) and snapshotted by
|
||||
// `collectLuaCaptureSideChannel`. Calling both here in the test process
|
||||
// exercises the same module instance, so a re-capture that produces no
|
||||
// heritage must drop the prior facts — otherwise reanalysis of a file that
|
||||
// lost its middleclass class would emit stale EXTENDS / HAS_METHOD edges.
|
||||
const heritageSrc = `local class = require("lib.class")
|
||||
local Animal = class("Animal")
|
||||
function Animal:speak() return "..." end
|
||||
local Dog = class("Dog", Animal)
|
||||
function Dog:bark() return "woof" end
|
||||
return Dog
|
||||
`;
|
||||
const noHeritageSrc = `local x = 1
|
||||
return x
|
||||
`;
|
||||
|
||||
it('populates facts on a middleclass capture', () => {
|
||||
emitLuaScopeCaptures(heritageSrc, 'lifecycle.lua');
|
||||
const facts = collectLuaCaptureSideChannel('lifecycle.lua');
|
||||
expect(facts).toBeDefined();
|
||||
expect(facts?.extendsPairs.length).toBeGreaterThan(0);
|
||||
expect(facts?.methodOwners.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('clears facts on a subsequent no-heritage capture (no stale state)', () => {
|
||||
// First capture establishes heritage facts for the file.
|
||||
emitLuaScopeCaptures(heritageSrc, 'lifecycle.lua');
|
||||
expect(collectLuaCaptureSideChannel('lifecycle.lua')).toBeDefined();
|
||||
// Re-capture with no middleclass — the prior facts must be dropped, not
|
||||
// retained for collectLuaCaptureSideChannel to snapshot as stale edges.
|
||||
emitLuaScopeCaptures(noHeritageSrc, 'lifecycle.lua');
|
||||
expect(collectLuaCaptureSideChannel('lifecycle.lua')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue