summaryrefslogtreecommitdiff
path: root/src/cli/diagnostics.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/diagnostics.zig')
-rw-r--r--src/cli/diagnostics.zig124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/cli/diagnostics.zig b/src/cli/diagnostics.zig
new file mode 100644
index 000000000..80dc06b5a
--- /dev/null
+++ b/src/cli/diagnostics.zig
@@ -0,0 +1,124 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const build_config = @import("../build_config.zig");
+
+/// A diagnostic message from parsing. This is used to provide additional
+/// human-friendly warnings and errors about the parsed data.
+///
+/// All of the memory for the diagnostic is allocated from the arena
+/// associated with the config structure. If an arena isn't available
+/// then diagnostics are not supported.
+pub const Diagnostic = struct {
+ location: Location = .none,
+ key: [:0]const u8 = "",
+ message: [:0]const u8,
+
+ /// The possible locations for a diagnostic message. This is used
+ /// to provide context for the message.
+ pub const Location = union(enum) {
+ none,
+ cli: usize,
+ file: struct {
+ path: []const u8,
+ line: usize,
+ },
+
+ pub fn fromIter(iter: anytype) Location {
+ const Iter = t: {
+ const T = @TypeOf(iter);
+ break :t switch (@typeInfo(T)) {
+ .Pointer => |v| v.child,
+ .Struct => T,
+ else => return .none,
+ };
+ };
+
+ if (!@hasDecl(Iter, "location")) return .none;
+ return iter.location() orelse .none;
+ }
+ };
+
+ /// Write the full user-friendly diagnostic message to the writer.
+ pub fn write(self: *const Diagnostic, writer: anytype) !void {
+ switch (self.location) {
+ .none => {},
+ .cli => |index| try writer.print("cli:{}:", .{index}),
+ .file => |file| try writer.print(
+ "{s}:{}:",
+ .{ file.path, file.line },
+ ),
+ }
+
+ if (self.key.len > 0) {
+ try writer.print("{s}: ", .{self.key});
+ } else if (self.location != .none) {
+ try writer.print(" ", .{});
+ }
+
+ try writer.print("{s}", .{self.message});
+ }
+};
+
+/// A list of diagnostics. The "_diagnostics" field must be this type
+/// for diagnostics to be supported. If this field is an incorrect type
+/// a compile-time error will be raised.
+///
+/// This is implemented as a simple wrapper around an array list
+/// so that we can inject some logic around adding diagnostics
+/// and potentially in the future structure them differently.
+pub const DiagnosticList = struct {
+ /// The list of diagnostics.
+ list: std.ArrayListUnmanaged(Diagnostic) = .{},
+
+ /// Precomputed data for diagnostics. This is used specifically
+ /// when we build libghostty so that we can precompute the messages
+ /// and return them via the C API without allocating memory at
+ /// call time.
+ precompute: Precompute = precompute_init,
+
+ const precompute_enabled = switch (build_config.artifact) {
+ // We enable precompute for tests so that the logic is
+ // semantically analyzed and run.
+ .exe, .wasm_module => builtin.is_test,
+
+ // We specifically want precompute for libghostty.
+ .lib => true,
+ };
+ const Precompute = if (precompute_enabled) struct {
+ messages: std.ArrayListUnmanaged([:0]const u8) = .{},
+ } else void;
+ const precompute_init: Precompute = if (precompute_enabled) .{} else {};
+
+ pub fn append(
+ self: *DiagnosticList,
+ alloc: Allocator,
+ diag: Diagnostic,
+ ) Allocator.Error!void {
+ try self.list.append(alloc, diag);
+ errdefer _ = self.list.pop();
+
+ if (comptime precompute_enabled) {
+ var buf = std.ArrayList(u8).init(alloc);
+ defer buf.deinit();
+ try diag.write(buf.writer());
+
+ const owned: [:0]const u8 = try buf.toOwnedSliceSentinel(0);
+ errdefer alloc.free(owned);
+
+ try self.precompute.messages.append(alloc, owned);
+ errdefer _ = self.precompute.messages.pop();
+
+ assert(self.precompute.messages.items.len == self.list.items.len);
+ }
+ }
+
+ pub fn empty(self: *const DiagnosticList) bool {
+ return self.list.items.len == 0;
+ }
+
+ pub fn items(self: *DiagnosticList) []Diagnostic {
+ return self.list.items;
+ }
+};