GitNexus/gitnexus/test/helpers/scope-model.ts
Gergő Magyar 911151e230
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
fix(resolution): resolve Go pointer-receiver calls, and report the program boundary instead of hedging (#2766) (#2782)
2026-08-01 22:42:18 +01:00

76 lines
3.4 KiB
TypeScript

/**
* Shared scope-model builder for scope-resolution unit tests.
*
* `finalizeScopeModel({ hooks: { resolveImportTarget, mergeBindings } })` is
* the incantation a test must perform before it can call any resolution pass,
* and it was being hand-copied per file — and, inside a single file, once per
* language. The only thing that ever varied was the resolver, the source and
* the file path, so those are the parameters here.
*
* The model is built from REAL extraction rather than a hand-assembled index
* on purpose: the defects these tests pin are cases where source-level
* intuition about what a binding CONTAINS is wrong (Go normalizes a free
* parameter's `*Host` to `Host` at capture but leaves a method receiver's
* spelled `*Host` intact), so a fixture that asserted the binding shape by
* hand would pin the intuition instead of the code.
*/
import type { ParsedFile, ScopeId } from 'gitnexus-shared';
import { extractParsedFile } from '../../src/core/ingestion/scope-extractor-bridge.js';
import { finalizeScopeModel } from '../../src/core/ingestion/finalize-orchestrator.js';
import {
buildWorkspaceResolutionIndex,
type WorkspaceResolutionIndex,
} from '../../src/core/ingestion/scope-resolution/workspace-index.js';
import type { ScopeResolver } from '../../src/core/ingestion/scope-resolution/contract/scope-resolver.js';
import type { ScopeResolutionIndexes } from '../../src/core/ingestion/model/scope-resolution-indexes.js';
export interface ScopeModelFixture {
/** The extracted file — for tests that must start a fold in a scope other
* than the module scope, or read the raw scope list. */
readonly parsed: ParsedFile;
readonly scopes: ScopeResolutionIndexes;
readonly index: WorkspaceResolutionIndex;
/** `parsed.referenceSites`: the sites a resolution pass walks. */
readonly sites: ParsedFile['referenceSites'];
/** `parsed.moduleScope`: the default `inScope` for a top-level position. */
readonly moduleScope: ScopeId;
/** Carried so callers can thread the language's own contract hooks
* (`elementTypeOf`, `stripTypePreservingDecoration`, …) without naming the
* resolver a second time. */
readonly resolver: ScopeResolver;
}
/**
* Extract `source` with `resolver`'s provider, run owner population and the
* shared finalize, and return the resolution indexes a pass needs.
*
* Throws rather than asserting, because callers build fixtures at module load
* where a failed `expect` has no test to attach to.
*/
export function buildScopeModel(
resolver: ScopeResolver,
source: string,
filePath: string,
): ScopeModelFixture {
const parsed = extractParsedFile(resolver.languageProvider, source, filePath);
if (parsed === undefined) throw new Error(`scope extraction failed for ${filePath}`);
resolver.populateOwners(parsed);
const parsedFiles: ParsedFile[] = [parsed];
const allFilePaths = new Set(parsedFiles.map((p) => p.filePath));
const scopes = finalizeScopeModel(parsedFiles, {
hooks: {
resolveImportTarget: (targetRaw, fromFile) =>
resolver.resolveImportTarget(targetRaw, fromFile, allFilePaths),
mergeBindings: (existing, incoming, scopeId) =>
resolver.mergeBindings(existing, incoming, scopeId),
},
});
return {
parsed,
scopes,
index: buildWorkspaceResolutionIndex(parsedFiles),
sites: parsed.referenceSites,
moduleScope: parsed.moduleScope,
resolver,
};
}