mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-23 00:41:36 +00:00
* Initial plan * feat: add C scope resolution files for language migration (RFC #909) Add 11 C language scope resolution files following the Go pattern: - query.ts: tree-sitter-c query and parser for C constructs - captures.ts: emit scope captures with arity enrichment - import-decomposer.ts: decompose #include into structured captures - arity-metadata.ts: C function declaration/call arity computation - interpret.ts: interpret C imports and type bindings - import-target.ts: resolve #include paths via suffix matching - arity.ts: C arity compatibility (variadic detection) - merge-bindings.ts: first-wins binding merge by tier - simple-hooks.ts: null hooks (no receivers/methods in C) - index.ts: barrel re-exports - scope-resolver.ts: ScopeResolver implementation for C Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * feat: migrate C to scope-based resolution (RFC #909 Ring 3) Add C ScopeResolver with: - tree-sitter-c scope query (structs, unions, enums, functions, macros, variables, includes) - emitCScopeCaptures with arity enrichment and typedef-struct dedup - interpretCImport for #include directives (system headers filtered) - resolveCImportTarget with suffix matching - cArityCompatibility with variadic detection - cMergeBindings (first-wins by tier) - Header file scanning for cross-language #include resolution - Register in SCOPE_RESOLVERS and MIGRATED_LANGUAGES - Integration test with 4 passing test cases Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ddcbc075-2999-492c-a0ac-47cddd401a4b Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: update registry-primary-flag test and add C legacy parity expected failures Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ddcbc075-2999-492c-a0ac-47cddd401a4b Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * refactor: improve arity-metadata readability per review feedback Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ddcbc075-2999-492c-a0ac-47cddd401a4b Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address CI failures — unused import, Dirent types, null comparison, lint, formatting Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/f4b6e20d-8d56-4834-8296-db82af27f8e1 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: replace loose comparisons with strict equality, remove optional chaining from childForFieldName Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/20628629-9dc7-45ec-8ae4-f3a14ad29d93 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * Potential fix for pull request finding 'CodeQL / Comparison between inconvertible types' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix: remove unnecessary optional chaining on non-null decl in findFuncDeclarator Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/9b012c35-7494-4180-8c6a-b83da8d8abb9 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address 5 production readiness review findings Finding 1: Enforce static functions as file-local via expandsWildcardTo hook - Add static-linkage.ts tracking module with markStaticName/isStaticName/expandCWildcardNames - Update captures.ts to detect storage_class_specifier static on functions - Wire expandsWildcardTo in scope-resolver.ts Finding 2: Expand test coverage to ≥30 cases (74 unit tests added) - c-captures.test.ts: 55 tests (scopes, structs, unions, enums, functions, typedef, field, variable, macro, imports, references, type bindings, arity, static) - c-imports.test.ts: 12 tests (decomposition, interpretation, target resolution, determinism, edge cases) - c-arity.test.ts: 18 tests (declaration arity, call arity, compatibility) Finding 3: Deterministic #include resolution on depth ties - Add lexicographic tiebreak in import-target.ts when candidates tie on path depth Finding 4: Revert unexplained package-lock.json change - Restored to pre-PR state (node >=20.0.0) Finding 5: Planning artifact commit acknowledged (squash on merge) Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/22ee780c-2b44-4e69-b9c4-8843ad6ec1ee Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address code review feedback — Set-based dedup, SyntaxNode type alias Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/22ee780c-2b44-4e69-b9c4-8843ad6ec1ee Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix: address second review findings 1-4 — isFileLocalDef hook, singleton docs, fn-ptr typedef, static isolation test Finding 1: Added `isFileLocalDef` hook to ScopeResolver contract + implementation in free-call-fallback.ts to filter C static functions from global free-call fallback. Threads caller filePath through pickUniqueGlobalCallable so static defs in other files are excluded. Finding 2: Documented single-invocation assumption on staticNames Map. Added clearStaticNames() call in loadResolutionConfig to prevent cross-repo contamination in server-mode scenarios. Finding 3: Added tree-sitter query pattern for function pointer typedef aliases (typedef void (*callback)(int, int)) in query.ts. Added unit test. Finding 4: Added c-static-isolation integration fixture (a.c with static helper, b.c with non-static helper, caller.c) and test asserting no CALLS edge from caller to a.c's static helper. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/5b948327-9f59-4ca2-9d8c-8c8087feb510 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix: skip static isolation integration test in legacy parity mode The `caller.c calls b:helper via include, NOT a:static helper` test requires scope-based wildcard import binding + isFileLocalDef filtering which is only available in the registry-primary path. The legacy DAG path does not resolve cross-file calls through #include → prototype chains. Added to LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/3be48249-b375-4446-973b-657400f530fb Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address 3 review findings — static leakage in Phase 2, build-dir skip list, same-directory preference Finding 1: Apply isFileLocalDef filtering in Phase 2 of pickUniqueGlobalCallable so cross-file static defs cannot leak through the SemanticModel fallback path. Finding 2: Expand scanHeaderFiles skip list with dist, build, out, target, _build, .next, cmake-build-* to avoid generated headers shadowing source ones. Finding 3: Implement same-directory sibling preference in resolveCImportTarget, matching C compiler #include "…" relative-lookup semantics. Sibling check now runs before exact match and suffix fallback. Tests: 11 new header-scan tests, 4 new import-target tests (96 total C tests). Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/5236f2b8-72a0-476d-bf39-cca041781014 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * refactor: skip redundant sibling path computation when targetRaw has no backslashes Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/5236f2b8-72a0-476d-bf39-cca041781014 Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * chore(autofix): apply prettier + eslint fixes via /autofix command * fix: normalize header-scan paths to forward slashes for Windows compatibility On Windows, path.relative() produces backslash-separated paths (e.g. src\foo.h) which caused test failures when comparing against forward-slash expectations. Normalize output with .replace(/\\/g, '/') matching the convention used throughout the scope-resolution pipeline. Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/88420759-8a8c-4e6d-affa-ecb5458a235a Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> * fix: address 4 findings — K&R arity, function-pointer docs, prototype docs, _filePath rename Finding 1: K&R empty parameter list `int foo()` now returns unknown arity `{}` instead of `{parameterCount:0}`. Distinguishes from explicit `int foo(void)`. 3 unit tests cover K&R definition, prototype, and void comparison. Finding 2: Added code comment documenting function-pointer-variable call capture as known architectural trade-off (same as Go resolver). Finding 3: Added code comment documenting prototype/definition duplication as graph-quality concern (no false CALLS edges). Finding 4: Renamed `_filePath` → `filePath` in captures.ts since it is actively used in markStaticName(). Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/ab2164f7-972f-4ea8-81fa-a14ce20d7cce Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
152 lines
5.9 KiB
TypeScript
152 lines
5.9 KiB
TypeScript
/**
|
|
* Shared test helpers for language resolution integration tests.
|
|
*/
|
|
import path from 'path';
|
|
import { it as vitestIt } from 'vitest';
|
|
import { runPipelineFromRepo } from '../../../src/core/ingestion/pipeline.js';
|
|
import type { PipelineOptions } from '../../../src/core/ingestion/pipeline.js';
|
|
import type { PipelineResult } from '../../../src/types/pipeline.js';
|
|
import type { GraphRelationship } from 'gitnexus-shared';
|
|
|
|
const LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES: Readonly<Record<string, ReadonlySet<string>>> = {
|
|
c: new Set([
|
|
// The legacy DAG path does not resolve the main → create_service call
|
|
// because the function prototype in the .h file and the definition in
|
|
// the .c file create a dedup ambiguity. The registry-primary path
|
|
// resolves it via scope-based wildcard import binding.
|
|
'emits CALLS edges for cross-file function calls',
|
|
// The legacy DAG path does not resolve cross-file calls through
|
|
// #include → prototype chains. The scope-based path resolves
|
|
// caller.c → b.h → public_b via wildcard import binding +
|
|
// isFileLocalDef filtering of static functions.
|
|
'caller.c calls b:helper via include, NOT a:static helper',
|
|
]),
|
|
csharp: new Set([
|
|
'emits the using-import edge App/Program.cs -> Models/User.cs through the scope-resolution path',
|
|
// Generic type-argument USES edges are emitted by the registry-primary
|
|
// resolver only; the legacy DAG path does not synthesize these references.
|
|
'emits USES edges for generic type arguments',
|
|
]),
|
|
go: new Set([
|
|
// The legacy DAG path does not resolve method calls when the method is
|
|
// defined in a different file from the receiver type (go-split-method-owner
|
|
// fixture). This requires scope-based cross-file package-sibling resolution
|
|
// which is only available in the registry-primary path.
|
|
'resolves user.Save() to the method whose receiver type is declared in another package file',
|
|
]),
|
|
python: new Set([
|
|
// Suffix-fallback lex tiebreak depends on the registry-primary
|
|
// resolver's deterministic sort. The legacy resolver returns the
|
|
// first match in `Set` iteration order, which is insertion-order
|
|
// dependent and not aligned with this guarantee. Backporting the
|
|
// sort to legacy is out of scope.
|
|
'picks the lexicographically smaller path on equal-depth ties',
|
|
'binds the call to alpha/services/sync.py, not omega',
|
|
'lex tiebreak still picks alpha/services/sync.py with reversed file-write order',
|
|
]),
|
|
};
|
|
|
|
type ResolverParityEnv = Readonly<Record<string, string | undefined>>;
|
|
type VitestIt = typeof vitestIt;
|
|
type CallableIt = (name: string, ...args: unknown[]) => unknown;
|
|
|
|
export function resolverParityFlagName(languageSlug: string): string {
|
|
return `REGISTRY_PRIMARY_${languageSlug.toUpperCase().replace(/-/g, '_')}`;
|
|
}
|
|
|
|
export function isLegacyResolverParityRun(
|
|
languageSlug: string,
|
|
env: ResolverParityEnv = process.env,
|
|
): boolean {
|
|
const value = env[resolverParityFlagName(languageSlug)]?.trim().toLowerCase();
|
|
return value === '0' || value === 'false' || value === 'no';
|
|
}
|
|
|
|
export function isLegacyResolverParityExpectedFailure(
|
|
languageSlug: string,
|
|
testName: string,
|
|
env: ResolverParityEnv = process.env,
|
|
): boolean {
|
|
if (!isLegacyResolverParityRun(languageSlug, env)) return false;
|
|
return LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES[languageSlug]?.has(testName) ?? false;
|
|
}
|
|
|
|
export function createResolverParityIt(languageSlug: string): VitestIt {
|
|
const wrapped = ((name: string, ...args: unknown[]) => {
|
|
const runner = isLegacyResolverParityExpectedFailure(languageSlug, name)
|
|
? vitestIt.skip
|
|
: vitestIt;
|
|
return (runner as unknown as CallableIt)(name, ...args);
|
|
}) as VitestIt;
|
|
|
|
Object.assign(wrapped, vitestIt);
|
|
return wrapped;
|
|
}
|
|
|
|
export const FIXTURES = path.resolve(__dirname, '..', '..', 'fixtures', 'lang-resolution');
|
|
export const CROSS_FILE_FIXTURES = path.resolve(
|
|
__dirname,
|
|
'..',
|
|
'..',
|
|
'fixtures',
|
|
'cross-file-binding',
|
|
);
|
|
|
|
export type RelEdge = {
|
|
source: string;
|
|
target: string;
|
|
sourceLabel: string;
|
|
targetLabel: string;
|
|
sourceFilePath: string;
|
|
targetFilePath: string;
|
|
rel: GraphRelationship;
|
|
};
|
|
|
|
export function getRelationships(result: PipelineResult, type: string): RelEdge[] {
|
|
const edges: RelEdge[] = [];
|
|
for (const rel of result.graph.iterRelationships()) {
|
|
if (rel.type === type) {
|
|
const sourceNode = result.graph.getNode(rel.sourceId);
|
|
const targetNode = result.graph.getNode(rel.targetId);
|
|
edges.push({
|
|
source: sourceNode?.properties.name ?? rel.sourceId,
|
|
target: targetNode?.properties.name ?? rel.targetId,
|
|
sourceLabel: sourceNode?.label ?? 'unknown',
|
|
targetLabel: targetNode?.label ?? 'unknown',
|
|
sourceFilePath: sourceNode?.properties.filePath ?? '',
|
|
targetFilePath: targetNode?.properties.filePath ?? '',
|
|
rel,
|
|
});
|
|
}
|
|
}
|
|
return edges;
|
|
}
|
|
|
|
export function getNodesByLabel(result: PipelineResult, label: string): string[] {
|
|
const names: string[] = [];
|
|
result.graph.forEachNode((n) => {
|
|
if (n.label === label) names.push(n.properties.name);
|
|
});
|
|
return names.sort();
|
|
}
|
|
|
|
export function edgeSet(edges: Array<{ source: string; target: string }>): string[] {
|
|
return edges.map((e) => `${e.source} → ${e.target}`).sort();
|
|
}
|
|
|
|
/** Get graph nodes by label with full properties (for parameterTypes assertions). */
|
|
export function getNodesByLabelFull(
|
|
result: PipelineResult,
|
|
label: string,
|
|
): Array<{ name: string; properties: Record<string, any> }> {
|
|
const nodes: Array<{ name: string; properties: Record<string, any> }> = [];
|
|
result.graph.forEachNode((n) => {
|
|
if (n.label === label) nodes.push({ name: n.properties.name, properties: n.properties });
|
|
});
|
|
return nodes.sort((a, b) => a.name.localeCompare(b.name));
|
|
}
|
|
|
|
// Tests can pass { skipGraphPhases: true } as third arg for faster runs
|
|
// (skips MRO, community detection, and process extraction).
|
|
export { runPipelineFromRepo };
|
|
export type { PipelineOptions, PipelineResult };
|