From 09b031d04400eb812e13355d6e2d90c8e60b125c Mon Sep 17 00:00:00 2001 From: Don Isaac Date: Sat, 12 Oct 2024 22:49:45 -0400 Subject: [PATCH] fix(parser): uncaught mismatch between JSX opening/closing tags (#14528) --- src/js_parser.zig | 6 +++-- test/regression/issue/14477/14477.test.ts | 23 +++++++++++++++++++ .../issue/14477/builtin-mismatch.tsx | 1 + .../issue/14477/component-mismatch.tsx | 2 ++ .../issue/14477/non-identifier-mismatch.tsx | 3 +++ 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 test/regression/issue/14477/14477.test.ts create mode 100644 test/regression/issue/14477/builtin-mismatch.tsx create mode 100644 test/regression/issue/14477/component-mismatch.tsx create mode 100644 test/regression/issue/14477/non-identifier-mismatch.tsx diff --git a/src/js_parser.zig b/src/js_parser.zig index 2e01434404079f..a948bc39d1bf2e 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -530,7 +530,8 @@ const JSXTag = struct { }; data: Data, range: logger.Range, - name: string = "", + /// Empty string for fragments. + name: string, pub fn parse(comptime P: type, p: *P) anyerror!JSXTag { const loc = p.lexer.loc(); @@ -559,6 +560,7 @@ const JSXTag = struct { .data = name, }, loc) }, .range = tag_range, + .name = name, }; } @@ -15778,7 +15780,7 @@ fn NewParser_( const end_tag = try JSXTag.parse(P, p); if (!strings.eql(end_tag.name, tag.name)) { - try p.log.addRangeErrorFmt(p.source, end_tag.range, p.allocator, "Expected closing tag \\ to match opening tag \\<{s}>", .{ + try p.log.addRangeErrorFmt(p.source, end_tag.range, p.allocator, "Expected closing tag \\ to match opening tag \\<{s}\\>", .{ end_tag.name, tag.name, }); diff --git a/test/regression/issue/14477/14477.test.ts b/test/regression/issue/14477/14477.test.ts new file mode 100644 index 00000000000000..b6dccc08d37788 --- /dev/null +++ b/test/regression/issue/14477/14477.test.ts @@ -0,0 +1,23 @@ +import { expect, test } from "bun:test"; +import { bunEnv, bunExe } from "harness"; +import { join } from "path"; +import fs from "fs"; + +test("JSXElement with mismatched closing tags produces a syntax error", async () => { + const files = await fs.promises.readdir(import.meta.dir); + const fixtures = files.filter(file => !file.endsWith(".test.ts")).map(fixture => join(import.meta.dir, fixture)); + + const bakery = fixtures.map( + fixture => + Bun.spawn({ + cmd: [bunExe(), fixture], + cwd: import.meta.dir, + stdio: ["inherit", "inherit", "inherit"], + env: bunEnv, + }).exited, + ); + + // all subprocesses should fail. + const exited = await Promise.all(bakery); + expect(exited).toEqual(Array.from({ length: fixtures.length }, () => 1)); +}); diff --git a/test/regression/issue/14477/builtin-mismatch.tsx b/test/regression/issue/14477/builtin-mismatch.tsx new file mode 100644 index 00000000000000..6e099b535669a6 --- /dev/null +++ b/test/regression/issue/14477/builtin-mismatch.tsx @@ -0,0 +1 @@ +console.log(

); diff --git a/test/regression/issue/14477/component-mismatch.tsx b/test/regression/issue/14477/component-mismatch.tsx new file mode 100644 index 00000000000000..82fd9088322660 --- /dev/null +++ b/test/regression/issue/14477/component-mismatch.tsx @@ -0,0 +1,2 @@ + +console.log(); diff --git a/test/regression/issue/14477/non-identifier-mismatch.tsx b/test/regression/issue/14477/non-identifier-mismatch.tsx new file mode 100644 index 00000000000000..a3f474fe220f07 --- /dev/null +++ b/test/regression/issue/14477/non-identifier-mismatch.tsx @@ -0,0 +1,3 @@ +// mismatch where openening tag is not a valid IdentifierName, but is a valid +// JSXIdentifierName +console.log(

);