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
11 changes: 9 additions & 2 deletions Runtime/BuiltIns/BuiltInTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public static class BuiltInTypes
"indexOf" => new TypeInfo.Function([StringType, NumberType], NumberType, RequiredParams: 1),
"toUpperCase" => new TypeInfo.Function([], StringType),
"toLowerCase" => new TypeInfo.Function([], StringType),
// toLocale{Lower,Upper}Case(locales?) — optional locale arg (string | string[] | Intl.Locale;
// Any covers every overload the runtime accepts). ECMA-402 §String.prototype.
"toLocaleLowerCase" => new TypeInfo.Function([AnyType], StringType, RequiredParams: 0),
"toLocaleUpperCase" => new TypeInfo.Function([AnyType], StringType, RequiredParams: 0),
"trim" => new TypeInfo.Function([], StringType),
// replace(searchValue, replaceValue) accepts string | RegExp for the
// pattern and string | (match, ...groups) => string for the value.
Expand Down Expand Up @@ -87,7 +91,9 @@ public static class BuiltInTypes
"replaceAll" => new TypeInfo.Function([AnyType, AnyType], StringType),
"at" => new TypeInfo.Function([NumberType], StringType), // returns string | undefined in TS
"normalize" => new TypeInfo.Function([StringType], StringType, RequiredParams: 0),
"localeCompare" => new TypeInfo.Function([StringType], NumberType, RequiredParams: 0),
// localeCompare(that, locales?, options?) — ECMA-402 adds the optional
// locales/options args on top of ECMA-262's single `that` param.
"localeCompare" => new TypeInfo.Function([StringType, AnyType, AnyType], NumberType, RequiredParams: 0),
"toString" => new TypeInfo.Function([], StringType), // primitive wrapper method
"match" => new TypeInfo.Function([AnyType], AnyType),
"matchAll" => new TypeInfo.Function([AnyType], new TypeInfo.Array(AnyType)),
Expand Down Expand Up @@ -1308,7 +1314,8 @@ [new TypeInfo.Function([elementType, NumberType], BooleanType)],
"length", "charAt", "substring", "indexOf", "toUpperCase", "toLowerCase", "trim", "replace",
"split", "includes", "startsWith", "endsWith", "slice", "substr", "repeat", "padStart",
"padEnd", "charCodeAt", "codePointAt", "concat", "lastIndexOf", "trimStart", "trimEnd",
"replaceAll", "at", "normalize", "localeCompare", "toString", "match", "matchAll", "search",
"replaceAll", "at", "normalize", "localeCompare", "toLocaleLowerCase", "toLocaleUpperCase",
"toString", "match", "matchAll", "search",
];

private static readonly string[] ArrayMemberNames =
Expand Down
19 changes: 12 additions & 7 deletions STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,15 +503,20 @@ Two external corpora pin SharpTS against canonical references. Both run as stand

