Skip to content

Commit

Permalink
Fix #16702, add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tau-dev committed Jul 16, 2024
1 parent ae89705 commit 24ebb83
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 19 deletions.
82 changes: 64 additions & 18 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2216,8 +2216,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
}
}
if (break_label != 0) {
const label_name = try astgen.identifierTokenString(break_label);
return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
const label_name = astgen.fmtIdentifier(break_label);
return astgen.failTok(break_label, "label not found: {}", .{label_name});
} else {
return astgen.failNode(node, "break expression outside loop", .{});
}
Expand Down Expand Up @@ -2289,8 +2289,8 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
}
}
if (break_label != 0) {
const label_name = try astgen.identifierTokenString(break_label);
return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
const label_name = astgen.fmtIdentifier(break_label);
return astgen.failTok(break_label, "label not found: {}", .{label_name});
} else {
return astgen.failNode(node, "continue expression outside loop", .{});
}
Expand Down Expand Up @@ -2400,10 +2400,8 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
const gen_zir = scope.cast(GenZir).?;
if (gen_zir.label) |prev_label| {
if (try astgen.tokenIdentEql(label, prev_label.token)) {
const label_name = try astgen.identifierTokenString(label);
return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
label_name,
}, &[_]u32{
const label_name = astgen.fmtIdentifier(label);
return astgen.failTokNotes(label, "redefinition of label {}", .{label_name}, &[_]u32{
try astgen.errNoteTok(
prev_label.token,
"previous definition here",
Expand Down Expand Up @@ -4740,8 +4738,8 @@ fn testDecl(
.top => break,
};
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(test_name_token);
return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
const ident_name = astgen.fmtIdentifier(test_name_token);
return astgen.failTok(test_name_token, "use of undeclared identifier {}", .{ident_name});
}

break :blk .{ .decltest = name_str_index };
Expand Down Expand Up @@ -8300,8 +8298,8 @@ fn localVarRef(

// Can't close over a runtime variable
if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
const ident_name = astgen.fmtIdentifier(ident_token);
return astgen.failNodeNotes(ident, "mutable {} not accessible from here", .{ident_name}, &.{
try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
});
Expand Down Expand Up @@ -8355,10 +8353,8 @@ fn localVarRef(
},
.top => break,
};
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
}
if (found_already == null)
return astgen.failNode(ident, "use of undeclared identifier {}", .{astgen.fmtIdentifier(ident_token)});

// Decl references happen by name rather than ZIR index so that when unrelated
// decls are modified, ZIR code containing references to them can be unmodified.
Expand Down Expand Up @@ -9238,8 +9234,8 @@ fn builtinCall(
.top => break,
};
if (found_already == null) {
const ident_name = try astgen.identifierTokenString(ident_token);
return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
const ident_name = astgen.fmtIdentifier(ident_token);
return astgen.failNode(params[0], "use of undeclared identifier {}", .{ident_name});
}
},
.field_access => {
Expand Down Expand Up @@ -11215,6 +11211,56 @@ fn rvalueInner(
}
}

fn fmtIdentifier(astgen: *AstGen, ident_token: Ast.TokenIndex) std.fmt.Formatter(formatIdentifier) {
return .{ .data = .{
.self = astgen,
.token_idx = ident_token,
} };
}

const IdentifierFmt = struct {
self: *AstGen,
token_idx: Ast.TokenIndex,
};

fn formatIdentifier(
ctx: IdentifierFmt,
comptime unused_format_string: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
comptime assert(unused_format_string.len == 0);
const astgen = ctx.self;

const token_slice = astgen.tree.tokenSlice(ctx.token_idx);
var buf: ArrayListUnmanaged(u8) = .{};
defer buf.deinit(astgen.gpa);

const complete_slice = if (token_slice[0] == '@') blk: {
try astgen.parseStrLit(ctx.token_idx, &buf, token_slice, 1);
break :blk buf.items;
} else token_slice;

const first = complete_slice[0];
const needs_escaping = if (!std.ascii.isAlphabetic(first) and first != '_')
true
else for (complete_slice[1..]) |c| {
if (!std.ascii.isAlphanumeric(c) and c != '_') {
break true;
}
} else false;

if (needs_escaping) {
try writer.writeAll("@\"");
try std.zig.stringEscape(complete_slice, "", .{}, writer);
try writer.writeByte('"');
} else {
try writer.writeByte('\'');
try writer.writeAll(complete_slice);
try writer.writeByte('\'');
}
}

/// Given an identifier token, obtain the string for it.
/// If the token uses @"" syntax, parses as a string, reports errors if applicable,
/// and allocates the result within `astgen.arena`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ comptime {
// target=native
//
// :2:15: error: expected error set type, found 'error.Foo'
// :3:15: error: expected error set type, found 'i32'
// :7:15: error: expected error set type, found 'i32'
70 changes: 70 additions & 0 deletions test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,76 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
});
}

{
const case = ctx.obj("identifier escapes", b.graph.host);

case.addError(
\\export fn a() void {
\\ return @"Á";
\\}
\\
\\export fn b() void {
\\ return @"B";
\\}
\\
\\export fn c() void {
\\ return @"a\nb";
\\}
, &[_][]const u8{
\\:2:12: error: use of undeclared identifier @"\xc3\x81"
,
\\:6:12: error: use of undeclared identifier 'B'
,
\\:10:12: error: use of undeclared identifier @"a\nb"
});
}


{
const case = ctx.obj("unmatched parentheses", b.graph.host);

case.addError(
\\export fn a() void {
\\}
\\}
, &[_][]const u8{
":3:1: error: unmatched curly brace",
":2:2: error: expected 'EOF', found '}'",
});

}

{
const case = ctx.obj("unmatched parentheses #2", b.graph.host);

case.addError(
\\const c = {
\\)
\\};
, &[_][]const u8{
":2:1: error: unmatched parenthesis",
":2:1: error: expected statement, found ')'",
});
}

{
const case = ctx.obj("unmatched parentheses #3", b.graph.host);

case.addError(
\\pub fn bar() void {
\\ // Oops...
\\ }
\\
\\ if (true) {
\\ return;
\\ }
\\}
, &[_][]const u8{
":8:1: error: unmatched curly brace",
":5:15: error: expected type expression, found '{'",
});
}

{
const case = ctx.obj("isolated carriage return in multiline string literal", b.graph.host);

Expand Down

0 comments on commit 24ebb83

Please sign in to comment.