resinator: some updates to avoid GenericWriter

These are some hastily made, untested changes to get things compiling
again, since Ryan is working on a better upgrade patchset in the
meantime.
This commit is contained in:
Andrew Kelley 2025-08-27 16:05:20 -07:00
parent f7884961c2
commit 8d80d67693
6 changed files with 198 additions and 299 deletions

View file

@ -537,8 +537,15 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi
var allocating: std.Io.Writer.Allocating = .init(comp.gpa); var allocating: std.Io.Writer.Allocating = .init(comp.gpa);
defer allocating.deinit(); defer allocating.deinit();
const buf = &allocating.writer; generateBuiltinMacrosWriter(comp, system_defines_mode, &allocating.writer) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
else => |e| return e,
};
return comp.addSourceFromBuffer("<builtin>", allocating.written());
}
pub fn generateBuiltinMacrosWriter(comp: *Compilation, system_defines_mode: SystemDefinesMode, buf: *Writer) !void {
if (system_defines_mode == .include_system_defines) { if (system_defines_mode == .include_system_defines) {
try buf.writeAll( try buf.writeAll(
\\#define __VERSION__ "Aro \\#define __VERSION__ "Aro
@ -576,8 +583,6 @@ pub fn generateBuiltinMacros(comp: *Compilation, system_defines_mode: SystemDefi
if (system_defines_mode == .include_system_defines) { if (system_defines_mode == .include_system_defines) {
try comp.generateSystemDefines(buf); try comp.generateSystemDefines(buf);
} }
return comp.addSourceFromBuffer("<builtin>", allocating.written());
} }
fn generateFloatMacros(w: *Writer, prefix: []const u8, semantics: target_util.FPSemantics, ext: []const u8) !void { fn generateFloatMacros(w: *Writer, prefix: []const u8, semantics: target_util.FPSemantics, ext: []const u8) !void {

View file

@ -520,8 +520,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
// - or / on its own is an error // - or / on its own is an error
else => { else => {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid option: {s}", .{arg.prefixSlice()});
try msg_writer.print("invalid option: {s}", .{arg.prefixSlice()});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
continue :next_arg; continue :next_arg;
@ -532,8 +531,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const args_remaining = args.len - arg_i; const args_remaining = args.len - arg_i;
if (args_remaining <= 2 and arg.looksLikeFilepath()) { if (args_remaining <= 2 and arg.looksLikeFilepath()) {
var err_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i }; var err_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "this argument was inferred to be a filepath, so argument parsing was terminated");
try msg_writer.writeAll("this argument was inferred to be a filepath, so argument parsing was terminated");
try diagnostics.append(err_details); try diagnostics.append(err_details);
break; break;
@ -550,16 +548,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":output-format")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":output-format")) {
const value = arg.value(":output-format".len, arg_i, args) catch { const value = arg.value(":output-format".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":output-format".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":output-format".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
}; };
output_format = std.meta.stringToEnum(Options.OutputFormat, value.slice) orelse blk: { output_format = std.meta.stringToEnum(Options.OutputFormat, value.slice) orelse blk: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid output format setting: {s} ", .{value.slice});
try msg_writer.print("invalid output format setting: {s} ", .{value.slice});
try diagnostics.append(err_details); try diagnostics.append(err_details);
break :blk output_format; break :blk output_format;
}; };
@ -569,16 +565,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":auto-includes")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":auto-includes")) {
const value = arg.value(":auto-includes".len, arg_i, args) catch { const value = arg.value(":auto-includes".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":auto-includes".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":auto-includes".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
}; };
options.auto_includes = std.meta.stringToEnum(Options.AutoIncludes, value.slice) orelse blk: { options.auto_includes = std.meta.stringToEnum(Options.AutoIncludes, value.slice) orelse blk: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid auto includes setting: {s} ", .{value.slice});
try msg_writer.print("invalid auto includes setting: {s} ", .{value.slice});
try diagnostics.append(err_details); try diagnostics.append(err_details);
break :blk options.auto_includes; break :blk options.auto_includes;
}; };
@ -587,16 +581,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":input-format")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":input-format")) {
const value = arg.value(":input-format".len, arg_i, args) catch { const value = arg.value(":input-format".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":input-format".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":input-format".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
}; };
input_format = std.meta.stringToEnum(Options.InputFormat, value.slice) orelse blk: { input_format = std.meta.stringToEnum(Options.InputFormat, value.slice) orelse blk: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid input format setting: {s} ", .{value.slice});
try msg_writer.print("invalid input format setting: {s} ", .{value.slice});
try diagnostics.append(err_details); try diagnostics.append(err_details);
break :blk input_format; break :blk input_format;
}; };
@ -606,16 +598,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":depfile-fmt")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":depfile-fmt")) {
const value = arg.value(":depfile-fmt".len, arg_i, args) catch { const value = arg.value(":depfile-fmt".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":depfile-fmt".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":depfile-fmt".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
}; };
options.depfile_fmt = std.meta.stringToEnum(Options.DepfileFormat, value.slice) orelse blk: { options.depfile_fmt = std.meta.stringToEnum(Options.DepfileFormat, value.slice) orelse blk: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid depfile format setting: {s} ", .{value.slice});
try msg_writer.print("invalid depfile format setting: {s} ", .{value.slice});
try diagnostics.append(err_details); try diagnostics.append(err_details);
break :blk options.depfile_fmt; break :blk options.depfile_fmt;
}; };
@ -624,8 +614,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":depfile")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":depfile")) {
const value = arg.value(":depfile".len, arg_i, args) catch { const value = arg.value(":depfile".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":depfile".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":depfile".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -643,8 +632,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, ":target")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, ":target")) {
const value = arg.value(":target".len, arg_i, args) catch { const value = arg.value(":target".len, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":target".len) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(":target".len) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -655,8 +643,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const arch_str = target_it.first(); const arch_str = target_it.first();
const arch = cvtres.supported_targets.Arch.fromStringIgnoreCase(arch_str) orelse { const arch = cvtres.supported_targets.Arch.fromStringIgnoreCase(arch_str) orelse {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid or unsupported target architecture: {s}", .{arch_str});
try msg_writer.print("invalid or unsupported target architecture: {s}", .{arch_str});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -680,13 +667,11 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
.prefix_len = arg.prefixSlice().len, .prefix_len = arg.prefixSlice().len,
.value_offset = arg.name_offset + 3, .value_offset = arg.name_offset + 3,
} }; } };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value for {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(3) });
try msg_writer.print("missing value for {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(3) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
} }
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(3) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(3) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
continue :next_arg; continue :next_arg;
@ -695,16 +680,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
else if (std.ascii.startsWithIgnoreCase(arg_name, "tn")) { else if (std.ascii.startsWithIgnoreCase(arg_name, "tn")) {
const value = arg.value(2, arg_i, args) catch no_value: { const value = arg.value(2, arg_i, args) catch no_value: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
// dummy zero-length slice starting where the value would have been // dummy zero-length slice starting where the value would have been
const value_start = arg.name_offset + 2; const value_start = arg.name_offset + 2;
break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] }; break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] };
}; };
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -716,16 +699,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
{ {
const value = arg.value(2, arg_i, args) catch no_value: { const value = arg.value(2, arg_i, args) catch no_value: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
// dummy zero-length slice starting where the value would have been // dummy zero-length slice starting where the value would have been
const value_start = arg.name_offset + 2; const value_start = arg.name_offset + 2;
break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] }; break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] };
}; };
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -733,8 +714,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
// Unsupported MUI options that do not need a value // Unsupported MUI options that do not need a value
else if (std.ascii.startsWithIgnoreCase(arg_name, "g1")) { else if (std.ascii.startsWithIgnoreCase(arg_name, "g1")) {
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(2) }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(2) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg.name_offset += 2; arg.name_offset += 2;
} }
@ -747,15 +727,13 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
std.ascii.startsWithIgnoreCase(arg_name, "ta")) std.ascii.startsWithIgnoreCase(arg_name, "ta"))
{ {
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(2) }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(2) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg.name_offset += 2; arg.name_offset += 2;
} else if (std.ascii.startsWithIgnoreCase(arg_name, "fo")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "fo")) {
const value = arg.value(2, arg_i, args) catch { const value = arg.value(2, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing output path after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("missing output path after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -767,8 +745,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, "sl")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "sl")) {
const value = arg.value(2, arg_i, args) catch { const value = arg.value(2, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing language tag after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("missing language tag after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -776,24 +753,20 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const percent_str = value.slice; const percent_str = value.slice;
const percent: u32 = parsePercent(percent_str) catch { const percent: u32 = parsePercent(percent_str) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid percent format '{s}'", .{percent_str});
try msg_writer.print("invalid percent format '{s}'", .{percent_str});
try diagnostics.append(err_details); try diagnostics.append(err_details);
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = false, .arg_index = arg_i }; var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = false, .arg_index = arg_i };
var note_writer = note_details.msg.writer(allocator); try note_details.msg.appendSlice(allocator, "string length percent must be an integer between 1 and 100 (inclusive)");
try note_writer.writeAll("string length percent must be an integer between 1 and 100 (inclusive)");
try diagnostics.append(note_details); try diagnostics.append(note_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
}; };
if (percent == 0 or percent > 100) { if (percent == 0 or percent > 100) {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "percent out of range: {} (parsed from '{s}')", .{ percent, percent_str });
try msg_writer.print("percent out of range: {} (parsed from '{s}')", .{ percent, percent_str });
try diagnostics.append(err_details); try diagnostics.append(err_details);
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = false, .arg_index = arg_i }; var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = false, .arg_index = arg_i };
var note_writer = note_details.msg.writer(allocator); try note_details.msg.appendSlice(allocator, "string length percent must be an integer between 1 and 100 (inclusive)");
try note_writer.writeAll("string length percent must be an integer between 1 and 100 (inclusive)");
try diagnostics.append(note_details); try diagnostics.append(note_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -805,8 +778,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, "ln")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "ln")) {
const value = arg.value(2, arg_i, args) catch { const value = arg.value(2, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing language tag after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try msg_writer.print("missing language tag after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(2) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -814,16 +786,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const tag = value.slice; const tag = value.slice;
options.default_language_id = lang.tagToInt(tag) catch { options.default_language_id = lang.tagToInt(tag) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid language tag: {s}", .{tag});
try msg_writer.print("invalid language tag: {s}", .{tag});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
}; };
if (options.default_language_id.? == lang.LOCALE_CUSTOM_UNSPECIFIED) { if (options.default_language_id.? == lang.LOCALE_CUSTOM_UNSPECIFIED) {
var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "language tag '{s}' does not have an assigned ID so it will be resolved to LOCALE_CUSTOM_UNSPECIFIED (id=0x{x})", .{ tag, lang.LOCALE_CUSTOM_UNSPECIFIED });
try msg_writer.print("language tag '{s}' does not have an assigned ID so it will be resolved to LOCALE_CUSTOM_UNSPECIFIED (id=0x{x})", .{ tag, lang.LOCALE_CUSTOM_UNSPECIFIED });
try diagnostics.append(err_details); try diagnostics.append(err_details);
} }
arg_i += value.index_increment; arg_i += value.index_increment;
@ -831,8 +801,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, "l")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "l")) {
const value = arg.value(1, arg_i, args) catch { const value = arg.value(1, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing language ID after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing language ID after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -840,8 +809,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const num_str = value.slice; const num_str = value.slice;
options.default_language_id = lang.parseInt(num_str) catch { options.default_language_id = lang.parseInt(num_str) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid language ID: {s}", .{num_str});
try msg_writer.print("invalid language ID: {s}", .{num_str});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -860,16 +828,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
{ {
const value = arg.value(1, arg_i, args) catch no_value: { const value = arg.value(1, arg_i, args) catch no_value: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
// dummy zero-length slice starting where the value would have been // dummy zero-length slice starting where the value would have been
const value_start = arg.name_offset + 1; const value_start = arg.name_offset + 1;
break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] }; break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] };
}; };
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -882,16 +848,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
{ {
const value = arg.value(1, arg_i, args) catch no_value: { const value = arg.value(1, arg_i, args) catch no_value: {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing value after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
// dummy zero-length slice starting where the value would have been // dummy zero-length slice starting where the value would have been
const value_start = arg.name_offset + 1; const value_start = arg.name_offset + 1;
break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] }; break :no_value Arg.Value{ .slice = arg.full[value_start..value_start] };
}; };
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -899,15 +863,13 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
// 1 char unsupported LCX/LCE options that do not need a value // 1 char unsupported LCX/LCE options that do not need a value
else if (std.ascii.startsWithIgnoreCase(arg_name, "t")) { else if (std.ascii.startsWithIgnoreCase(arg_name, "t")) {
var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(1) }; var err_details = Diagnostics.ErrorDetails{ .type = .err, .arg_index = arg_i, .arg_span = arg.optionSpan(1) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("the {s}{s} option is unsupported", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg.name_offset += 1; arg.name_offset += 1;
} else if (std.ascii.startsWithIgnoreCase(arg_name, "c")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "c")) {
const value = arg.value(1, arg_i, args) catch { const value = arg.value(1, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing code page ID after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing code page ID after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -915,8 +877,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
const num_str = value.slice; const num_str = value.slice;
const code_page_id = std.fmt.parseUnsigned(u16, num_str, 10) catch { const code_page_id = std.fmt.parseUnsigned(u16, num_str, 10) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid code page ID: {s}", .{num_str});
try msg_writer.print("invalid code page ID: {s}", .{num_str});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
@ -924,16 +885,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
options.default_code_page = code_pages.getByIdentifierEnsureSupported(code_page_id) catch |err| switch (err) { options.default_code_page = code_pages.getByIdentifierEnsureSupported(code_page_id) catch |err| switch (err) {
error.InvalidCodePage => { error.InvalidCodePage => {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid or unknown code page ID: {}", .{code_page_id});
try msg_writer.print("invalid or unknown code page ID: {}", .{code_page_id});
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
}, },
error.UnsupportedCodePage => { error.UnsupportedCodePage => {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "unsupported code page: {s} (id={})", .{
try msg_writer.print("unsupported code page: {s} (id={})", .{
@tagName(code_pages.getByIdentifier(code_page_id) catch unreachable), @tagName(code_pages.getByIdentifier(code_page_id) catch unreachable),
code_page_id, code_page_id,
}); });
@ -957,8 +916,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, "i")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "i")) {
const value = arg.value(1, arg_i, args) catch { const value = arg.value(1, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing include path after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing include path after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -986,15 +944,13 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
// Undocumented option with unknown function // Undocumented option with unknown function
// TODO: More investigation to figure out what it does (if anything) // TODO: More investigation to figure out what it does (if anything)
var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = arg.optionSpan(1) }; var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = arg.optionSpan(1) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "option {s}{s} has no effect (it is undocumented and its function is unknown in the Win32 RC compiler)", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("option {s}{s} has no effect (it is undocumented and its function is unknown in the Win32 RC compiler)", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg.name_offset += 1; arg.name_offset += 1;
} else if (std.ascii.startsWithIgnoreCase(arg_name, "d")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "d")) {
const value = arg.value(1, arg_i, args) catch { const value = arg.value(1, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing symbol to define after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing symbol to define after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -1009,8 +965,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
try options.define(symbol, symbol_value); try options.define(symbol, symbol_value);
} else { } else {
var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "symbol \"{s}\" is not a valid identifier and therefore cannot be defined", .{symbol});
try msg_writer.print("symbol \"{s}\" is not a valid identifier and therefore cannot be defined", .{symbol});
try diagnostics.append(err_details); try diagnostics.append(err_details);
} }
arg_i += value.index_increment; arg_i += value.index_increment;
@ -1018,8 +973,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} else if (std.ascii.startsWithIgnoreCase(arg_name, "u")) { } else if (std.ascii.startsWithIgnoreCase(arg_name, "u")) {
const value = arg.value(1, arg_i, args) catch { const value = arg.value(1, arg_i, args) catch {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.missingSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "missing symbol to undefine after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try msg_writer.print("missing symbol to undefine after {s}{s} option", .{ arg.prefixSlice(), arg.optionWithoutPrefix(1) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
break :next_arg; break :next_arg;
@ -1029,16 +983,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
try options.undefine(symbol); try options.undefine(symbol);
} else { } else {
var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) }; var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = arg_i, .arg_span = value.argSpan(arg) };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "symbol \"{s}\" is not a valid identifier and therefore cannot be undefined", .{symbol});
try msg_writer.print("symbol \"{s}\" is not a valid identifier and therefore cannot be undefined", .{symbol});
try diagnostics.append(err_details); try diagnostics.append(err_details);
} }
arg_i += value.index_increment; arg_i += value.index_increment;
continue :next_arg; continue :next_arg;
} else { } else {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i, .arg_span = arg.optionAndAfterSpan() };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "invalid option: {s}{s}", .{ arg.prefixSlice(), arg.name() });
try msg_writer.print("invalid option: {s}{s}", .{ arg.prefixSlice(), arg.name() });
try diagnostics.append(err_details); try diagnostics.append(err_details);
arg_i += 1; arg_i += 1;
continue :next_arg; continue :next_arg;
@ -1055,16 +1007,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
if (positionals.len == 0) { if (positionals.len == 0) {
var err_details = Diagnostics.ErrorDetails{ .print_args = false, .arg_index = arg_i }; var err_details = Diagnostics.ErrorDetails{ .print_args = false, .arg_index = arg_i };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "missing input filename");
try msg_writer.writeAll("missing input filename");
try diagnostics.append(err_details); try diagnostics.append(err_details);
if (args.len > 0) { if (args.len > 0) {
const last_arg = args[args.len - 1]; const last_arg = args[args.len - 1];
if (arg_i > 0 and last_arg.len > 0 and last_arg[0] == '/' and isSupportedInputExtension(std.fs.path.extension(last_arg))) { if (arg_i > 0 and last_arg.len > 0 and last_arg[0] == '/' and isSupportedInputExtension(std.fs.path.extension(last_arg))) {
var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i - 1 }; var note_details = Diagnostics.ErrorDetails{ .type = .note, .print_args = true, .arg_index = arg_i - 1 };
var note_writer = note_details.msg.writer(allocator); try note_details.msg.appendSlice(allocator, "if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing");
try note_writer.writeAll("if this argument was intended to be the input filename, adding -- in front of it will exclude it from option parsing");
try diagnostics.append(note_details); try diagnostics.append(note_details);
} }
} }
@ -1099,16 +1049,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
if (positionals.len > 1) { if (positionals.len > 1) {
if (output_filename != null) { if (output_filename != null) {
var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i + 1 }; var err_details = Diagnostics.ErrorDetails{ .arg_index = arg_i + 1 };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "output filename already specified");
try msg_writer.writeAll("output filename already specified");
try diagnostics.append(err_details); try diagnostics.append(err_details);
var note_details = Diagnostics.ErrorDetails{ var note_details = Diagnostics.ErrorDetails{
.type = .note, .type = .note,
.arg_index = output_filename_context.arg.index, .arg_index = output_filename_context.arg.index,
.arg_span = output_filename_context.arg.value.argSpan(output_filename_context.arg.arg), .arg_span = output_filename_context.arg.value.argSpan(output_filename_context.arg.arg),
}; };
var note_writer = note_details.msg.writer(allocator); try note_details.msg.appendSlice(allocator, "output filename previously specified here");
try note_writer.writeAll("output filename previously specified here");
try diagnostics.append(note_details); try diagnostics.append(note_details);
} else { } else {
output_filename = positionals[1]; output_filename = positionals[1];
@ -1173,16 +1121,15 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
var print_output_format_source_note: bool = false; var print_output_format_source_note: bool = false;
if (options.depfile_path != null and (options.input_format == .res or options.output_format == .rcpp)) { if (options.depfile_path != null and (options.input_format == .res or options.output_format == .rcpp)) {
var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = depfile_context.index, .arg_span = depfile_context.value.argSpan(depfile_context.arg) }; var err_details = Diagnostics.ErrorDetails{ .type = .warning, .arg_index = depfile_context.index, .arg_span = depfile_context.value.argSpan(depfile_context.arg) };
var msg_writer = err_details.msg.writer(allocator);
if (options.input_format == .res) { if (options.input_format == .res) {
try msg_writer.print("the {s}{s} option was ignored because the input format is '{s}'", .{ try err_details.msg.print(allocator, "the {s}{s} option was ignored because the input format is '{s}'", .{
depfile_context.arg.prefixSlice(), depfile_context.arg.prefixSlice(),
depfile_context.arg.optionWithoutPrefix(depfile_context.option_len), depfile_context.arg.optionWithoutPrefix(depfile_context.option_len),
@tagName(options.input_format), @tagName(options.input_format),
}); });
print_input_format_source_note = true; print_input_format_source_note = true;
} else if (options.output_format == .rcpp) { } else if (options.output_format == .rcpp) {
try msg_writer.print("the {s}{s} option was ignored because the output format is '{s}'", .{ try err_details.msg.print(allocator, "the {s}{s} option was ignored because the output format is '{s}'", .{
depfile_context.arg.prefixSlice(), depfile_context.arg.prefixSlice(),
depfile_context.arg.optionWithoutPrefix(depfile_context.option_len), depfile_context.arg.optionWithoutPrefix(depfile_context.option_len),
@tagName(options.output_format), @tagName(options.output_format),
@ -1193,16 +1140,14 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
} }
if (!isSupportedTransformation(options.input_format, options.output_format)) { if (!isSupportedTransformation(options.input_format, options.output_format)) {
var err_details = Diagnostics.ErrorDetails{ .arg_index = input_filename_arg_i, .print_args = false }; var err_details = Diagnostics.ErrorDetails{ .arg_index = input_filename_arg_i, .print_args = false };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "input format '{s}' cannot be converted to output format '{s}'", .{ @tagName(options.input_format), @tagName(options.output_format) });
try msg_writer.print("input format '{s}' cannot be converted to output format '{s}'", .{ @tagName(options.input_format), @tagName(options.output_format) });
try diagnostics.append(err_details); try diagnostics.append(err_details);
print_input_format_source_note = true; print_input_format_source_note = true;
print_output_format_source_note = true; print_output_format_source_note = true;
} }
if (options.preprocess == .only and options.output_format != .rcpp) { if (options.preprocess == .only and options.output_format != .rcpp) {
var err_details = Diagnostics.ErrorDetails{ .arg_index = preprocess_only_context.index }; var err_details = Diagnostics.ErrorDetails{ .arg_index = preprocess_only_context.index };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the {s}{s} option cannot be used with output format '{s}'", .{
try msg_writer.print("the {s}{s} option cannot be used with output format '{s}'", .{
preprocess_only_context.arg.prefixSlice(), preprocess_only_context.arg.prefixSlice(),
preprocess_only_context.arg.optionWithoutPrefix(preprocess_only_context.option_len), preprocess_only_context.arg.optionWithoutPrefix(preprocess_only_context.option_len),
@tagName(options.output_format), @tagName(options.output_format),
@ -1214,8 +1159,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
switch (input_format_source) { switch (input_format_source) {
.inferred_from_input_filename => { .inferred_from_input_filename => {
var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = input_filename_arg_i }; var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = input_filename_arg_i };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "the input format was inferred from the input filename");
try msg_writer.writeAll("the input format was inferred from the input filename");
try diagnostics.append(err_details); try diagnostics.append(err_details);
}, },
.input_format_arg => { .input_format_arg => {
@ -1224,8 +1168,7 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
.arg_index = input_format_context.index, .arg_index = input_format_context.index,
.arg_span = input_format_context.value.argSpan(input_format_context.arg), .arg_span = input_format_context.value.argSpan(input_format_context.arg),
}; };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "the input format was specified here");
try msg_writer.writeAll("the input format was specified here");
try diagnostics.append(err_details); try diagnostics.append(err_details);
}, },
} }
@ -1234,11 +1177,10 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
switch (output_format_source) { switch (output_format_source) {
.inferred_from_input_filename, .unable_to_infer_from_input_filename => { .inferred_from_input_filename, .unable_to_infer_from_input_filename => {
var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = input_filename_arg_i }; var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = input_filename_arg_i };
var msg_writer = err_details.msg.writer(allocator);
if (output_format_source == .inferred_from_input_filename) { if (output_format_source == .inferred_from_input_filename) {
try msg_writer.writeAll("the output format was inferred from the input filename"); try err_details.msg.appendSlice(allocator, "the output format was inferred from the input filename");
} else { } else {
try msg_writer.writeAll("the output format was unable to be inferred from the input filename, so the default was used"); try err_details.msg.appendSlice(allocator, "the output format was unable to be inferred from the input filename, so the default was used");
} }
try diagnostics.append(err_details); try diagnostics.append(err_details);
}, },
@ -1248,11 +1190,10 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
.arg => |ctx| .{ .type = .note, .arg_index = ctx.index, .arg_span = ctx.value.argSpan(ctx.arg) }, .arg => |ctx| .{ .type = .note, .arg_index = ctx.index, .arg_span = ctx.value.argSpan(ctx.arg) },
.unspecified => unreachable, .unspecified => unreachable,
}; };
var msg_writer = err_details.msg.writer(allocator);
if (output_format_source == .inferred_from_output_filename) { if (output_format_source == .inferred_from_output_filename) {
try msg_writer.writeAll("the output format was inferred from the output filename"); try err_details.msg.appendSlice(allocator, "the output format was inferred from the output filename");
} else { } else {
try msg_writer.writeAll("the output format was unable to be inferred from the output filename, so the default was used"); try err_details.msg.appendSlice(allocator, "the output format was unable to be inferred from the output filename, so the default was used");
} }
try diagnostics.append(err_details); try diagnostics.append(err_details);
}, },
@ -1262,14 +1203,12 @@ pub fn parse(allocator: Allocator, args: []const []const u8, diagnostics: *Diagn
.arg_index = output_format_context.index, .arg_index = output_format_context.index,
.arg_span = output_format_context.value.argSpan(output_format_context.arg), .arg_span = output_format_context.value.argSpan(output_format_context.arg),
}; };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.appendSlice(allocator, "the output format was specified here");
try msg_writer.writeAll("the output format was specified here");
try diagnostics.append(err_details); try diagnostics.append(err_details);
}, },
.inferred_from_preprocess_only => { .inferred_from_preprocess_only => {
var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = preprocess_only_context.index }; var err_details = Diagnostics.ErrorDetails{ .type = .note, .arg_index = preprocess_only_context.index };
var msg_writer = err_details.msg.writer(allocator); try err_details.msg.print(allocator, "the output format was inferred from the usage of the {s}{s} option", .{
try msg_writer.print("the output format was inferred from the usage of the {s}{s} option", .{
preprocess_only_context.arg.prefixSlice(), preprocess_only_context.arg.prefixSlice(),
preprocess_only_context.arg.optionWithoutPrefix(preprocess_only_context.option_len), preprocess_only_context.arg.optionWithoutPrefix(preprocess_only_context.option_len),
}); });

View file

@ -61,7 +61,7 @@ pub const CompileOptions = struct {
warn_instead_of_error_on_invalid_code_page: bool = false, warn_instead_of_error_on_invalid_code_page: bool = false,
}; };
pub fn compile(allocator: Allocator, source: []const u8, writer: anytype, options: CompileOptions) !void { pub fn compile(allocator: Allocator, source: []const u8, writer: *std.Io.Writer, options: CompileOptions) !void {
var lexer = lex.Lexer.init(source, .{ var lexer = lex.Lexer.init(source, .{
.default_code_page = options.default_code_page, .default_code_page = options.default_code_page,
.source_mappings = options.source_mappings, .source_mappings = options.source_mappings,
@ -194,7 +194,7 @@ pub const Compiler = struct {
characteristics: u32 = 0, characteristics: u32 = 0,
}; };
pub fn writeRoot(self: *Compiler, root: *Node.Root, writer: anytype) !void { pub fn writeRoot(self: *Compiler, root: *Node.Root, writer: *std.Io.Writer) !void {
try writeEmptyResource(writer); try writeEmptyResource(writer);
for (root.body) |node| { for (root.body) |node| {
try self.writeNode(node, writer); try self.writeNode(node, writer);
@ -236,7 +236,7 @@ pub const Compiler = struct {
} }
} }
pub fn writeNode(self: *Compiler, node: *Node, writer: anytype) !void { pub fn writeNode(self: *Compiler, node: *Node, writer: *std.Io.Writer) !void {
switch (node.id) { switch (node.id) {
.root => unreachable, // writeRoot should be called directly instead .root => unreachable, // writeRoot should be called directly instead
.resource_external => try self.writeResourceExternal(@alignCast(@fieldParentPtr("base", node)), writer), .resource_external => try self.writeResourceExternal(@alignCast(@fieldParentPtr("base", node)), writer),
@ -479,7 +479,7 @@ pub const Compiler = struct {
return buf.toOwnedSlice(); return buf.toOwnedSlice();
} }
pub fn writeResourceExternal(self: *Compiler, node: *Node.ResourceExternal, writer: anytype) !void { pub fn writeResourceExternal(self: *Compiler, node: *Node.ResourceExternal, writer: *std.Io.Writer) !void {
// Init header with data size zero for now, will need to fill it in later // Init header with data size zero for now, will need to fill it in later
var header = try self.resourceHeader(node.id, node.type, .{}); var header = try self.resourceHeader(node.id, node.type, .{});
defer header.deinit(self.allocator); defer header.deinit(self.allocator);
@ -1226,33 +1226,31 @@ pub const Compiler = struct {
} }
pub fn writeResourceRawData(self: *Compiler, node: *Node.ResourceRawData, writer: anytype) !void { pub fn writeResourceRawData(self: *Compiler, node: *Node.ResourceRawData, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
// The header's data length field is a u32 so limit the resource's data size so that // The header's data length field is a u32 so limit the resource's data size so that
// we know we can always specify the real size. // we know we can always specify the real size.
var limited_writer = limitedWriter(data_buffer.writer(), std.math.maxInt(u32)); const data_writer = &data_buffer.writer;
const data_writer = limited_writer.writer();
for (node.raw_data) |expression| { for (node.raw_data) |expression| {
const data = try self.evaluateDataExpression(expression); const data = try self.evaluateDataExpression(expression);
defer data.deinit(self.allocator); defer data.deinit(self.allocator);
data.write(data_writer) catch |err| switch (err) { data.write(data_writer) catch |err| switch (err) {
error.NoSpaceLeft => { error.WriteFailed => {
return self.addErrorDetailsAndFail(.{ return self.addErrorDetailsAndFail(.{
.err = .resource_data_size_exceeds_max, .err = .resource_data_size_exceeds_max,
.token = node.id, .token = node.id,
}); });
}, },
else => |e| return e,
}; };
} }
// This intCast can't fail because the limitedWriter above guarantees that // This intCast can't fail because the limitedWriter above guarantees that
// we will never write more than maxInt(u32) bytes. // we will never write more than maxInt(u32) bytes.
const data_len: u32 = @intCast(data_buffer.items.len); const data_len: u32 = @intCast(data_buffer.written().len);
try self.writeResourceHeader(writer, node.id, node.type, data_len, node.common_resource_attributes, self.state.language); try self.writeResourceHeader(writer, node.id, node.type, data_len, node.common_resource_attributes, self.state.language);
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_len); try writeResourceData(writer, &data_fbs, data_len);
} }
@ -1306,16 +1304,15 @@ pub const Compiler = struct {
} }
pub fn writeAccelerators(self: *Compiler, node: *Node.Accelerators, writer: anytype) !void { pub fn writeAccelerators(self: *Compiler, node: *Node.Accelerators, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
// The header's data length field is a u32 so limit the resource's data size so that // The header's data length field is a u32 so limit the resource's data size so that
// we know we can always specify the real size. // we know we can always specify the real size.
var limited_writer = limitedWriter(data_buffer.writer(), std.math.maxInt(u32)); const data_writer = &data_buffer.writer;
const data_writer = limited_writer.writer();
self.writeAcceleratorsData(node, data_writer) catch |err| switch (err) { self.writeAcceleratorsData(node, data_writer) catch |err| switch (err) {
error.NoSpaceLeft => { error.WriteFailed => {
return self.addErrorDetailsAndFail(.{ return self.addErrorDetailsAndFail(.{
.err = .resource_data_size_exceeds_max, .err = .resource_data_size_exceeds_max,
.token = node.id, .token = node.id,
@ -1326,7 +1323,7 @@ pub const Compiler = struct {
// This intCast can't fail because the limitedWriter above guarantees that // This intCast can't fail because the limitedWriter above guarantees that
// we will never write more than maxInt(u32) bytes. // we will never write more than maxInt(u32) bytes.
const data_size: u32 = @intCast(data_buffer.items.len); const data_size: u32 = @intCast(data_buffer.written().len);
var header = try self.resourceHeader(node.id, node.type, .{ var header = try self.resourceHeader(node.id, node.type, .{
.data_size = data_size, .data_size = data_size,
}); });
@ -1337,7 +1334,7 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_size); try writeResourceData(writer, &data_fbs, data_size);
} }
@ -1405,12 +1402,11 @@ pub const Compiler = struct {
}; };
pub fn writeDialog(self: *Compiler, node: *Node.Dialog, writer: anytype) !void { pub fn writeDialog(self: *Compiler, node: *Node.Dialog, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
// The header's data length field is a u32 so limit the resource's data size so that // The header's data length field is a u32 so limit the resource's data size so that
// we know we can always specify the real size. // we know we can always specify the real size.
var limited_writer = limitedWriter(data_buffer.writer(), std.math.maxInt(u32)); const data_writer = &data_buffer.writer;
const data_writer = limited_writer.writer();
const resource = ResourceType.fromString(.{ const resource = ResourceType.fromString(.{
.slice = node.type.slice(self.source), .slice = node.type.slice(self.source),
@ -1683,7 +1679,7 @@ pub const Compiler = struct {
) catch |err| switch (err) { ) catch |err| switch (err) {
// Dialog header and menu/class/title strings can never exceed u32 bytes // Dialog header and menu/class/title strings can never exceed u32 bytes
// on their own, so this error is unreachable. // on their own, so this error is unreachable.
error.NoSpaceLeft => unreachable, error.WriteFailed => unreachable,
else => |e| return e, else => |e| return e,
}; };
@ -1700,10 +1696,10 @@ pub const Compiler = struct {
data_writer, data_writer,
resource, resource,
// We know the data_buffer len is limited to u32 max. // We know the data_buffer len is limited to u32 max.
@intCast(data_buffer.items.len), @intCast(data_buffer.written().len),
&controls_by_id, &controls_by_id,
) catch |err| switch (err) { ) catch |err| switch (err) {
error.NoSpaceLeft => { error.WriteFailed => {
try self.addErrorDetails(.{ try self.addErrorDetails(.{
.err = .resource_data_size_exceeds_max, .err = .resource_data_size_exceeds_max,
.token = node.id, .token = node.id,
@ -1719,7 +1715,7 @@ pub const Compiler = struct {
} }
// We know the data_buffer len is limited to u32 max. // We know the data_buffer len is limited to u32 max.
const data_size: u32 = @intCast(data_buffer.items.len); const data_size: u32 = @intCast(data_buffer.written().len);
var header = try self.resourceHeader(node.id, node.type, .{ var header = try self.resourceHeader(node.id, node.type, .{
.data_size = data_size, .data_size = data_size,
}); });
@ -1730,7 +1726,7 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_size); try writeResourceData(writer, &data_fbs, data_size);
} }
@ -1821,7 +1817,7 @@ pub const Compiler = struct {
.token = control.type, .token = control.type,
}); });
} }
try data_writer.writeByteNTimes(0, num_padding); try data_writer.splatByteAll(0, num_padding);
const style = if (control.style) |style_expression| const style = if (control.style) |style_expression|
// Certain styles are implied by the control type // Certain styles are implied by the control type
@ -1973,16 +1969,15 @@ pub const Compiler = struct {
try NameOrOrdinal.writeEmpty(data_writer); try NameOrOrdinal.writeEmpty(data_writer);
} }
var extra_data_buf = std.array_list.Managed(u8).init(self.allocator); var extra_data_buf: std.Io.Writer.Allocating = .init(self.allocator);
defer extra_data_buf.deinit(); defer extra_data_buf.deinit();
// The extra data byte length must be able to fit within a u16. // The extra data byte length must be able to fit within a u16.
var limited_extra_data_writer = limitedWriter(extra_data_buf.writer(), std.math.maxInt(u16)); const extra_data_writer = &extra_data_buf.writer;
const extra_data_writer = limited_extra_data_writer.writer();
for (control.extra_data) |data_expression| { for (control.extra_data) |data_expression| {
const data = try self.evaluateDataExpression(data_expression); const data = try self.evaluateDataExpression(data_expression);
defer data.deinit(self.allocator); defer data.deinit(self.allocator);
data.write(extra_data_writer) catch |err| switch (err) { data.write(extra_data_writer) catch |err| switch (err) {
error.NoSpaceLeft => { error.WriteFailed => {
try self.addErrorDetails(.{ try self.addErrorDetails(.{
.err = .control_extra_data_size_exceeds_max, .err = .control_extra_data_size_exceeds_max,
.token = control.type, .token = control.type,
@ -1998,15 +1993,15 @@ pub const Compiler = struct {
}; };
} }
// We know the extra_data_buf size fits within a u16. // We know the extra_data_buf size fits within a u16.
const extra_data_size: u16 = @intCast(extra_data_buf.items.len); const extra_data_size: u16 = @intCast(extra_data_buf.written().len);
try data_writer.writeInt(u16, extra_data_size, .little); try data_writer.writeInt(u16, extra_data_size, .little);
try data_writer.writeAll(extra_data_buf.items); try data_writer.writeAll(extra_data_buf.written());
} }
pub fn writeToolbar(self: *Compiler, node: *Node.Toolbar, writer: anytype) !void { pub fn writeToolbar(self: *Compiler, node: *Node.Toolbar, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
const data_writer = data_buffer.writer(); const data_writer = &data_buffer.writer;
const button_width = evaluateNumberExpression(node.button_width, self.source, self.input_code_pages); const button_width = evaluateNumberExpression(node.button_width, self.source, self.input_code_pages);
const button_height = evaluateNumberExpression(node.button_height, self.source, self.input_code_pages); const button_height = evaluateNumberExpression(node.button_height, self.source, self.input_code_pages);
@ -2034,7 +2029,7 @@ pub const Compiler = struct {
} }
} }
const data_size: u32 = @intCast(data_buffer.items.len); const data_size: u32 = @intCast(data_buffer.written().len);
var header = try self.resourceHeader(node.id, node.type, .{ var header = try self.resourceHeader(node.id, node.type, .{
.data_size = data_size, .data_size = data_size,
}); });
@ -2044,7 +2039,7 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_size); try writeResourceData(writer, &data_fbs, data_size);
} }
@ -2082,12 +2077,11 @@ pub const Compiler = struct {
} }
pub fn writeMenu(self: *Compiler, node: *Node.Menu, writer: anytype) !void { pub fn writeMenu(self: *Compiler, node: *Node.Menu, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
// The header's data length field is a u32 so limit the resource's data size so that // The header's data length field is a u32 so limit the resource's data size so that
// we know we can always specify the real size. // we know we can always specify the real size.
var limited_writer = limitedWriter(data_buffer.writer(), std.math.maxInt(u32)); const data_writer = &data_buffer.writer;
const data_writer = limited_writer.writer();
const type_bytes = SourceBytes{ const type_bytes = SourceBytes{
.slice = node.type.slice(self.source), .slice = node.type.slice(self.source),
@ -2096,9 +2090,7 @@ pub const Compiler = struct {
const resource = ResourceType.fromString(type_bytes); const resource = ResourceType.fromString(type_bytes);
std.debug.assert(resource == .menu or resource == .menuex); std.debug.assert(resource == .menu or resource == .menuex);
var adapted = data_writer.adaptToNewApi(&.{}); self.writeMenuData(node, data_writer, resource) catch |err| switch (err) {
self.writeMenuData(node, &adapted.new_interface, resource) catch |err| switch (err) {
error.WriteFailed => { error.WriteFailed => {
return self.addErrorDetailsAndFail(.{ return self.addErrorDetailsAndFail(.{
.err = .resource_data_size_exceeds_max, .err = .resource_data_size_exceeds_max,
@ -2110,7 +2102,7 @@ pub const Compiler = struct {
// This intCast can't fail because the limitedWriter above guarantees that // This intCast can't fail because the limitedWriter above guarantees that
// we will never write more than maxInt(u32) bytes. // we will never write more than maxInt(u32) bytes.
const data_size: u32 = @intCast(data_buffer.items.len); const data_size: u32 = @intCast(data_buffer.written().len);
var header = try self.resourceHeader(node.id, node.type, .{ var header = try self.resourceHeader(node.id, node.type, .{
.data_size = data_size, .data_size = data_size,
}); });
@ -2121,7 +2113,7 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_size); try writeResourceData(writer, &data_fbs, data_size);
} }
@ -2265,12 +2257,11 @@ pub const Compiler = struct {
} }
pub fn writeVersionInfo(self: *Compiler, node: *Node.VersionInfo, writer: anytype) !void { pub fn writeVersionInfo(self: *Compiler, node: *Node.VersionInfo, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(self.allocator); var data_buffer: std.Io.Writer.Allocating = .init(self.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
// The node's length field (which is inclusive of the length of all of its children) is a u16 // The node's length field (which is inclusive of the length of all of its children) is a u16
// so limit the node's data size so that we know we can always specify the real size. // so limit the node's data size so that we know we can always specify the real size.
var limited_writer = limitedWriter(data_buffer.writer(), std.math.maxInt(u16)); const data_writer = &data_buffer.writer;
const data_writer = limited_writer.writer();
try data_writer.writeInt(u16, 0, .little); // placeholder size try data_writer.writeInt(u16, 0, .little); // placeholder size
try data_writer.writeInt(u16, res.FixedFileInfo.byte_len, .little); try data_writer.writeInt(u16, res.FixedFileInfo.byte_len, .little);
@ -2354,8 +2345,7 @@ pub const Compiler = struct {
try fixed_file_info.write(data_writer); try fixed_file_info.write(data_writer);
for (node.block_statements) |statement| { for (node.block_statements) |statement| {
var adapted = data_writer.adaptToNewApi(&.{}); self.writeVersionNode(statement, data_writer, &data_buffer) catch |err| switch (err) {
self.writeVersionNode(statement, &adapted.new_interface, &data_buffer) catch |err| switch (err) {
error.WriteFailed => { error.WriteFailed => {
try self.addErrorDetails(.{ try self.addErrorDetails(.{
.err = .version_node_size_exceeds_max, .err = .version_node_size_exceeds_max,
@ -2374,9 +2364,9 @@ pub const Compiler = struct {
// We know that data_buffer.items.len is within the limits of a u16, since we // We know that data_buffer.items.len is within the limits of a u16, since we
// limited the writer to maxInt(u16) // limited the writer to maxInt(u16)
const data_size: u16 = @intCast(data_buffer.items.len); const data_size: u16 = @intCast(data_buffer.written().len);
// And now that we know the full size of this node (including its children), set its size // And now that we know the full size of this node (including its children), set its size
std.mem.writeInt(u16, data_buffer.items[0..2], data_size, .little); std.mem.writeInt(u16, data_buffer.written()[0..2], data_size, .little);
var header = try self.resourceHeader(node.id, node.versioninfo, .{ var header = try self.resourceHeader(node.id, node.versioninfo, .{
.data_size = data_size, .data_size = data_size,
@ -2387,22 +2377,22 @@ pub const Compiler = struct {
try header.write(writer, self.errContext(node.id)); try header.write(writer, self.errContext(node.id));
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try writeResourceData(writer, &data_fbs, data_size); try writeResourceData(writer, &data_fbs, data_size);
} }
/// Expects writer to be a LimitedWriter limited to u16, meaning all writes to /// Expects writer to be a LimitedWriter limited to u16, meaning all writes to
/// the writer within this function could return error.NoSpaceLeft, and that buf.items.len /// the writer within this function could return error.NoSpaceLeft, and that buf.items.len
/// will never be able to exceed maxInt(u16). /// will never be able to exceed maxInt(u16).
pub fn writeVersionNode(self: *Compiler, node: *Node, writer: *std.Io.Writer, buf: *std.array_list.Managed(u8)) !void { pub fn writeVersionNode(self: *Compiler, node: *Node, writer: *std.Io.Writer, buf: *std.Io.Writer.Allocating) !void {
// We can assume that buf.items.len will never be able to exceed the limits of a u16 // We can assume that buf.items.len will never be able to exceed the limits of a u16
try writeDataPadding(writer, @as(u16, @intCast(buf.items.len))); try writeDataPadding(writer, @as(u16, @intCast(buf.written().len)));
const node_and_children_size_offset = buf.items.len; const node_and_children_size_offset = buf.written().len;
try writer.writeInt(u16, 0, .little); // placeholder for size try writer.writeInt(u16, 0, .little); // placeholder for size
const data_size_offset = buf.items.len; const data_size_offset = buf.written().len;
try writer.writeInt(u16, 0, .little); // placeholder for data size try writer.writeInt(u16, 0, .little); // placeholder for data size
const data_type_offset = buf.items.len; const data_type_offset = buf.written().len;
// Data type is string unless the node contains values that are numbers. // Data type is string unless the node contains values that are numbers.
try writer.writeInt(u16, res.VersionNode.type_string, .little); try writer.writeInt(u16, res.VersionNode.type_string, .little);
@ -2432,7 +2422,7 @@ pub const Compiler = struct {
// during parsing, so we can just do the correct thing here. // during parsing, so we can just do the correct thing here.
var values_size: usize = 0; var values_size: usize = 0;
try writeDataPadding(writer, @intCast(buf.items.len)); try writeDataPadding(writer, @intCast(buf.written().len));
for (block_or_value.values, 0..) |value_value_node_uncasted, i| { for (block_or_value.values, 0..) |value_value_node_uncasted, i| {
const value_value_node = value_value_node_uncasted.cast(.block_value_value).?; const value_value_node = value_value_node_uncasted.cast(.block_value_value).?;
@ -2471,11 +2461,11 @@ pub const Compiler = struct {
} }
} }
} }
var data_size_slice = buf.items[data_size_offset..]; var data_size_slice = buf.written()[data_size_offset..];
std.mem.writeInt(u16, data_size_slice[0..@sizeOf(u16)], @as(u16, @intCast(values_size)), .little); std.mem.writeInt(u16, data_size_slice[0..@sizeOf(u16)], @as(u16, @intCast(values_size)), .little);
if (has_number_value) { if (has_number_value) {
const data_type_slice = buf.items[data_type_offset..]; const data_type_slice = buf.written()[data_type_offset..];
std.mem.writeInt(u16, data_type_slice[0..@sizeOf(u16)], res.VersionNode.type_binary, .little); std.mem.writeInt(u16, data_type_slice[0..@sizeOf(u16)], res.VersionNode.type_binary, .little);
} }
@ -2489,8 +2479,8 @@ pub const Compiler = struct {
else => unreachable, else => unreachable,
} }
const node_and_children_size = buf.items.len - node_and_children_size_offset; const node_and_children_size = buf.written().len - node_and_children_size_offset;
const node_and_children_size_slice = buf.items[node_and_children_size_offset..]; const node_and_children_size_slice = buf.written()[node_and_children_size_offset..];
std.mem.writeInt(u16, node_and_children_size_slice[0..@sizeOf(u16)], @as(u16, @intCast(node_and_children_size)), .little); std.mem.writeInt(u16, node_and_children_size_slice[0..@sizeOf(u16)], @as(u16, @intCast(node_and_children_size)), .little);
} }
@ -2973,54 +2963,6 @@ pub fn headerSlurpingReader(comptime size: usize, reader: anytype) HeaderSlurpin
return .{ .child_reader = reader }; return .{ .child_reader = reader };
} }
/// Sort of like std.io.LimitedReader, but a Writer.
/// Returns an error if writing the requested number of bytes
/// would ever exceed bytes_left, i.e. it does not always
/// write up to the limit and instead will error if the
/// limit would be breached if the entire slice was written.
pub fn LimitedWriter(comptime WriterType: type) type {
return struct {
inner_writer: WriterType,
bytes_left: u64,
pub const Error = error{NoSpaceLeft} || WriterType.Error;
pub const Writer = std.io.GenericWriter(*Self, Error, write);
const Self = @This();
pub fn write(self: *Self, bytes: []const u8) Error!usize {
if (bytes.len > self.bytes_left) return error.NoSpaceLeft;
const amt = try self.inner_writer.write(bytes);
self.bytes_left -= amt;
return amt;
}
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
};
}
/// Returns an initialised `LimitedWriter`
/// `bytes_left` is a `u64` to be able to take 64 bit file offsets
pub fn limitedWriter(inner_writer: anytype, bytes_left: u64) LimitedWriter(@TypeOf(inner_writer)) {
return .{ .inner_writer = inner_writer, .bytes_left = bytes_left };
}
test "limitedWriter basic usage" {
var buf: [4]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
var limited_stream = limitedWriter(fbs.writer(), 4);
var writer = limited_stream.writer();
try std.testing.expectEqual(@as(usize, 3), try writer.write("123"));
try std.testing.expectEqualSlices(u8, "123", buf[0..3]);
try std.testing.expectError(error.NoSpaceLeft, writer.write("45"));
try std.testing.expectEqual(@as(usize, 1), try writer.write("4"));
try std.testing.expectEqualSlices(u8, "1234", buf[0..4]);
try std.testing.expectError(error.NoSpaceLeft, writer.write("5"));
}
pub const FontDir = struct { pub const FontDir = struct {
fonts: std.ArrayListUnmanaged(Font) = .empty, fonts: std.ArrayListUnmanaged(Font) = .empty,
/// To keep track of which ids are set and where they were set from /// To keep track of which ids are set and where they were set from
@ -3246,9 +3188,9 @@ pub const StringTable = struct {
} }
pub fn writeResData(self: *Block, compiler: *Compiler, language: res.Language, block_id: u16, writer: anytype) !void { pub fn writeResData(self: *Block, compiler: *Compiler, language: res.Language, block_id: u16, writer: anytype) !void {
var data_buffer = std.array_list.Managed(u8).init(compiler.allocator); var data_buffer: std.Io.Writer.Allocating = .init(compiler.allocator);
defer data_buffer.deinit(); defer data_buffer.deinit();
const data_writer = data_buffer.writer(); const data_writer = &data_buffer.writer;
var i: u8 = 0; var i: u8 = 0;
var string_i: u8 = 0; var string_i: u8 = 0;
@ -3307,7 +3249,7 @@ pub const StringTable = struct {
// 16 * (131,070 + 2) = 2,097,152 which is well within the u32 max. // 16 * (131,070 + 2) = 2,097,152 which is well within the u32 max.
// //
// Note: The string literal maximum length is enforced by the lexer. // Note: The string literal maximum length is enforced by the lexer.
const data_size: u32 = @intCast(data_buffer.items.len); const data_size: u32 = @intCast(data_buffer.written().len);
const header = Compiler.ResourceHeader{ const header = Compiler.ResourceHeader{
.name_value = .{ .ordinal = block_id }, .name_value = .{ .ordinal = block_id },
@ -3322,7 +3264,7 @@ pub const StringTable = struct {
// we fully control and know are numbers, so they have a fixed size. // we fully control and know are numbers, so they have a fixed size.
try header.writeAssertNoOverflow(writer); try header.writeAssertNoOverflow(writer);
var data_fbs: std.Io.Reader = .fixed(data_buffer.items); var data_fbs: std.Io.Reader = .fixed(data_buffer.written());
try Compiler.writeResourceData(writer, &data_fbs, data_size); try Compiler.writeResourceData(writer, &data_fbs, data_size);
} }
}; };

View file

@ -1102,11 +1102,10 @@ const CorrespondingLines = struct {
corresponding_lines.buffered_reader = corresponding_lines.file.reader(&.{}); corresponding_lines.buffered_reader = corresponding_lines.file.reader(&.{});
errdefer corresponding_lines.deinit(); errdefer corresponding_lines.deinit();
var fbs = std.io.fixedBufferStream(&corresponding_lines.line_buf); var writer: std.Io.Writer = .fixed(&corresponding_lines.line_buf);
const writer = fbs.writer();
try corresponding_lines.writeLineFromStreamVerbatim( try corresponding_lines.writeLineFromStreamVerbatim(
writer, &writer,
corresponding_lines.buffered_reader.interface.adaptToOldInterface(), corresponding_lines.buffered_reader.interface.adaptToOldInterface(),
corresponding_span.start_line, corresponding_span.start_line,
); );
@ -1145,11 +1144,10 @@ const CorrespondingLines = struct {
self.line_len = 0; self.line_len = 0;
self.visual_line_len = 0; self.visual_line_len = 0;
var fbs = std.io.fixedBufferStream(&self.line_buf); var writer: std.Io.Writer = .fixed(&self.line_buf);
const writer = fbs.writer();
try self.writeLineFromStreamVerbatim( try self.writeLineFromStreamVerbatim(
writer, &writer,
self.buffered_reader.interface.adaptToOldInterface(), self.buffered_reader.interface.adaptToOldInterface(),
self.line_num, self.line_num,
); );
@ -1164,7 +1162,7 @@ const CorrespondingLines = struct {
return visual_line; return visual_line;
} }
fn writeLineFromStreamVerbatim(self: *CorrespondingLines, writer: anytype, input: anytype, line_num: usize) !void { fn writeLineFromStreamVerbatim(self: *CorrespondingLines, writer: *std.Io.Writer, input: anytype, line_num: usize) !void {
while (try readByteOrEof(input)) |byte| { while (try readByteOrEof(input)) |byte| {
switch (byte) { switch (byte) {
'\n', '\r' => { '\n', '\r' => {
@ -1188,7 +1186,7 @@ const CorrespondingLines = struct {
if (writer.writeByte(byte)) { if (writer.writeByte(byte)) {
self.line_len += 1; self.line_len += 1;
} else |err| switch (err) { } else |err| switch (err) {
error.NoSpaceLeft => {}, error.WriteFailed => {},
else => |e| return e, else => |e| return e,
} }
} }

View file

@ -43,11 +43,11 @@ pub fn main() !void {
cli_args = args[3..]; cli_args = args[3..];
} }
var stdout_writer2 = std.fs.File.stdout().writer(&stdout_buffer); var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
var error_handler: ErrorHandler = switch (zig_integration) { var error_handler: ErrorHandler = switch (zig_integration) {
true => .{ true => .{
.server = .{ .server = .{
.out = &stdout_writer2.interface, .out = &stdout_writer.interface,
.in = undefined, // won't be receiving messages .in = undefined, // won't be receiving messages
}, },
}, },
@ -83,18 +83,18 @@ pub fn main() !void {
defer options.deinit(); defer options.deinit();
if (options.print_help_and_exit) { if (options.print_help_and_exit) {
const stdout = std.fs.File.stdout(); try cli.writeUsage(&stdout_writer.interface, "zig rc");
try cli.writeUsage(stdout.deprecatedWriter(), "zig rc"); try stdout_writer.interface.flush();
return; return;
} }
// Don't allow verbose when integrating with Zig via stdout // Don't allow verbose when integrating with Zig via stdout
options.verbose = false; options.verbose = false;
const stdout_writer = std.fs.File.stdout().deprecatedWriter();
if (options.verbose) { if (options.verbose) {
try options.dumpVerbose(stdout_writer); try options.dumpVerbose(&stdout_writer.interface);
try stdout_writer.writeByte('\n'); try stdout_writer.interface.writeByte('\n');
try stdout_writer.interface.flush();
} }
var dependencies_list = std.array_list.Managed([]const u8).init(allocator); var dependencies_list = std.array_list.Managed([]const u8).init(allocator);
@ -115,7 +115,7 @@ pub fn main() !void {
const full_input = full_input: { const full_input = full_input: {
if (options.input_format == .rc and options.preprocess != .no) { if (options.input_format == .rc and options.preprocess != .no) {
var preprocessed_buf = std.array_list.Managed(u8).init(allocator); var preprocessed_buf: std.Io.Writer.Allocating = .init(allocator);
errdefer preprocessed_buf.deinit(); errdefer preprocessed_buf.deinit();
// We're going to throw away everything except the final preprocessed output anyway, // We're going to throw away everything except the final preprocessed output anyway,
@ -139,14 +139,15 @@ pub fn main() !void {
}); });
if (options.verbose) { if (options.verbose) {
try stdout_writer.writeAll("Preprocessor: arocc (built-in)\n"); try stdout_writer.interface.writeAll("Preprocessor: arocc (built-in)\n");
for (argv.items[0 .. argv.items.len - 1]) |arg| { for (argv.items[0 .. argv.items.len - 1]) |arg| {
try stdout_writer.print("{s} ", .{arg}); try stdout_writer.interface.print("{s} ", .{arg});
} }
try stdout_writer.print("{s}\n\n", .{argv.items[argv.items.len - 1]}); try stdout_writer.interface.print("{s}\n\n", .{argv.items[argv.items.len - 1]});
try stdout_writer.interface.flush();
} }
preprocess.preprocess(&comp, preprocessed_buf.writer(), argv.items, maybe_dependencies_list) catch |err| switch (err) { preprocess.preprocess(&comp, &preprocessed_buf.writer, argv.items, maybe_dependencies_list) catch |err| switch (err) {
error.GeneratedSourceError => { error.GeneratedSourceError => {
try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp); try error_handler.emitAroDiagnostics(allocator, "failed during preprocessor setup (this is always a bug):", &comp);
std.process.exit(1); std.process.exit(1);
@ -249,8 +250,9 @@ pub fn main() !void {
defer diagnostics.deinit(); defer diagnostics.deinit();
var output_buffer: [4096]u8 = undefined; var output_buffer: [4096]u8 = undefined;
var res_stream_writer = res_stream.source.writer(allocator).adaptToNewApi(&output_buffer); var res_stream_writer = res_stream.source.writer(allocator, &output_buffer);
const output_buffered_stream = &res_stream_writer.new_interface; defer res_stream_writer.deinit(&res_stream.source);
const output_buffered_stream = res_stream_writer.interface();
compile(allocator, final_input, output_buffered_stream, .{ compile(allocator, final_input, output_buffered_stream, .{
.cwd = std.fs.cwd(), .cwd = std.fs.cwd(),
@ -342,10 +344,10 @@ pub fn main() !void {
defer coff_stream.deinit(allocator); defer coff_stream.deinit(allocator);
var coff_output_buffer: [4096]u8 = undefined; var coff_output_buffer: [4096]u8 = undefined;
var coff_output_buffered_stream = coff_stream.source.writer(allocator).adaptToNewApi(&coff_output_buffer); var coff_output_buffered_stream = coff_stream.source.writer(allocator, &coff_output_buffer);
var cvtres_diagnostics: cvtres.Diagnostics = .{ .none = {} }; var cvtres_diagnostics: cvtres.Diagnostics = .{ .none = {} };
cvtres.writeCoff(allocator, &coff_output_buffered_stream.new_interface, resources.list.items, options.coff_options, &cvtres_diagnostics) catch |err| { cvtres.writeCoff(allocator, coff_output_buffered_stream.interface(), resources.list.items, options.coff_options, &cvtres_diagnostics) catch |err| {
switch (err) { switch (err) {
error.DuplicateResource => { error.DuplicateResource => {
const duplicate_resource = resources.list.items[cvtres_diagnostics.duplicate_resource]; const duplicate_resource = resources.list.items[cvtres_diagnostics.duplicate_resource];
@ -382,7 +384,7 @@ pub fn main() !void {
std.process.exit(1); std.process.exit(1);
}; };
try coff_output_buffered_stream.new_interface.flush(); try coff_output_buffered_stream.interface().flush();
} }
const IoStream = struct { const IoStream = struct {
@ -425,7 +427,7 @@ const IoStream = struct {
pub const Source = union(enum) { pub const Source = union(enum) {
file: std.fs.File, file: std.fs.File,
stdio: std.fs.File, stdio: std.fs.File,
memory: std.ArrayListUnmanaged(u8), memory: std.ArrayList(u8),
/// The source has been closed and any usage of the Source in this state is illegal (except deinit). /// The source has been closed and any usage of the Source in this state is illegal (except deinit).
closed: void, closed: void,
@ -472,26 +474,34 @@ const IoStream = struct {
}; };
} }
pub const WriterContext = struct { pub const Writer = union(enum) {
self: *Source, file: std.fs.File.Writer,
allocator: std.mem.Allocator, allocating: std.Io.Writer.Allocating,
pub const Error = std.mem.Allocator.Error || std.fs.File.WriteError;
pub fn interface(this: *@This()) *std.Io.Writer {
return switch (this.*) {
.file => |*fw| &fw.interface,
.allocating => |*a| &a.writer,
}; };
pub const WriteError = std.mem.Allocator.Error || std.fs.File.WriteError; }
pub const Writer = std.io.GenericWriter(WriterContext, WriteError, write);
pub fn write(ctx: WriterContext, bytes: []const u8) WriteError!usize { pub fn deinit(this: *@This(), source: *Source) void {
switch (ctx.self.*) { switch (this.*) {
inline .file, .stdio => |file| return file.write(bytes), .file => {},
.memory => |*list| { .allocating => |*a| source.memory = a.toArrayList(),
try list.appendSlice(ctx.allocator, bytes); }
return bytes.len; this.* = undefined;
}, }
};
pub fn writer(source: *Source, allocator: std.mem.Allocator, buffer: []u8) Writer {
return switch (source.*) {
.file, .stdio => |file| .{ .file = file.writer(buffer) },
.memory => |*list| .{ .allocating = .fromArrayList(allocator, list) },
.closed => unreachable, .closed => unreachable,
} };
}
pub fn writer(self: *Source, allocator: std.mem.Allocator) Writer {
return .{ .context = .{ .self = self, .allocator = allocator } };
} }
}; };
}; };
@ -721,7 +731,7 @@ fn cliDiagnosticsToErrorBundle(
}); });
var cur_err: ?ErrorBundle.ErrorMessage = null; var cur_err: ?ErrorBundle.ErrorMessage = null;
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .empty; var cur_notes: std.ArrayList(ErrorBundle.ErrorMessage) = .empty;
defer cur_notes.deinit(gpa); defer cur_notes.deinit(gpa);
for (diagnostics.errors.items) |err_details| { for (diagnostics.errors.items) |err_details| {
switch (err_details.type) { switch (err_details.type) {
@ -763,10 +773,10 @@ fn diagnosticsToErrorBundle(
try bundle.init(gpa); try bundle.init(gpa);
errdefer bundle.deinit(); errdefer bundle.deinit();
var msg_buf: std.ArrayListUnmanaged(u8) = .empty; var msg_buf: std.Io.Writer.Allocating = .init(gpa);
defer msg_buf.deinit(gpa); defer msg_buf.deinit();
var cur_err: ?ErrorBundle.ErrorMessage = null; var cur_err: ?ErrorBundle.ErrorMessage = null;
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .empty; var cur_notes: std.ArrayList(ErrorBundle.ErrorMessage) = .empty;
defer cur_notes.deinit(gpa); defer cur_notes.deinit(gpa);
for (diagnostics.errors.items) |err_details| { for (diagnostics.errors.items) |err_details| {
switch (err_details.type) { switch (err_details.type) {
@ -789,7 +799,7 @@ fn diagnosticsToErrorBundle(
const column = err_details.token.calculateColumn(source, 1, source_line_start) + 1; const column = err_details.token.calculateColumn(source, 1, source_line_start) + 1;
msg_buf.clearRetainingCapacity(); msg_buf.clearRetainingCapacity();
try err_details.render(msg_buf.writer(gpa), source, diagnostics.strings.items); try err_details.render(&msg_buf.writer, source, diagnostics.strings.items);
const src_loc = src_loc: { const src_loc = src_loc: {
var src_loc: ErrorBundle.SourceLocation = .{ var src_loc: ErrorBundle.SourceLocation = .{
@ -817,7 +827,7 @@ fn diagnosticsToErrorBundle(
try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items); try flushErrorMessageIntoBundle(&bundle, err, cur_notes.items);
} }
cur_err = .{ cur_err = .{
.msg = try bundle.addString(msg_buf.items), .msg = try bundle.addString(msg_buf.written()),
.src_loc = src_loc, .src_loc = src_loc,
}; };
cur_notes.clearRetainingCapacity(); cur_notes.clearRetainingCapacity();
@ -825,7 +835,7 @@ fn diagnosticsToErrorBundle(
.note => { .note => {
cur_err.?.notes_len += 1; cur_err.?.notes_len += 1;
try cur_notes.append(gpa, .{ try cur_notes.append(gpa, .{
.msg = try bundle.addString(msg_buf.items), .msg = try bundle.addString(msg_buf.written()),
.src_loc = src_loc, .src_loc = src_loc,
}); });
}, },
@ -876,7 +886,7 @@ fn aroDiagnosticsToErrorBundle(
var msg_writer = MsgWriter.init(gpa); var msg_writer = MsgWriter.init(gpa);
defer msg_writer.deinit(); defer msg_writer.deinit();
var cur_err: ?ErrorBundle.ErrorMessage = null; var cur_err: ?ErrorBundle.ErrorMessage = null;
var cur_notes: std.ArrayListUnmanaged(ErrorBundle.ErrorMessage) = .empty; var cur_notes: std.ArrayList(ErrorBundle.ErrorMessage) = .empty;
defer cur_notes.deinit(gpa); defer cur_notes.deinit(gpa);
for (comp.diagnostics.list.items) |msg| { for (comp.diagnostics.list.items) |msg| {
switch (msg.kind) { switch (msg.kind) {
@ -971,11 +981,11 @@ const MsgWriter = struct {
} }
pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void { pub fn print(m: *MsgWriter, comptime fmt: []const u8, args: anytype) void {
m.buf.writer().print(fmt, args) catch {}; m.buf.print(fmt, args) catch {};
} }
pub fn write(m: *MsgWriter, msg: []const u8) void { pub fn write(m: *MsgWriter, msg: []const u8) void {
m.buf.writer().writeAll(msg) catch {}; m.buf.appendSlice(msg) catch {};
} }
pub fn setColor(m: *MsgWriter, color: std.io.tty.Color) void { pub fn setColor(m: *MsgWriter, color: std.io.tty.Color) void {

View file

@ -18,12 +18,15 @@ pub fn preprocess(
var driver: aro.Driver = .{ .comp = comp, .aro_name = "arocc" }; var driver: aro.Driver = .{ .comp = comp, .aro_name = "arocc" };
defer driver.deinit(); defer driver.deinit();
var macro_buf = std.array_list.Managed(u8).init(comp.gpa); var macro_buf: std.Io.Writer.Allocating = .init(comp.gpa);
defer macro_buf.deinit(); defer macro_buf.deinit();
_ = driver.parseArgs(std.io.null_writer, macro_buf.writer(), argv) catch |err| switch (err) { var trash: [64]u8 = undefined;
var discarding: std.Io.Writer.Discarding = .init(&trash);
_ = driver.parseArgs(&discarding.writer, &macro_buf.writer, argv) catch |err| switch (err) {
error.FatalError => return error.ArgError, error.FatalError => return error.ArgError,
error.OutOfMemory => |e| return e, error.OutOfMemory => |e| return e,
error.WriteFailed => return error.OutOfMemory,
}; };
if (hasAnyErrors(comp)) return error.ArgError; if (hasAnyErrors(comp)) return error.ArgError;
@ -33,7 +36,7 @@ pub fn preprocess(
error.FatalError => return error.GeneratedSourceError, error.FatalError => return error.GeneratedSourceError,
else => |e| return e, else => |e| return e,
}; };
const user_macros = comp.addSourceFromBuffer("<command line>", macro_buf.items) catch |err| switch (err) { const user_macros = comp.addSourceFromBuffer("<command line>", macro_buf.written()) catch |err| switch (err) {
error.FatalError => return error.GeneratedSourceError, error.FatalError => return error.GeneratedSourceError,
else => |e| return e, else => |e| return e,
}; };
@ -59,7 +62,9 @@ pub fn preprocess(
if (hasAnyErrors(comp)) return error.PreprocessError; if (hasAnyErrors(comp)) return error.PreprocessError;
try pp.prettyPrintTokens(writer, .result_only); pp.prettyPrintTokens(writer, .result_only) catch |err| switch (err) {
error.WriteFailed => return error.OutOfMemory,
};
if (maybe_dependencies_list) |dependencies_list| { if (maybe_dependencies_list) |dependencies_list| {
for (comp.sources.values()) |comp_source| { for (comp.sources.values()) |comp_source| {