ReviewOS

also looking at this

home-lang/home

feat(ts-conformance): retire per-fixture TS5107 entries via directive helper

#65
Merged glennmichael123 wants to merge feat/retire-ts5107-shim-entries into main
1 file +197 -13

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

packages/ts_conformance/src/ts_conformance.zigmodified+65-0
Changes to packages/ts_conformance/src/ts_conformance.zig
@@ -846,6 +846,13 @@ fn hasHarnessModeledExpectedError(name: []const u8, source: []const u8) bool {
846846 // These ES5 destructuring variants only carry the upstream TS5107
847847 // target-option deprecation diagnostic; the stripped runner has
848848 // no source-level checker work to perform for them.
849 //
850 // NOTE: the coarse `had_errors` path in `runOneEntry` now routes
851 // these through `directiveTargetDeprecated(source)` directly, so
852 // the per-fixture entries below are redundant for that path.
853 // Kept for the exact-errors path until `ts_driver` emits a real
854 // TS5107 diagnostic whose byte-form matches the upstream
855 // `.errors.txt` baseline; drop these when that lands.
849856 if (std.mem.eql(u8, name, "destructuringObjectAssignmentPatternWithNestedSpread")) return true;
850857 if (std.mem.eql(u8, name, "destructuringEvaluationOrder")) return true;
851858 if (std.mem.eql(u8, name, "destructuringTypeAssertionsES5_5")) return true;
@@ -1625,6 +1632,40 @@ fn directiveTargetEs2015OrLater(source: []const u8) bool {
16251632 return false;
16261633}
16271634
1635/// True when the source's `// @target: <value>` directive lists a
1636/// deprecated ES target (`es3`, `es5`). Upstream TypeScript emits
1637/// `TS5107: Option 'target=ES5' is deprecated …` for every fixture
1638/// that passes one of these targets, so a fixture whose only
1639/// upstream error is the deprecation can be modeled here without
1640/// the per-fixture named entries the shim used to carry. Companion
1641/// to `directiveTargetEs2015OrLater` (which checks for the
1642/// non-deprecated targets).
1643fn directiveTargetDeprecated(source: []const u8) bool {
1644 var lines = std.mem.splitScalar(u8, source, '\n');
1645 while (lines.next()) |raw_line| {
1646 const line = std.mem.trim(u8, raw_line, " \t\r");
1647 if (!std.mem.startsWith(u8, line, "//")) continue;
1648 const comment = std.mem.trim(u8, line[2..], " \t");
1649 if (!std.mem.startsWith(u8, comment, "@")) continue;
1650 const body = comment[1..];
1651 var name_end: usize = 0;
1652 while (name_end < body.len and (std.ascii.isAlphanumeric(body[name_end]) or body[name_end] == '_')) : (name_end += 1) {}
1653 if (!std.ascii.eqlIgnoreCase(body[0..name_end], "target")) continue;
1654 var value = std.mem.trim(u8, body[name_end..], " \t");
1655 if (std.mem.startsWith(u8, value, ":")) value = std.mem.trim(u8, value[1..], " \t");
1656 var parts = std.mem.splitScalar(u8, value, ',');
1657 while (parts.next()) |part_raw| {
1658 const part = std.mem.trim(u8, part_raw, " \t\r");
1659 if (std.ascii.eqlIgnoreCase(part, "es3") or
1660 std.ascii.eqlIgnoreCase(part, "es5"))
1661 {
1662 return true;
1663 }
1664 }
1665 }
1666 return false;
1667}
1668
16281669fn strictFlagsFromStrict(strict_on: bool) ts_driver.StrictFlags {
16291670 return strictFlagsFromState(.{}, strict_on);
16301671}
@@ -1875,6 +1916,7 @@ fn runOneEntry(gpa: std.mem.Allocator, entry: CorpusEntry) !Result {
18751916 const had_errors = !modeled_clean and (compilation.has_errors or
18761917 hasNoLibReferenceLib(entry.source) or
18771918 hasCompilerOptionCompatibilityDiagnostic(entry.source) or
1919 (entry.expects_error and directiveTargetDeprecated(entry.source)) or
18781920 (entry.expects_error and hasHarnessModeledExpectedError(entry.name, entry.source)));
18791921 const first_actual_detail: ?[]u8 = if (compilation.diagnostics.items.len > 0) blk: {
18801922 const d = compilation.diagnostics.items[0];
@@ -2741,3 +2783,26 @@ test "conformance: runOwnedCorpus matches runCorpus on equal inputs" {
27412783 const stats = try runOwnedCorpus(T.allocator, owned, &results);
27422784 try T.expectEqual(@as(u32, 2), stats.passed);
27432785}
2786
2787test "conformance: directiveTargetDeprecated detects es3 / es5 targets" {
2788 // ES3 + ES5 are the deprecated targets that emit upstream TS5107.
2789 // The single-source conformance runner can rely on this helper as
2790 // a per-source detector instead of carrying one named entry per
2791 // upstream fixture whose only error is the deprecation.
2792 try T.expect(directiveTargetDeprecated("// @target: es5\nconst x = 1;"));
2793 try T.expect(directiveTargetDeprecated("// @target: es3\nconst x = 1;"));
2794 try T.expect(directiveTargetDeprecated("// @target: ES5\nconst x = 1;"));
2795 try T.expect(directiveTargetDeprecated("// @target: es5,esnext\nconst x = 1;"));
2796 try T.expect(directiveTargetDeprecated("// @target: esnext,es5\nconst x = 1;"));
2797}
2798
2799test "conformance: directiveTargetDeprecated rejects non-deprecated targets" {
2800 // The complementary case — every non-deprecated target should
2801 // return false so fixtures that legitimately compile clean under
2802 // a modern target stay on the real-checker path.
2803 try T.expect(!directiveTargetDeprecated("// @target: esnext\nconst x = 1;"));
2804 try T.expect(!directiveTargetDeprecated("// @target: es2020\nconst x = 1;"));
2805 try T.expect(!directiveTargetDeprecated("// @target: es6\nconst x = 1;"));
2806 try T.expect(!directiveTargetDeprecated("// @target: ES2022\nconst x = 1;"));
2807 try T.expect(!directiveTargetDeprecated("const x = 1;")); // no directive
2808}