mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-01 02:01:24 +00:00
* docs: add Objective-C fork provider notes * feat(objective-c): add deterministic provider and grammar * feat(objective-c): finalize provider MVP * fix(objective-c): harden provider integration * fix(objective-c): normalize bare macro markers * docs(objective-c): integrate provider documentation * fix(objective-c): harden resolution and header classification * fix(objective-c): complete provider follow-ups * fix: address Objective-C review follow-ups * chore: format Objective-C grammar sources * fix(objective-c): harden review follow-ups * Address PR review feedback (#3179) Keep Objective-C chunking and macro recovery aligned with the grammar, and stop Community MEMBER_OF edges from leaking into symbol context. Co-authored-by: Cursor <cursoragent@cursor.com> * Address follow-up review on ObjC chunking and language fallback. Keep preprocessor directive text from changing file-scope brace depth, group real ivar nodes, skip header modifiers, and restore Rakefile/Gemfile detection through getLanguageFromFilename. Co-authored-by: Cursor <cursoragent@cursor.com> * Parse Objective-C headers with the objc grammar in embeddings. ensureAndParse and structural extraction now use the same content classifier as ingest, including method snippets from .h files, so Protocol/Category/Class chunks are not re-parsed as C++. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Keep file-scope macro elision off C line splices and @interface/@protocol/@implementation bodies, and attach ivar attributes to the following instance variable when chunking. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(bench): rebaseline Objective-C CSV emit * feat(objective-c): add workspace resolution and linear emit benches Plain .h files are classified as C++, so the ObjC pass could not resolve #import of those headers. Load a C/C#-style workspace once per pass, and keep protocol-candidate USES linear. Refs #3179 Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) - Compare LadybugDB labels() as a scalar when excluding Community MEMBER_OF edges. - Walk superclass members, skip file-static C sibling defs, and ignore comments in ObjC header/macro scans. Note: pre-existing failure in objective-c-provider integration (worker-pool ready timeout) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Emit Objective-C declaration captures so compilation-unit siblings can share header/implementation bindings, and keep class vs protocol visibility groups distinct. Note: pre-existing failure in worker-pool startup (GITNEXUS_WORKER_READY_TIMEOUT_MS) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3179) Emit every comma-separated property/ivar declarator, and count @interface after a multiline block comment closes so in-declaration macros stay intact. Note: pre-existing failure in worker-pool startup (GITNEXUS_WORKER_READY_TIMEOUT_MS) not addressed by this PR. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: ximengkai <ximengkai@soyoung.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
5.3 KiB
TypeScript
157 lines
5.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
const runScopeResolutionMock = vi.hoisted(() => vi.fn());
|
|
vi.mock('../../src/core/ingestion/scope-resolution/pipeline/run.js', async (importOriginal) => {
|
|
const actual =
|
|
await importOriginal<
|
|
typeof import('../../src/core/ingestion/scope-resolution/pipeline/run.js')
|
|
>();
|
|
return { ...actual, runScopeResolution: runScopeResolutionMock };
|
|
});
|
|
|
|
import { createKnowledgeGraph } from '../../src/core/graph/graph.js';
|
|
import { createSemanticModel } from '../../src/core/ingestion/model/index.js';
|
|
import { scopeResolutionPhase } from '../../src/core/ingestion/scope-resolution/pipeline/phase.js';
|
|
import type { ParseOutput } from '../../src/core/ingestion/pipeline-phases/parse.js';
|
|
import type { StructureOutput } from '../../src/core/ingestion/pipeline-phases/structure.js';
|
|
import type {
|
|
PhaseResult,
|
|
PipelineContext,
|
|
} from '../../src/core/ingestion/pipeline-phases/types.js';
|
|
import { SupportedLanguages } from 'gitnexus-shared';
|
|
|
|
const phaseResult = <T>(phaseName: string, output: T): PhaseResult<T> => ({
|
|
phaseName,
|
|
output,
|
|
durationMs: 0,
|
|
});
|
|
|
|
describe('scopeResolutionPhase failure reconciliation', () => {
|
|
let repoDir = '';
|
|
|
|
afterEach(() => {
|
|
runScopeResolutionMock.mockReset();
|
|
if (repoDir) fs.rmSync(repoDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it('retains a parse failure when the main-thread provider fallback also fails', async () => {
|
|
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scope-phase-failure-'));
|
|
fs.writeFileSync(path.join(repoDir, 'broken.py'), 'def broken(:\n');
|
|
|
|
runScopeResolutionMock.mockReturnValue({
|
|
filesProcessed: 0,
|
|
filesSkipped: 1,
|
|
scopeExtractionFailedPaths: ['broken.py'],
|
|
importsEmitted: 0,
|
|
resolve: { unresolved: 0 },
|
|
referenceEdgesEmitted: 0,
|
|
referenceSkipped: 0,
|
|
propertyDispatchSkippedKeys: 0,
|
|
importedValueRefEdges: 0,
|
|
uniqueNamePropertyEdges: 0,
|
|
uniqueNamePropertyAmbiguous: 0,
|
|
uniqueNamePropertyNarrowed: 0,
|
|
uniqueNamePropertyAmbiguousNames: [],
|
|
uniqueNamePropertyCrossLanguage: 0,
|
|
uniqueNamePropertyCrossLanguageNames: [],
|
|
resolutionOutcomes: [],
|
|
undecidedSatisfaction: [],
|
|
functionSummaries: [],
|
|
callSummaries: [],
|
|
});
|
|
|
|
const graph = createKnowledgeGraph();
|
|
const ctx: PipelineContext = {
|
|
repoPath: repoDir,
|
|
graph,
|
|
onProgress: () => {},
|
|
pipelineStart: Date.now(),
|
|
};
|
|
const structure: StructureOutput = {
|
|
scannedFiles: [{ path: 'broken.py', size: 13 }],
|
|
allPaths: ['broken.py'],
|
|
allPathSet: new Set(['broken.py']),
|
|
totalFiles: 1,
|
|
};
|
|
const parse = {
|
|
model: createSemanticModel(),
|
|
parsedFiles: [],
|
|
contentLanguageByPath: new Map(),
|
|
scopeExtractionFailures: ['broken.py'],
|
|
} as unknown as ParseOutput;
|
|
const deps = new Map<string, PhaseResult<unknown>>([
|
|
['structure', phaseResult('structure', structure)],
|
|
['parse', phaseResult('parse', parse)],
|
|
['crossFile', phaseResult('crossFile', {})],
|
|
]);
|
|
|
|
const output = await scopeResolutionPhase.execute(ctx, deps);
|
|
|
|
expect(runScopeResolutionMock).toHaveBeenCalledOnce();
|
|
expect(output.scopeExtractionFailures).toEqual(['broken.py']);
|
|
});
|
|
|
|
it('uses the parse phase content classification for ambiguous headers', async () => {
|
|
repoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'scope-phase-header-language-'));
|
|
fs.writeFileSync(
|
|
path.join(repoDir, 'ObjectiveC.h'),
|
|
'@interface ObjectiveC : NSObject\n@end\n',
|
|
);
|
|
|
|
runScopeResolutionMock.mockReturnValue({
|
|
filesProcessed: 1,
|
|
filesSkipped: 0,
|
|
scopeExtractionFailedPaths: [],
|
|
importsEmitted: 0,
|
|
resolve: { unresolved: 0 },
|
|
referenceEdgesEmitted: 0,
|
|
referenceSkipped: 0,
|
|
propertyDispatchSkippedKeys: 0,
|
|
importedValueRefEdges: 0,
|
|
uniqueNamePropertyEdges: 0,
|
|
uniqueNamePropertyNarrowed: 0,
|
|
uniqueNamePropertyAmbiguous: 0,
|
|
uniqueNamePropertyAmbiguousNames: [],
|
|
uniqueNamePropertyCrossLanguage: 0,
|
|
uniqueNamePropertyCrossLanguageNames: [],
|
|
resolutionOutcomes: [],
|
|
undecidedSatisfaction: [],
|
|
functionSummaries: [],
|
|
callSummaries: [],
|
|
});
|
|
|
|
const ctx: PipelineContext = {
|
|
repoPath: repoDir,
|
|
graph: createKnowledgeGraph(),
|
|
onProgress: () => {},
|
|
pipelineStart: Date.now(),
|
|
};
|
|
const structure: StructureOutput = {
|
|
scannedFiles: [{ path: 'ObjectiveC.h', size: 38 }],
|
|
allPaths: ['ObjectiveC.h'],
|
|
allPathSet: new Set(['ObjectiveC.h']),
|
|
totalFiles: 1,
|
|
};
|
|
const parse = {
|
|
model: createSemanticModel(),
|
|
parsedFiles: [],
|
|
contentLanguageByPath: new Map([['ObjectiveC.h', SupportedLanguages.ObjectiveC]]),
|
|
scopeExtractionFailures: [],
|
|
} as unknown as ParseOutput;
|
|
const deps = new Map<string, PhaseResult<unknown>>([
|
|
['structure', phaseResult('structure', structure)],
|
|
['parse', phaseResult('parse', parse)],
|
|
['crossFile', phaseResult('crossFile', {})],
|
|
]);
|
|
|
|
await scopeResolutionPhase.execute(ctx, deps);
|
|
|
|
expect(runScopeResolutionMock).toHaveBeenCalledWith(
|
|
expect.any(Object),
|
|
expect.objectContaining({ language: SupportedLanguages.ObjectiveC }),
|
|
);
|
|
});
|
|
});
|