`SharpTS.TypeScriptConformance/` runs a subset of [microsoft/TypeScript's conformance corpus](https://github.com/microsoft/TypeScript/tree/main/tests/cases/conformance) and diffs our type-checker diagnostics against `tsc`'s `*.errors.txt` baselines. Pinned to TS v5.5.4. Pass classification is on `(line, tsCode)` tuples — see [#80](https://github.com/nickna/SharpTS/issues/80) for the tracking epic.

| Subset | Tests | Pass | Fail | ParseError | Skipped |
|---|---:|---:|---:|---:|---:|
| `types/typeRelationships/assignmentCompatibility/` | 70 | 16 (22.9%) | 50 | 4 | 0 |
| `types/conditional/` | 9 | 0 | 5 | 3 | 1 |
| **Total** | **79** | **16 (20.3%)** | **55** | **7** | **1** |
The committed subset spans `types/typeRelationships/{assignmentCompatibility,subtypesAndSuperTypes}`, `types/conditional`, and the ES-version library folders (`Symbols`, `es6/Symbols`, `es2016`–`es2023`, `esnext`):

The parser is no longer the bottleneck. An extended parser sweep took the subset's `ParseError` count from 57 → 7 (≈86%): ambient `declare` of non-class declarations, `declare function`, generic/this/conditional/mapped/indexed-access/constructor/leading-operator types, `module Foo {}` namespaces, call/construct/index signatures, keyword & string/numeric property names, function-type and arrow optional/rest parameters, and more. Negative-test matching itself was unlocked by propagating canonical `TSnnnn` codes through the type-checker's error-recovery path ([#125](https://github.com/nickna/SharpTS/issues/125)), which also added a `strictNullChecks` option (default on; the runner follows each test's `@strict`/`@strictNullChecks` directive).
| Bucket | Count | Share |
|---|---:|---:|
| `Pass` | 182 | 61.5% |
| `Fail` | 75 | 25.3% |
| `ParseError` | 24 | 8.1% |
| `Skipped` (multi-file 8 / lib-drift 3 / directive 3) | 14 | 4.7% |
| `TypeCheckError` | 1 | 0.3% |
| **Total** | **296** | |

The dominant bucket is now `Fail` — tests that parse and reach the type checker but whose diagnostic set doesn't yet match `tsc`'s. These are mostly the `assignmentCompatibility` cases, which require deeper callable / constructable / structural assignability. The largest remaining levers are structural class-to-class assignability ([#129](https://github.com/nickna/SharpTS/issues/129) — SharpTS is nominal by design) and loading `tsc`'s `lib.*.d.ts` ([#99](https://github.com/nickna/SharpTS/issues/99)).
The parser is no longer the dominant bottleneck (an earlier sweep took the original subset's `ParseError` count from 57 → 7): ambient `declare` of non-class declarations, `declare function`, generic/this/conditional/mapped/indexed-access/constructor/leading-operator types, `module Foo {}` namespaces, call/construct/index signatures, keyword & string/numeric property names, function-type and arrow optional/rest parameters, and more. Negative-test matching itself was unlocked by propagating canonical `TSnnnn` codes through the type-checker's error-recovery path ([#125](https://github.com/nickna/SharpTS/issues/125)), which also added a `strictNullChecks` option (default on; the runner follows each test's `@strict`/`@strictNullChecks` directive). The residual `ParseError` bucket is now concentrated in the `symbolProperty*`/`symbolType*` tests — computed well-known-symbol names in *value/class* position (the [#99](https://github.com/nickna/SharpTS/issues/99) Phase-A parser work only hardened *type* position).

The dominant bucket is `Fail` — tests that parse and reach the type checker but whose diagnostic set doesn't yet match `tsc`'s. A [#99](https://github.com/nickna/SharpTS/issues/99) measurement spike over the lib-sensitive folders decomposed these: of 66 lib-folder Fails, ~17 are **spurious** (SharpTS emits an error `tsc` doesn't), a long tail are individual checker gaps (assignability, symbol arithmetic, computed-name constraints), and only ~6 are explicit lib-version drift (`TS2550`/`TS2583`/`TS2585`). **The finding: loading `tsc`'s `lib.*.d.ts` is _not_ the current pass-rate lever — checker breadth and parser gaps dominate the divergence.** Loading `lib.*.d.ts` ([#99](https://github.com/nickna/SharpTS/issues/99)) remains valuable as an *enabler* (it removes the "`Pass` = coincidence" asterisk since globals resolve to `any` today, unblocks DOM/JSX wholesale, and is a prerequisite for per-node types/symbols in [#88](https://github.com/nickna/SharpTS/issues/88)), but the near-term levers are checker correctness (kill the spurious-error cases; structural class-to-class assignability [#129](https://github.com/nickna/SharpTS/issues/129)) and finishing symbol-name parsing.

`Skipped` includes:
- **Multi-file tests** (`Skipped:multi-file-deferred`) — cross-file resolution into the runner is follow-up work.
Expand Down
55 changes: 52 additions & 3 deletions SharpTS.TypeScriptConformance/TypeScriptConformanceRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,64 @@ public TypeScriptConformanceResult RunOne(string testFilePath)
/// <c>tests/baselines/reference/&lt;testname&gt;.errors.txt</c> (flat directory,
/// no folder mirroring). Returns the expected path even if the file
/// doesn't exist — caller treats absence as "no expected diagnostics."
///
/// Multi-target tests (<c>// @target: es2015, es2020, ...</c>) emit one
/// baseline per target — <c>name(target=X).errors.txt</c> — and no plain
/// file. SharpTS has a single always-latest world model (globals are always
/// available, no per-target lib), so we compare against the newest available
/// target variant. Without this, those tests are scored against an empty
/// baseline and every real diagnostic is mis-counted as spurious.
/// </summary>
private string ResolveBaselinePath(string testFilePath)
{
var basename = Path.GetFileNameWithoutExtension(testFilePath);
return Path.Combine(
TypeScriptConformancePaths.BaselinesDir(_typescriptRoot),
$"{basename}.errors.txt");
var dir = TypeScriptConformancePaths.BaselinesDir(_typescriptRoot);
var plain = Path.Combine(dir, $"{basename}.errors.txt");
if (File.Exists(plain)) return plain;
return ResolveNewestTargetBaseline(dir, basename) ?? plain;
}

/// <summary>
/// Newest-target <c>name(target=X).errors.txt</c> baseline for a multi-target
/// test, or null if none exist. "Newest" follows ES ordering
/// (<c>es3 &lt; es5 &lt; es2015 &lt; ... &lt; esnext</c>) so the chosen baseline
/// matches SharpTS's always-latest lib surface.
/// </summary>
private static string? ResolveNewestTargetBaseline(string baselinesDir, string basename)
{
string? best = null;
var bestRank = int.MinValue;
foreach (var path in Directory.EnumerateFiles(baselinesDir, $"{basename}(target=*).errors.txt"))
{
var file = Path.GetFileName(path);
const string marker = "(target=";
var open = file.IndexOf(marker, StringComparison.Ordinal);
var close = file.IndexOf(").errors.txt", StringComparison.Ordinal);
if (open < 0 || close <= open + marker.Length) continue;
var target = file.Substring(open + marker.Length, close - (open + marker.Length));
var rank = TargetRank(target);
if (rank > bestRank) { bestRank = rank; best = path; }
}
return best;
}

private static int TargetRank(string target) => target.Trim().ToLowerInvariant() switch
{
"es3" => 3,
"es5" => 5,
"es6" or "es2015" => 2015,
"es2016" => 2016,
"es2017" => 2017,
"es2018" => 2018,
"es2019" => 2019,
"es2020" => 2020,
"es2021" => 2021,
"es2022" => 2022,
"es2023" => 2023,
"esnext" => int.MaxValue,
_ => 0,
};

/// <summary>
/// Converts SharpTS diagnostics into the (line, tsCode) match-key form.
/// Drops diagnostics with no <c>TsCode</c> (SharpTS-only — see #95) — they
Expand Down
Loading
Loading