mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Fix some error reporting bugs
This commit is contained in:
parent
b8c77f591e
commit
bc178fa664
3 changed files with 176 additions and 12 deletions
|
|
@ -656,11 +656,11 @@ pub const Store = struct {
|
|||
break :blk self.snapshots.sliceVars(fn_args)[0];
|
||||
};
|
||||
|
||||
try self.writeWithContext(idx, .General, dispatcher);
|
||||
try self.writeWithContext(dispatcher, .General, idx);
|
||||
_ = try self.buf.writer().write(".");
|
||||
_ = try self.buf.writer().write(self.idents.getText(constraint.fn_name));
|
||||
_ = try self.buf.writer().write(" : ");
|
||||
try self.writeWithContext(idx, .General, constraint.fn_content);
|
||||
try self.writeWithContext(constraint.fn_content, .General, idx);
|
||||
}
|
||||
_ = try self.buf.writer().write("]");
|
||||
}
|
||||
|
|
@ -1048,15 +1048,77 @@ pub const Store = struct {
|
|||
}
|
||||
|
||||
/// Append a constraint to the list, if it doesn't already exist
|
||||
/// Deduplicates based on method name and dispatcher type variable
|
||||
fn appendStaticDispatchConstraint(self: *Self, constraint_to_add: SnapshotStaticDispatchConstraint) std.mem.Allocator.Error!void {
|
||||
// Extract dispatcher (first arg) identity from the constraint to add
|
||||
const add_dispatcher = self.getDispatcherIdentity(constraint_to_add.fn_content);
|
||||
|
||||
for (self.static_dispatch_constraints.items) |constraint| {
|
||||
if (constraint.fn_name == constraint_to_add.fn_name and constraint.fn_content == constraint_to_add.fn_content) {
|
||||
return;
|
||||
if (constraint.fn_name == constraint_to_add.fn_name) {
|
||||
// Same method name - check if dispatcher identity is the same
|
||||
const existing_dispatcher = self.getDispatcherIdentity(constraint.fn_content);
|
||||
if (dispatcherIdentitiesEqual(add_dispatcher, existing_dispatcher)) {
|
||||
return; // Duplicate constraint
|
||||
}
|
||||
// Also check fn_content directly for backwards compatibility
|
||||
if (constraint.fn_content == constraint_to_add.fn_content) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = try self.static_dispatch_constraints.append(constraint_to_add);
|
||||
}
|
||||
|
||||
/// Dispatcher identity for deduplication - either a Var (for flex), an Ident.Idx (for rigid), or recursive
|
||||
const DispatcherIdentity = union(enum) {
|
||||
flex_var: Var,
|
||||
rigid_name: Ident.Idx,
|
||||
recursive: void, // All recursive types are equivalent for deduplication
|
||||
};
|
||||
|
||||
/// Get the dispatcher (first argument) identity from a function content
|
||||
fn getDispatcherIdentity(self: *Self, fn_content: SnapshotContentIdx) ?DispatcherIdentity {
|
||||
const content = self.snapshots.getContent(fn_content);
|
||||
if (content != .structure) return null;
|
||||
|
||||
const fn_args = switch (content.structure) {
|
||||
.fn_effectful => |func| func.args,
|
||||
.fn_pure => |func| func.args,
|
||||
.fn_unbound => |func| func.args,
|
||||
else => return null,
|
||||
};
|
||||
|
||||
if (fn_args.len() == 0) return null;
|
||||
|
||||
const first_arg_idx = self.snapshots.sliceVars(fn_args)[0];
|
||||
const first_arg_content = self.snapshots.getContent(first_arg_idx);
|
||||
|
||||
return switch (first_arg_content) {
|
||||
.flex => |flex| DispatcherIdentity{ .flex_var = flex.var_ },
|
||||
.rigid => |rigid| DispatcherIdentity{ .rigid_name = rigid.name },
|
||||
.recursive => DispatcherIdentity{ .recursive = {} },
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn dispatcherIdentitiesEqual(a: ?DispatcherIdentity, b: ?DispatcherIdentity) bool {
|
||||
if (a == null or b == null) return false;
|
||||
return switch (a.?) {
|
||||
.flex_var => |a_var| switch (b.?) {
|
||||
.flex_var => |b_var| a_var == b_var,
|
||||
else => false,
|
||||
},
|
||||
.rigid_name => |a_name| switch (b.?) {
|
||||
.rigid_name => |b_name| a_name == b_name,
|
||||
else => false,
|
||||
},
|
||||
.recursive => switch (b.?) {
|
||||
.recursive => true, // All recursive types are equal
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a name for a flex var that may appear multiple times in the type
|
||||
fn writeFlexVarName(self: *Self, flex_var: Var, _: SnapshotContentIdx, context: TypeContext, root_idx: SnapshotContentIdx) std.mem.Allocator.Error!void {
|
||||
// Check if we've seen this flex var before.
|
||||
|
|
@ -1464,11 +1526,11 @@ pub const SnapshotWriter = struct {
|
|||
break :blk self.snapshots.sliceVars(fn_args)[0];
|
||||
};
|
||||
|
||||
try self.writeWithContext(idx, .General, dispatcher);
|
||||
try self.writeWithContext(dispatcher, .General, idx);
|
||||
_ = try self.buf.writer().write(".");
|
||||
_ = try self.buf.writer().write(self.idents.getText(constraint.fn_name));
|
||||
_ = try self.buf.writer().write(" : ");
|
||||
try self.writeWithContext(idx, .General, constraint.fn_content);
|
||||
try self.writeWithContext(constraint.fn_content, .General, idx);
|
||||
}
|
||||
_ = try self.buf.writer().write("]");
|
||||
}
|
||||
|
|
@ -1856,15 +1918,77 @@ pub const SnapshotWriter = struct {
|
|||
}
|
||||
|
||||
/// Append a constraint to the list, if it doesn't already exist
|
||||
/// Deduplicates based on method name and dispatcher type variable
|
||||
fn appendStaticDispatchConstraint(self: *Self, constraint_to_add: SnapshotStaticDispatchConstraint) std.mem.Allocator.Error!void {
|
||||
// Extract dispatcher (first arg) identity from the constraint to add
|
||||
const add_dispatcher = self.getDispatcherIdentity(constraint_to_add.fn_content);
|
||||
|
||||
for (self.static_dispatch_constraints.items) |constraint| {
|
||||
if (constraint.fn_name == constraint_to_add.fn_name and constraint.fn_content == constraint_to_add.fn_content) {
|
||||
return;
|
||||
if (constraint.fn_name == constraint_to_add.fn_name) {
|
||||
// Same method name - check if dispatcher identity is the same
|
||||
const existing_dispatcher = self.getDispatcherIdentity(constraint.fn_content);
|
||||
if (dispatcherIdentitiesEqual(add_dispatcher, existing_dispatcher)) {
|
||||
return; // Duplicate constraint
|
||||
}
|
||||
// Also check fn_content directly for backwards compatibility
|
||||
if (constraint.fn_content == constraint_to_add.fn_content) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = try self.static_dispatch_constraints.append(constraint_to_add);
|
||||
}
|
||||
|
||||
/// Dispatcher identity for deduplication - either a Var (for flex), an Ident.Idx (for rigid), or recursive
|
||||
const DispatcherIdentity = union(enum) {
|
||||
flex_var: Var,
|
||||
rigid_name: Ident.Idx,
|
||||
recursive: void, // All recursive types are equivalent for deduplication
|
||||
};
|
||||
|
||||
/// Get the dispatcher (first argument) identity from a function content
|
||||
fn getDispatcherIdentity(self: *Self, fn_content: SnapshotContentIdx) ?DispatcherIdentity {
|
||||
const content = self.snapshots.getContent(fn_content);
|
||||
if (content != .structure) return null;
|
||||
|
||||
const fn_args = switch (content.structure) {
|
||||
.fn_effectful => |func| func.args,
|
||||
.fn_pure => |func| func.args,
|
||||
.fn_unbound => |func| func.args,
|
||||
else => return null,
|
||||
};
|
||||
|
||||
if (fn_args.len() == 0) return null;
|
||||
|
||||
const first_arg_idx = self.snapshots.sliceVars(fn_args)[0];
|
||||
const first_arg_content = self.snapshots.getContent(first_arg_idx);
|
||||
|
||||
return switch (first_arg_content) {
|
||||
.flex => |flex| DispatcherIdentity{ .flex_var = flex.var_ },
|
||||
.rigid => |rigid| DispatcherIdentity{ .rigid_name = rigid.name },
|
||||
.recursive => DispatcherIdentity{ .recursive = {} },
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
fn dispatcherIdentitiesEqual(a: ?DispatcherIdentity, b: ?DispatcherIdentity) bool {
|
||||
if (a == null or b == null) return false;
|
||||
return switch (a.?) {
|
||||
.flex_var => |a_var| switch (b.?) {
|
||||
.flex_var => |b_var| a_var == b_var,
|
||||
else => false,
|
||||
},
|
||||
.rigid_name => |a_name| switch (b.?) {
|
||||
.rigid_name => |b_name| a_name == b_name,
|
||||
else => false,
|
||||
},
|
||||
.recursive => switch (b.?) {
|
||||
.recursive => true, // All recursive types are equal
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a name for a flex var that may appear multiple times in the type
|
||||
fn writeFlexVarName(self: *Self, flex_var: Var, _: SnapshotContentIdx, context: TypeContext, root_idx: SnapshotContentIdx) std.mem.Allocator.Error!void {
|
||||
// Check if we've seen this flex var before.
|
||||
|
|
|
|||
|
|
@ -298,8 +298,8 @@ fn renderElementToTerminal(element: DocumentElement, writer: *std.Io.Writer, pal
|
|||
try writer.writeAll(" │ ");
|
||||
try writer.writeAll(palette.reset);
|
||||
|
||||
// Print spaces up to the start column
|
||||
try source_region.printSpaces(writer, region.start_column - 1);
|
||||
// Print leading whitespace, preserving tabs from the source line
|
||||
try source_region.printLeadingWhitespace(writer, line, region.start_column);
|
||||
|
||||
// Print the underline
|
||||
try writer.writeAll(color);
|
||||
|
|
@ -370,9 +370,15 @@ fn renderElementToTerminal(element: DocumentElement, writer: *std.Io.Writer, pal
|
|||
var col_position: u32 = 1;
|
||||
for (data.underline_regions) |underline| {
|
||||
if (underline.start_line == line_num and underline.start_line == underline.end_line) {
|
||||
// Print spaces up to the start column
|
||||
// Print whitespace up to the start column
|
||||
if (underline.start_column > col_position) {
|
||||
try source_region.printSpaces(writer, underline.start_column - col_position);
|
||||
if (col_position == 1) {
|
||||
// First underline: preserve tabs from source
|
||||
try source_region.printLeadingWhitespace(writer, line, underline.start_column);
|
||||
} else {
|
||||
// Subsequent underlines: just use spaces
|
||||
try source_region.printSpaces(writer, underline.start_column - col_position);
|
||||
}
|
||||
}
|
||||
|
||||
// Print the underline
|
||||
|
|
|
|||
|
|
@ -32,6 +32,40 @@ pub fn printSpaces(writer: anytype, count: u32) !void {
|
|||
}
|
||||
}
|
||||
|
||||
/// Print leading whitespace from source line, preserving tabs.
|
||||
/// Copies exact whitespace characters (tabs/spaces) from the line,
|
||||
/// and uses spaces for non-whitespace characters.
|
||||
/// This ensures underlines align correctly when the source contains tabs.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - writer: The output writer
|
||||
/// - line: The source line text
|
||||
/// - target_column: The 1-based column to print up to (exclusive)
|
||||
pub fn printLeadingWhitespace(writer: anytype, line: []const u8, target_column: u32) !void {
|
||||
if (target_column <= 1) return;
|
||||
|
||||
const chars_to_print = target_column - 1;
|
||||
var i: u32 = 0;
|
||||
while (i < chars_to_print) : (i += 1) {
|
||||
if (i < line.len) {
|
||||
const char = line[i];
|
||||
if (char == '\t') {
|
||||
// Preserve tabs exactly
|
||||
try writer.writeAll("\t");
|
||||
} else if (char == ' ') {
|
||||
// Preserve spaces exactly
|
||||
try writer.writeAll(" ");
|
||||
} else {
|
||||
// Non-whitespace: use a space to maintain width
|
||||
try writer.writeAll(" ");
|
||||
}
|
||||
} else {
|
||||
// Past end of line: use spaces
|
||||
try writer.writeAll(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== TESTS =====
|
||||
|
||||
test "calculateLineNumberWidth" {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue