semanticDiff
On this page

semanticDiff()

Function in Tooling

Compare two modules at the IR and reflection layer.

import { semanticDiff } from 'typeshade'

The signature, the description and the examples come from the compiler's own source at commit 26de7be8.

Syntax

function semanticDiff(
a: ModuleDecl,
b: ModuleDecl,
opts: SemanticDiffOptions & { readonly transforms: readonly EmitPlugin[] },
): ClassifiedSemanticDiff;
function semanticDiff(
a: ModuleDecl,
b: ModuleDecl,
opts?: SemanticDiffOptions,
): SemanticDiff;

Parameters

a ModuleDecl

the reference module, the development side when transforms is declared.

b ModuleDecl

the module to compare against it, the transformed side.

opts SemanticDiffOptions & { readonly transforms: readonly EmitPlugin[] }

the axes to disregard, names and declOrder by default, and optionally the declared transform pipeline.

Return value

ClassifiedSemanticDiff

the four buckets, and explained when transforms was declared.

Description

A textual diff of emitted source is hard to read once the optimizer and the production plugins have run; this comparison works above that noise.

It reports four buckets: the entry and vertex interface, the bind-group and layout resources, the constants (module consts, overrides, and the multiset of body literals), and the per-statement controlFlow skeleton. Empty in all four means the two modules agree on everything a host binds to and everything the code does. Each line is prefixed - for a fact only in a and + for one only in b, and isSemanticallyEqual is the all-empty test.

A pass that legitimately changes the program is not expected to be empty here. Inlining rewrites call sites and duplicates literals, which is exactly what the buckets report. Declare such a pass in transforms, the same plugin array the production emit takes, and every difference the declared pipeline provably causes moves into explained, one entry naming the plugin, the bucket and the line.

A line moves to explained only when applying that plugin’s own transformIR to the reference side actually removes the line from the diff. A regression that merely looks like an optimizer rewrite stays in its bucket, so a check that compares a development build against a production build fails only on the unexplained differences.

Text-stage plugins explain nothing, because the comparison never sees emitted text. Declaring the full production array is therefore safe: minify and aliasTypes contribute no explained entries and remove no coverage, so you can pass the array you actually ship.

Renaming with mangle produces no differences under the default options, because 'names' canonicalizes exactly the identifiers that pass is free to rewrite.

Examples

Example

import { semanticDiff, isSemanticallyEqual } from 'typeshade'
import { inline, obfuscate } from 'typeshade/emit-prod'
const d = semanticDiff(devModule, prodModule, { transforms: [inline(), ...obfuscate()] })
isSemanticallyEqual(d) // true when prod differs from dev only as the declared pipeline dictates
d.explained // [{ transform: 'inline', bucket: 'controlFlow', line: '…' }, …]

In the guide

See also

Source

src/core/semantic-diff.ts, line 558, at commit 26de7be8

Edit this page Report a problem