Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion schema.neo4j.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"schema_version": "2.1.0",
"schema_version": "2.0.0",
"generator": "codeanalyzer-typescript",
"marker_labels": [],
"node_labels": [
Expand Down
4 changes: 2 additions & 2 deletions src/build/neo4j/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function project(app: TSAnalysis, _appName?: string): GraphRows {
for (const ext of Object.values(root.external_symbols ?? {})) {
b.node([CAN, "TSExternal"], "id", ext.id, prune({ id: ext.id, kind: "external", name: ext.name, module: ext.module }));
}
// 2.1.0: `synthesized_callables` is mostly a compatibility index (old id → tree id) whose targets
// #92: `synthesized_callables` is mostly a compatibility index (old id → tree id) whose targets
// are already projected as tree nodes. Only the residual fallback entries — a signature no
// provider could name, recognisable because the map key IS the entry's own id — still need a
// standalone node, so call-graph edges pointing at them do not dangle.
Expand Down Expand Up @@ -193,7 +193,7 @@ const ANON_SIG = /\.<anon@\d+:\d+>$/;

function projectCallable(b: RowBuilder, c: TSCallable, owner: NodeRef, ownerRel: string, fileKey: string, source: string): void {
// An unnamed callable carries :TSAnonymousCallable alongside :TSCallable — one node, two labels,
// reached by ordinary containment. That is what keeps pre-2.1.0 MATCH (:TSAnonymousCallable)
// reached by ordinary containment. That is what keeps older MATCH (:TSAnonymousCallable)
// queries working and puts these nodes on the snapshot wipe's containment walk (issue #75).
const labels = ANON_SIG.test(c.signature) ? [CAN, "TSCallable", "TSAnonymousCallable"] : [CAN, "TSCallable"];
const node = b.node(labels, "id", c.id, callableProps(c, fileKey, source));
Expand Down
6 changes: 3 additions & 3 deletions src/build/neo4j/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* SCHEMA_VERSION: MAJOR on a breaking change (renamed/removed label, relationship or key), MINOR
* on additive. v2 is a MAJOR bump from v1 (keys moved signature→can:// id; labels reshaped).
*/
export const SCHEMA_VERSION = "2.1.0";
export const SCHEMA_VERSION = "2.0.0";

export type PropType = "string" | "integer" | "float" | "boolean" | "string[]" | "integer[]";

Expand Down Expand Up @@ -67,7 +67,7 @@ export const NODE_LABELS: NodeLabel[] = [
// Repository-artifact layer (#101, python PR #160 parity): language-NEUTRAL labels — the
// deliberate exception to TS-prefixing, so sibling analyzers MERGE onto the same
// :Artifact/:Package/:ConfigKey nodes. Edges that stay this analyzer's own claim keep the TS_
// prefix. Additive within 2.1.0 — SCHEMA_VERSION moves only when every analyzer re-baselines
// prefix. Additive within 2.0.0 — SCHEMA_VERSION moves only when every analyzer re-baselines
// together.
{
label: "Artifact",
Expand Down Expand Up @@ -163,7 +163,7 @@ export const NODE_LABELS: NodeLabel[] = [
},
{ label: "TSExternal", mergeLabel: CAN, key: "id", properties: { ...COMMON, name: "string", module: "string" } },
{
// 2.1.0: a marker label carried *alongside* :TSCallable by an unnamed arrow / function
// #92: a marker label carried *alongside* :TSCallable by an unnamed arrow / function
// expression, which is now a real tree node reached by TS_DECLARES from its enclosing
// callable. It is no longer a node kind of its own — the property set is TSCallable's — but
// the label is retained so existing MATCH (:TSAnonymousCallable) queries keep resolving.
Expand Down
2 changes: 1 addition & 1 deletion src/schema/emit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { resolveLiteralConfigUses } from "../semantic_analysis/configUse";
import { widenConfigUses } from "../dataflow/configUse";

const LANGUAGE = "typescript";
const SCHEMA_VERSION = "2.1.0";
const SCHEMA_VERSION = "2.0.0";
const ANALYZER_NAME = "codeanalyzer-typescript";
/** Highest analysis level this emitter populates today (L1 tree, L2 call graph, L3/L4 dataflow). */
const MAX_IMPLEMENTED = 4;
Expand Down
8 changes: 4 additions & 4 deletions src/schema/homing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* application root, so the no-dangling rule holds.
*
* - `homeExternals`: external library call targets → `can://…/@external/<module>/<name>` nodes.
* - `homeSynthesized`: the 2.1.0 anonymous-callable compatibility index (pre-2.1.0 id → the
* - `homeSynthesized`: the #92 anonymous-callable compatibility index (the older id → the
* tree id that replaced it), plus residual fallback nodes for signatures no provider could
* name (recognizable because the map key equals the entry's own id).
*
Expand Down Expand Up @@ -42,11 +42,11 @@ export function homeExternals(app: AnalysisInternal, appId: string, idBySig: Map
}

/**
* The compatibility index for anonymous callables (schema 2.1.0).
* The compatibility index for anonymous callables (#92).
*
* Anonymous callables are real nodes in the containment tree, signed positionally
* (`<enclosing-sig>.<anon@line:col>`) and reachable by containment. This map is not a node
* registry: it maps the **pre-2.1.0 id** of each anonymous callable — `<enclosing-can-id>@<line>:<col>`,
* registry: it maps the **older id** of each anonymous callable — `<enclosing-can-id>@<line>:<col>`,
* derived from the old `<enclosing-sig>:<line:col>` signature — onto the tree id that replaced it,
* so a consumer holding an old id can still resolve it.
*
Expand All @@ -65,7 +65,7 @@ export function homeSynthesized(app: AnalysisInternal, appId: string, idBySig: M
const m = /^(.*?)((?:\.<anon@\d+:\d+>)+)$/.exec(sig);
if (!m) continue;
const host = idBySig.get(m[1] as string);
if (!host) continue; // module-level anonymous callable — no resolvable pre-2.1.0 id
if (!host) continue; // module-level anonymous callable — no resolvable older id
const last = /<anon@(\d+):(\d+)>$/.exec(sig) as RegExpExecArray;
out[`${host}@${last[1]}:${last[2]}`] = { id, kind: "callable" };
}
Expand Down
6 changes: 3 additions & 3 deletions src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ export interface TSExternalSymbol {
}

// A first-party anonymous callback a call-graph builder resolved as an edge endpoint but could
// not name against the symbol table (a residual-fallback safety net; since 2.1.0 the tree names
// not name against the symbol table (a residual-fallback safety net; since #92 the tree names
// anonymous callables positionally, so this map is normally empty). The map key IS the
// synthesized signature, so an edge `source`/`target` byte-matches it like a real signature.
export interface TSSynthesizedCallable {
Expand Down Expand Up @@ -491,7 +491,7 @@ export interface AnalysisInternal {
// ----------------------------------------------------------------------------------------------

export interface TSAnalysis {
schema_version: string; // "2.1.0"
schema_version: string; // "2.0.0"
language: string; // "typescript"
max_level: number; // highest level populated; consumers read this, not key-sniffing
k_limit?: number; // access-path depth bound for the L3/L4 dataflow (present at L3+)
Expand Down Expand Up @@ -522,7 +522,7 @@ export interface TSApplication {
config_reads: TSConfigRead[];
// TS-additive (parity): edge endpoints outside the containment tree need an id home.
external_symbols?: Record<string, import("./homing").TSExternalNode>; // L2 — library call targets, keyed by id
// L2 — 2.1.0 compatibility index: pre-2.1.0 anonymous-callable id → the tree id that replaced
// L2 — #92 compatibility index: the older anonymous-callable id → the tree id that replaced
// it. Entries whose key equals their own `id` are the residual fallback nodes for signatures no
// provider could name.
synthesized_callables?: Record<string, import("./homing").TSSynthesizedNode>;
Expand Down
6 changes: 3 additions & 3 deletions test/anonymous-callables.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Issue #92 (schema 2.1.0): an unnamed arrow / function expression is a callable in its own right.
* Issue #92: an unnamed arrow / function expression is a callable in its own right.
* It is tree-contained under its enclosing callable with a durable positional signature segment
* (`<anon@line:col>`), carries its own body/cfg/cdg/ddg and formal-in vertices, and owns the call
* sites that used to be attributed to the callable that merely encloses it.
Expand Down Expand Up @@ -136,8 +136,8 @@ describe("anonymous callables are first-class (issue #92)", () => {
}
});

test("the envelope declares schema 2.1.0", () => {
expect(application.schema_version).toBe("2.1.0");
test("the envelope declares schema 2.0.0", () => {
expect(application.schema_version).toBe("2.0.0");
});
});

Expand Down
2 changes: 1 addition & 1 deletion test/schema-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ function allIds(): string[] {

describe("schema v2 — L1 envelope", () => {
test("root envelope matches the canonical shape", () => {
expect(v2.schema_version).toBe("2.1.0");
expect(v2.schema_version).toBe("2.0.0");
expect(v2.language).toBe("typescript");
expect(v2.max_level).toBe(1);
expect(Object.keys(root).sort()).toEqual([
Expand Down
Loading