syntax error rather than crashing for multiple exports with the same name

This commit is contained in:
pfg
2025-01-08 12:29:35 -08:00
parent 3e1016a47a
commit 1cbd4699bc
2 changed files with 34 additions and 12 deletions

View File

@@ -159,6 +159,7 @@ pub const ModuleInfo = struct {
requested_modules: std.AutoArrayHashMap(StringID, FetchParameters),
buffer: std.ArrayList(StringID),
record_kinds: std.ArrayList(RecordKind),
exported_names: std.AutoArrayHashMapUnmanaged(StringID, void),
contains_import_meta: bool,
finalized: bool = false,
@@ -207,18 +208,29 @@ pub const ModuleInfo = struct {
try self._addRecord(.import_info_namespace, &.{ try self.str(module_name), try self.str("*"), try self.str(local_name) });
}
pub fn addExportInfoIndirect(self: *ModuleInfo, export_name: []const u8, import_name: []const u8, module_name: []const u8) !void {
try self._addRecord(.export_info_indirect, &.{ try self.str(export_name), try self.str(import_name), try self.str(module_name) });
const export_name_id = try self.str(export_name);
if (try self._hasOrAddExportedName(export_name_id)) return; // a syntax error will be emitted later in this case
try self._addRecord(.export_info_indirect, &.{ export_name_id, try self.str(import_name), try self.str(module_name) });
}
pub fn addExportInfoLocal(self: *ModuleInfo, export_name: []const u8, local_name: []const u8) !void {
try self._addRecord(.export_info_local, &.{ try self.str(export_name), try self.str(local_name), @enumFromInt(std.math.maxInt(u32)) });
const export_name_id = try self.str(export_name);
if (try self._hasOrAddExportedName(export_name_id)) return; // a syntax error will be emitted later in this case
try self._addRecord(.export_info_local, &.{ export_name_id, try self.str(local_name), @enumFromInt(std.math.maxInt(u32)) });
}
pub fn addExportInfoNamespace(self: *ModuleInfo, export_name: []const u8, module_name: []const u8) !void {
try self._addRecord(.export_info_namespace, &.{ try self.str(export_name), try self.str(module_name) });
const export_name_id = try self.str(export_name);
if (try self._hasOrAddExportedName(export_name_id)) return; // a syntax error will be emitted later in this case
try self._addRecord(.export_info_namespace, &.{ export_name_id, try self.str(module_name) });
}
pub fn addExportInfoStar(self: *ModuleInfo, module_name: []const u8) !void {
try self._addRecord(.export_info_star, &.{try self.str(module_name)});
}
pub fn _hasOrAddExportedName(self: *ModuleInfo, name: StringID) !bool {
if (try self.exported_names.fetchPut(self.gpa, name, {}) != null) return true;
return false;
}
pub fn create(gpa: std.mem.Allocator) !*ModuleInfo {
const res = try gpa.create(ModuleInfo);
res.* = ModuleInfo.init(gpa);
@@ -230,6 +242,7 @@ pub const ModuleInfo = struct {
.strings_map = .{},
.strings_buf = .{},
.strings_lens = .{},
.exported_names = .{},
.requested_modules = std.AutoArrayHashMap(StringID, FetchParameters).init(allocator),
.buffer = std.ArrayList(StringID).init(allocator),
.record_kinds = std.ArrayList(RecordKind).init(allocator),
@@ -240,6 +253,7 @@ pub const ModuleInfo = struct {
self.strings_map.deinit(self.gpa);
self.strings_buf.deinit(self.gpa);
self.strings_lens.deinit(self.gpa);
self.exported_names.deinit(self.gpa);
self.requested_modules.deinit();
self.buffer.deinit();
self.record_kinds.deinit();

View File

@@ -139,21 +139,29 @@ describe("through export merge", () => {
["direct2", "export const value = 'abc'; export {value};"],
["ns", "export * as value from './c'; export * as value from './c';"],
]) {
test(name, () => {
describe(name, () => {
const dir = tempDirWithFiles("type-import", {
["main." + fmt]: "import {value} from './a'; console.log(value);",
["a." + fmt]: mode,
["b." + fmt]: fmt === "ts" ? "export type value = 'b';" : "",
["c." + fmt]: "export const value = 'c';",
});
const result = Bun.spawnSync({
cmd: [bunExe(), "main." + fmt],
cwd: dir,
env: bunEnv,
stdio: ["inherit", "pipe", "pipe"],
});
expect(result.stderr?.toString().trim()).toInclude("SyntaxError: Cannot export a duplicate name 'value'.");
expect(result.exitCode).toBe(1);
for (const file of ["main." + fmt, "a." + fmt]) {
test(file, () => {
const result = Bun.spawnSync({
cmd: [bunExe(), file],
cwd: dir,
env: bunEnv,
stdio: ["inherit", "pipe", "pipe"],
});
expect(result.stderr?.toString().trim()).toInclude(
file === "a." + fmt
? 'error: Multiple exports with the same name "value"\n' // bun's syntax error
: "SyntaxError: Cannot export a duplicate name 'value'.\n", // jsc's syntax error
);
expect(result.exitCode).toBe(1);
});
}
});
}
});