Skip to content

TS Conformance: assign TSnnnn diagnostic codes at type-check throw sites #95

@nickna

Description

@nickna

Part of #80. Blocks #84.

Why this is its own issue

#84 frames TS-code mapping as a sub-decision inside the runner work ("option 1: hand-mapped table; option 2: line-only match"). It isn't really a sub-decision — it determines whether Pass in the conformance baseline means anything. Without consistent TSnnnn codes on our diagnostics, the baseline either:

  • Matches on line only (option 2): so lossy that adjacent-line diagnostics with different TS codes both look like a match, and a test expecting TS2322 at L5 + TS7006 at L6 passes if we report any errors at L5 and L6.
  • Or relies on a tiny coarse-bucket map (option 1 as currently described): our 5 type-error codes can't distinguish the dozens of TS codes that all map to TypeMismatch (e.g. TS2322 assignability, TS2345 argument type, TS2741 missing property, TS2740 missing properties, ...).

The real situation in our checker:

  • Diagnostics/DiagnosticCode.cs — 5 type-error codes total (TypeError, TypeMismatch, UndefinedMember, InvalidCall, TypeOperation).
  • TypeSystem/332 distinct throw new TypeCheckException(...) call sites across 23 files. Each is a real, distinguishable diagnostic shape that today collapses into one of those 5 codes.
  • TS itself has hundreds of TSnnnn codes (diagnosticMessages.json in the vendored checkout enumerates them).

So the lift is: tag each of our 332 throw sites with the TS code that most closely corresponds, without refactoring the DiagnosticCode enum schema.

Recommended approach: tag at the call site

Add a tsCode string parameter to TypeCheckException (and Diagnostic). Every throw site picks the most-applicable TSnnnn:

throw new TypeCheckException(
    $"Type '{actual}' is not assignable to type '{expected}'.",
    DiagnosticCode.TypeMismatch,
    tsCode: \"TS2322\",
    location);

Pros:

  • No enum thrash. DiagnosticCode keeps its 5-bucket shape; tsCode is an additive freeform string for conformance diffing only.
  • 332 mechanical edits, parallelizable, can land in chunks (one file at a time).
  • Conformance runner (TS Conformance: errors-baseline runner + bucket diff harness #84) just consumes Diagnostic.TsCode and matches on (file, line, tsCode) tuples — clean, lossless, version-pinned to whichever TS tag we vendor.
  • Internal SHARPTSnnn codes still flow through to MSBuild output for SharpTS users; only conformance runner cares about tsCode.

Cons:

  • 332 edits is real work. Mitigated by chunking and by leaving the tsCode nullable initially — un-tagged sites just don't participate in conformance matching yet.
  • Some of our throw sites have no clean TS analogue (e.g. SharpTS-specific @DotNetType errors). Those leave tsCode null and are excluded from baseline diff for that test.

Rejected alternatives

  • Refactor DiagnosticCode to ~100 finer enum values. Schema thrash, touches every consumer of the enum, and doesn't give us anything tsCode doesn't.
  • Match on line only (TS Conformance: errors-baseline runner + bucket diff harness #84 option 2). Too lossy. Discussed above.
  • Match on text snippets of TS messages. TS rewords messages between versions; would thrash the baseline.

Work

  1. Add nullable string? TsCode to TypeCheckException and Diagnostic. No behavior change for un-tagged sites.
  2. Build a reference table: walk external/typescript/src/compiler/diagnosticMessages.json once and produce a TSnnnn → message-template lookup as a comment block in code (or generated file). Helps reviewers pick the right code at each site.
  3. Tag the 332 throw sites in waves, one TypeChecker.*.cs partial at a time. Order by frequency (Properties.cs:41 sites, Expressions.cs:34, Calls.cs:32 first).
  4. Land an analyzer or unit test that warns when a new TypeCheckException is added without a tsCode (soft warning, doesn't break the build). Optional but prevents drift.

Acceptance

  • Every throw new TypeCheckException(...) site in TypeSystem/ either has a tsCode or is explicitly marked SharpTS-specific (e.g. tsCode: null with a // SharpTS-only comment).
  • Diagnostic.TsCode is exposed on the public collector surface.
  • TS Conformance: errors-baseline runner + bucket diff harness #84 can match (file, line, tsCode) against *.errors.txt baselines without further plumbing.

Out of scope

  • Rewording our error messages to match TS message text. Wording match is explicitly not the goal — we match on codes only (per TS Conformance: errors-baseline runner + bucket diff harness #84 strategy). Our internal messages stay as-is.
  • Mapping runtime / parse / compile diagnostics. This issue is type-checker-only because the conformance corpus is type-checker-focused.

Reference

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions