Merge pull request #10913 from Vexu/err

further parser error improvements
This commit is contained in:
Veikka Tuominen 2022-02-18 13:53:47 +02:00 committed by GitHub
commit 53241f288e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 351 additions and 252 deletions

View File

@ -10405,7 +10405,7 @@ pub fn main() !void {
<p>String literals such as {#syntax#}"foo"{#endsyntax#} are in the global constant data section.
This is why it is an error to pass a string literal to a mutable slice, like this:
</p>
{#code_begin|test_err|expected type '[]u8'#}
{#code_begin|test_err|cannot cast pointer to array literal to slice type '[]u8'#}
fn foo(s: []u8) void {
_ = s;
}

View File

@ -66,20 +66,11 @@ pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void
/// Returns an extra offset for column and byte offset of errors that
/// should point after the token in the error message.
pub fn errorOffset(tree: Ast, error_tag: Error.Tag, token: TokenIndex) u32 {
return switch (error_tag) {
.expected_semi_after_decl,
.expected_semi_after_stmt,
.expected_comma_after_field,
.expected_comma_after_arg,
.expected_comma_after_param,
.expected_comma_after_initializer,
.expected_comma_after_switch_prong,
.expected_semi_or_else,
.expected_semi_or_lbrace,
=> @intCast(u32, tree.tokenSlice(token).len),
else => 0,
};
pub fn errorOffset(tree: Ast, parse_error: Error) u32 {
return if (parse_error.token_is_prev)
@intCast(u32, tree.tokenSlice(parse_error.token).len)
else
0;
}
pub fn tokenLocation(self: Ast, start_offset: ByteOffset, token_index: TokenIndex) Location {
@ -162,22 +153,22 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
},
.expected_block => {
return stream.print("expected block or field, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_block_or_assignment => {
return stream.print("expected block or assignment, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_block_or_expr => {
return stream.print("expected block or expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_block_or_field => {
return stream.print("expected block or field, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_container_members => {
@ -187,42 +178,42 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
},
.expected_expr => {
return stream.print("expected expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_expr_or_assignment => {
return stream.print("expected expression or assignment, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_fn => {
return stream.print("expected function, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_inlinable => {
return stream.print("expected 'while' or 'for', found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_labelable => {
return stream.print("expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_param_list => {
return stream.print("expected parameter list, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_prefix_expr => {
return stream.print("expected prefix expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_primary_type_expr => {
return stream.print("expected primary type expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_pub_item => {
@ -230,7 +221,7 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
},
.expected_return_type => {
return stream.print("expected return type expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_semi_or_else => {
@ -244,39 +235,34 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
token_tags[parse_error.token].symbol(),
});
},
.expected_string_literal => {
return stream.print("expected string literal, found '{s}'", .{
token_tags[parse_error.token].symbol(),
});
},
.expected_suffix_op => {
return stream.print("expected pointer dereference, optional unwrap, or field access, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_type_expr => {
return stream.print("expected type expression, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_var_decl => {
return stream.print("expected variable declaration, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_var_decl_or_fn => {
return stream.print("expected variable declaration or function, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_loop_payload => {
return stream.print("expected loop payload, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.expected_container => {
return stream.print("expected a struct, enum or union, found '{s}'", .{
token_tags[parse_error.token].symbol(),
token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)].symbol(),
});
},
.extern_fn_body => {
@ -305,11 +291,6 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
.invalid_bit_range => {
return stream.writeAll("bit range not allowed on slices and arrays");
},
.invalid_token => {
return stream.print("invalid token: '{s}'", .{
token_tags[parse_error.token].symbol(),
});
},
.same_line_doc_comment => {
return stream.writeAll("same line documentation comment");
},
@ -319,6 +300,9 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
.varargs_nonfinal => {
return stream.writeAll("function prototype has parameter after varargs");
},
.expected_continue_expr => {
return stream.writeAll("expected ':' before while continue expression");
},
.expected_semi_after_decl => {
return stream.writeAll("expected ';' after declaration");
@ -341,9 +325,19 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
.expected_comma_after_switch_prong => {
return stream.writeAll("expected ',' after switch prong");
},
.expected_initializer => {
return stream.writeAll("expected field initializer");
},
.previous_field => {
return stream.writeAll("field before declarations here");
},
.next_field => {
return stream.writeAll("field after declarations here");
},
.expected_token => {
const found_tag = token_tags[parse_error.token];
const found_tag = token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)];
const expected_symbol = parse_error.extra.expected_tag.symbol();
switch (found_tag) {
.invalid => return stream.print("expected '{s}', found invalid bytes", .{
@ -2483,6 +2477,9 @@ pub const full = struct {
pub const Error = struct {
tag: Tag,
is_note: bool = false,
/// True if `token` points to the token before the token causing an issue.
token_is_prev: bool = false,
token: TokenIndex,
extra: union {
none: void,
@ -2511,7 +2508,6 @@ pub const Error = struct {
expected_semi_or_else,
expected_semi_or_lbrace,
expected_statement,
expected_string_literal,
expected_suffix_op,
expected_type_expr,
expected_var_decl,
@ -2526,12 +2522,10 @@ pub const Error = struct {
extra_volatile_qualifier,
ptr_mod_on_array_child_type,
invalid_bit_range,
invalid_token,
same_line_doc_comment,
unattached_doc_comment,
varargs_nonfinal,
// these have `token` set to token after which a semicolon was expected
expected_continue_expr,
expected_semi_after_decl,
expected_semi_after_stmt,
expected_comma_after_field,
@ -2539,6 +2533,10 @@ pub const Error = struct {
expected_comma_after_param,
expected_comma_after_initializer,
expected_comma_after_switch_prong,
expected_initializer,
previous_field,
next_field,
/// `expected_tag` is populated.
expected_token,

View File

@ -91,6 +91,9 @@ const Parser = struct {
extra_data: std.ArrayListUnmanaged(Node.Index),
scratch: std.ArrayListUnmanaged(Node.Index),
/// Used for the error note of decl_between_fields error.
last_field: TokenIndex = undefined,
const SmallSpan = union(enum) {
zero_or_one: Node.Index,
multi: Node.SubRange,
@ -147,11 +150,6 @@ const Parser = struct {
return result;
}
fn warn(p: *Parser, tag: Ast.Error.Tag) error{OutOfMemory}!void {
@setCold(true);
try p.warnMsg(.{ .tag = tag, .token = p.tok_i });
}
fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void {
@setCold(true);
try p.warnMsg(.{
@ -161,13 +159,53 @@ const Parser = struct {
});
}
fn warnExpectedAfter(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void {
fn warn(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void {
@setCold(true);
try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i - 1 });
try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i });
}
fn warnMsg(p: *Parser, msg: Ast.Error) error{OutOfMemory}!void {
@setCold(true);
switch (msg.tag) {
.expected_semi_after_decl,
.expected_semi_after_stmt,
.expected_comma_after_field,
.expected_comma_after_arg,
.expected_comma_after_param,
.expected_comma_after_initializer,
.expected_comma_after_switch_prong,
.expected_semi_or_else,
.expected_semi_or_lbrace,
.expected_token,
.expected_block,
.expected_block_or_assignment,
.expected_block_or_expr,
.expected_block_or_field,
.expected_container_members,
.expected_expr,
.expected_expr_or_assignment,
.expected_fn,
.expected_inlinable,
.expected_labelable,
.expected_param_list,
.expected_prefix_expr,
.expected_primary_type_expr,
.expected_pub_item,
.expected_return_type,
.expected_suffix_op,
.expected_type_expr,
.expected_var_decl,
.expected_var_decl_or_fn,
.expected_loop_payload,
.expected_container,
=> if (msg.token != 0 and !p.tokensOnSameLine(msg.token - 1, msg.token)) {
var copy = msg;
copy.token_is_prev = true;
copy.token -= 1;
return p.errors.append(p.gpa, copy);
},
else => {},
}
try p.errors.append(p.gpa, msg);
}
@ -235,6 +273,8 @@ const Parser = struct {
.keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
.identifier => {
p.tok_i += 1;
const identifier = p.tok_i;
defer p.last_field = identifier;
const container_field = try p.expectContainerFieldRecoverable();
if (container_field != 0) {
switch (field_state) {
@ -245,6 +285,16 @@ const Parser = struct {
.tag = .decl_between_fields,
.token = p.nodes.items(.main_token)[node],
});
try p.warnMsg(.{
.tag = .previous_field,
.is_note = true,
.token = p.last_field,
});
try p.warnMsg(.{
.tag = .next_field,
.is_note = true,
.token = identifier,
});
// Continue parsing; error will be reported later.
field_state = .err;
},
@ -264,7 +314,7 @@ const Parser = struct {
}
// There is not allowed to be a decl after a field with no comma.
// Report error but recover parser.
try p.warnExpectedAfter(.expected_comma_after_field);
try p.warn(.expected_comma_after_field);
p.findNextContainerMember();
}
},
@ -338,6 +388,8 @@ const Parser = struct {
trailing = p.token_tags[p.tok_i - 1] == .semicolon;
},
.identifier => {
const identifier = p.tok_i;
defer p.last_field = identifier;
const container_field = try p.expectContainerFieldRecoverable();
if (container_field != 0) {
switch (field_state) {
@ -348,6 +400,14 @@ const Parser = struct {
.tag = .decl_between_fields,
.token = p.nodes.items(.main_token)[node],
});
try p.warnMsg(.{
.tag = .previous_field,
.token = p.last_field,
});
try p.warnMsg(.{
.tag = .next_field,
.token = identifier,
});
// Continue parsing; error will be reported later.
field_state = .err;
},
@ -367,7 +427,7 @@ const Parser = struct {
}
// There is not allowed to be a decl after a field with no comma.
// Report error but recover parser.
try p.warnExpectedAfter(.expected_comma_after_field);
try p.warn(.expected_comma_after_field);
p.findNextContainerMember();
}
},
@ -585,7 +645,7 @@ const Parser = struct {
// Since parseBlock only return error.ParseError on
// a missing '}' we can assume this function was
// supposed to end here.
try p.warnExpectedAfter(.expected_semi_or_lbrace);
try p.warn(.expected_semi_or_lbrace);
return null_node;
},
}
@ -996,7 +1056,7 @@ const Parser = struct {
};
_ = p.eatToken(.keyword_else) orelse {
if (else_required) {
try p.warnExpectedAfter(.expected_semi_or_else);
try p.warn(.expected_semi_or_else);
}
return p.addNode(.{
.tag = .if_simple,
@ -1091,7 +1151,7 @@ const Parser = struct {
};
_ = p.eatToken(.keyword_else) orelse {
if (else_required) {
try p.warnExpectedAfter(.expected_semi_or_else);
try p.warn(.expected_semi_or_else);
}
return p.addNode(.{
.tag = .for_simple,
@ -1166,7 +1226,7 @@ const Parser = struct {
};
_ = p.eatToken(.keyword_else) orelse {
if (else_required) {
try p.warnExpectedAfter(.expected_semi_or_else);
try p.warn(.expected_semi_or_else);
}
if (cont_expr == 0) {
return p.addNode(.{
@ -1402,7 +1462,8 @@ const Parser = struct {
}
const rhs = try p.parseExprPrecedence(info.prec + 1);
if (rhs == 0) {
return p.fail(.invalid_token);
try p.warn(.expected_expr);
return node;
}
node = try p.addNode(.{
@ -1881,7 +1942,7 @@ const Parser = struct {
/// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
fn parseIfExpr(p: *Parser) !Node.Index {
return p.parseIf(parseExpr);
return p.parseIf(expectExpr);
}
/// Block <- LBRACE Statement* RBRACE
@ -2050,7 +2111,7 @@ const Parser = struct {
},
.colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_initializer),
else => try p.warn(.expected_comma_after_initializer),
}
if (p.eatToken(.r_brace)) |_| break;
const next = try p.expectFieldInit();
@ -2091,7 +2152,7 @@ const Parser = struct {
},
.colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_initializer),
else => try p.warn(.expected_comma_after_initializer),
}
}
const comma = (p.token_tags[p.tok_i - 2] == .comma);
@ -2170,7 +2231,7 @@ const Parser = struct {
},
.colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_arg),
else => try p.warn(.expected_comma_after_arg),
}
}
const comma = (p.token_tags[p.tok_i - 2] == .comma);
@ -2226,7 +2287,7 @@ const Parser = struct {
},
.colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_arg),
else => try p.warn(.expected_comma_after_arg),
}
}
const comma = (p.token_tags[p.tok_i - 2] == .comma);
@ -2349,7 +2410,7 @@ const Parser = struct {
.builtin => return p.parseBuiltinCall(),
.keyword_fn => return p.parseFnProto(),
.keyword_if => return p.parseIf(parseTypeExpr),
.keyword_if => return p.parseIf(expectTypeExpr),
.keyword_switch => return p.expectSwitchExpr(),
.keyword_extern,
@ -2467,7 +2528,7 @@ const Parser = struct {
},
.colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_initializer),
else => try p.warn(.expected_comma_after_initializer),
}
if (p.eatToken(.r_brace)) |_| break;
const next = try p.expectFieldInit();
@ -2519,7 +2580,7 @@ const Parser = struct {
},
.colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_initializer),
else => try p.warn(.expected_comma_after_initializer),
}
}
const comma = (p.token_tags[p.tok_i - 2] == .comma);
@ -2580,7 +2641,7 @@ const Parser = struct {
},
.colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_field),
else => try p.warn(.expected_comma_after_field),
}
}
return p.addNode(.{
@ -2879,7 +2940,7 @@ const Parser = struct {
p.tok_i += 2;
return identifier;
}
return 0;
return null_node;
}
/// FieldInit <- DOT IDENTIFIER EQUAL Expr
@ -2896,15 +2957,23 @@ const Parser = struct {
}
fn expectFieldInit(p: *Parser) !Node.Index {
_ = try p.expectToken(.period);
_ = try p.expectToken(.identifier);
_ = try p.expectToken(.equal);
if (p.token_tags[p.tok_i] != .period or
p.token_tags[p.tok_i + 1] != .identifier or
p.token_tags[p.tok_i + 2] != .equal)
return p.fail(.expected_initializer);
p.tok_i += 3;
return p.expectExpr();
}
/// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
fn parseWhileContinueExpr(p: *Parser) !Node.Index {
_ = p.eatToken(.colon) orelse return null_node;
_ = p.eatToken(.colon) orelse {
if (p.token_tags[p.tok_i] == .l_paren and
p.tokensOnSameLine(p.tok_i - 1, p.tok_i))
return p.fail(.expected_continue_expr);
return null_node;
};
_ = try p.expectToken(.l_paren);
const node = try p.parseAssignExpr();
if (node == 0) return p.fail(.expected_expr_or_assignment);
@ -3413,7 +3482,7 @@ const Parser = struct {
// All possible delimiters.
.colon, .r_paren, .r_brace, .r_bracket => break,
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_switch_prong),
else => try p.warn(.expected_comma_after_switch_prong),
}
}
return p.listToSpan(p.scratch.items[scratch_top..]);
@ -3442,7 +3511,7 @@ const Parser = struct {
},
.colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_param),
else => try p.warn(.expected_comma_after_param),
}
}
if (varargs == .nonfinal) {
@ -3486,7 +3555,7 @@ const Parser = struct {
break;
},
// Likely just a missing comma; give error but continue parsing.
else => try p.warnExpectedAfter(.expected_comma_after_arg),
else => try p.warn(.expected_comma_after_arg),
}
}
const comma = (p.token_tags[p.tok_i - 2] == .comma);
@ -3530,57 +3599,6 @@ const Parser = struct {
}
}
// string literal or multiline string literal
fn parseStringLiteral(p: *Parser) !Node.Index {
switch (p.token_tags[p.tok_i]) {
.string_literal => {
const main_token = p.nextToken();
return p.addNode(.{
.tag = .string_literal,
.main_token = main_token,
.data = .{
.lhs = undefined,
.rhs = undefined,
},
});
},
.multiline_string_literal_line => {
const first_line = p.nextToken();
while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
p.tok_i += 1;
}
return p.addNode(.{
.tag = .multiline_string_literal,
.main_token = first_line,
.data = .{
.lhs = first_line,
.rhs = p.tok_i - 1,
},
});
},
else => return null_node,
}
}
fn expectStringLiteral(p: *Parser) !Node.Index {
const node = try p.parseStringLiteral();
if (node == 0) {
return p.fail(.expected_string_literal);
}
return node;
}
fn expectIntegerLiteral(p: *Parser) !Node.Index {
return p.addNode(.{
.tag = .integer_literal,
.main_token = try p.expectToken(.integer_literal),
.data = .{
.lhs = undefined,
.rhs = undefined,
},
});
}
/// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)?
fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index {
const if_token = p.eatToken(.keyword_if) orelse return null_node;
@ -3590,7 +3608,7 @@ const Parser = struct {
_ = try p.parsePtrPayload();
const then_expr = try bodyParseFn(p);
if (then_expr == 0) return p.fail(.invalid_token);
assert(then_expr != 0);
_ = p.eatToken(.keyword_else) orelse return p.addNode(.{
.tag = .if_simple,
@ -3602,7 +3620,7 @@ const Parser = struct {
});
_ = try p.parsePayload();
const else_expr = try bodyParseFn(p);
if (else_expr == 0) return p.fail(.invalid_token);
assert(then_expr != 0);
return p.addNode(.{
.tag = .@"if",
@ -3649,25 +3667,14 @@ const Parser = struct {
}
fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
const token = p.nextToken();
if (p.token_tags[token] != tag) {
p.tok_i -= 1; // Go back so that we can recover properly.
if (p.token_tags[p.tok_i] != tag) {
return p.failMsg(.{
.tag = .expected_token,
.token = token,
.token = p.tok_i,
.extra = .{ .expected_tag = tag },
});
}
return token;
}
fn expectTokenRecoverable(p: *Parser, tag: Token.Tag) !?TokenIndex {
if (p.token_tags[p.tok_i] != tag) {
try p.warnExpected(tag);
return null;
} else {
return p.nextToken();
}
return p.nextToken();
}
fn expectSemicolon(p: *Parser, error_tag: AstError.Tag, recoverable: bool) Error!void {
@ -3675,7 +3682,7 @@ const Parser = struct {
_ = p.nextToken();
return;
}
try p.warnExpectedAfter(error_tag);
try p.warn(error_tag);
if (!recoverable) return error.ParseError;
}

View File

@ -226,6 +226,8 @@ test "zig fmt: decl between fields" {
\\};
, &[_]Error{
.decl_between_fields,
.previous_field,
.next_field,
});
}
@ -5018,6 +5020,25 @@ test "zig fmt: make single-line if no trailing comma" {
);
}
test "zig fmt: while continue expr" {
try testCanonical(
\\test {
\\ while (i > 0)
\\ (i * 2);
\\}
\\
);
try testError(
\\test {
\\ while (i > 0) (i -= 1) {
\\ print("test123", .{});
\\ }
\\}
, &[_]Error{
.expected_continue_expr,
});
}
test "zig fmt: error for invalid bit range" {
try testError(
\\var x: []align(0:0:0)u8 = bar;
@ -5057,7 +5078,9 @@ test "recovery: block statements" {
\\ inline;
\\}
, &[_]Error{
.invalid_token,
.expected_expr,
.expected_semi_after_stmt,
.expected_statement,
.expected_inlinable,
});
}
@ -5076,7 +5099,7 @@ test "recovery: missing comma" {
, &[_]Error{
.expected_comma_after_switch_prong,
.expected_comma_after_switch_prong,
.invalid_token,
.expected_expr,
});
}

View File

@ -322,7 +322,18 @@ pub const Token = struct {
}
pub fn symbol(tag: Tag) []const u8 {
return tag.lexeme() orelse @tagName(tag);
return tag.lexeme() orelse switch (tag) {
.invalid => "invalid bytes",
.identifier => "an identifier",
.string_literal, .multiline_string_literal_line => "a string literal",
.char_literal => "a character literal",
.eof => "EOF",
.builtin => "a builtin function",
.integer_literal => "an integer literal",
.float_literal => "a floating point literal",
.doc_comment, .container_doc_comment => "a document comment",
else => unreachable,
};
}
};
};

View File

@ -2995,7 +2995,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
const token_starts = file.tree.tokens.items(.start);
const token_tags = file.tree.tokens.items(.tag);
const extra_offset = file.tree.errorOffset(parse_err.tag, parse_err.token);
const extra_offset = file.tree.errorOffset(parse_err);
try file.tree.renderError(parse_err, msg.writer());
const err_msg = try gpa.create(ErrorMsg);
err_msg.* = .{
@ -3006,14 +3006,25 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
},
.msg = msg.toOwnedSlice(),
};
if (token_tags[parse_err.token] == .invalid) {
const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token).len);
const byte_abs = token_starts[parse_err.token] + bad_off;
if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) {
const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len);
const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off;
try mod.errNoteNonLazy(.{
.file_scope = file,
.parent_decl_node = 0,
.lazy = .{ .byte_abs = byte_abs },
}, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])});
} else if (parse_err.tag == .decl_between_fields) {
try mod.errNoteNonLazy(.{
.file_scope = file,
.parent_decl_node = 0,
.lazy = .{ .byte_abs = token_starts[file.tree.errors[1].token] },
}, err_msg, "field before declarations here", .{});
try mod.errNoteNonLazy(.{
.file_scope = file,
.parent_decl_node = 0,
.lazy = .{ .byte_abs = token_starts[file.tree.errors[2].token] },
}, err_msg, "field after declarations here", .{});
}
{

View File

@ -3799,9 +3799,7 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
};
defer tree.deinit(gpa);
for (tree.errors) |parse_error| {
try printErrMsgToStdErr(gpa, arena, parse_error, tree, "<stdin>", color);
}
try printErrsMsgToStdErr(gpa, arena, tree.errors, tree, "<stdin>", color);
var has_ast_error = false;
if (check_ast_flag) {
const Module = @import("Module.zig");
@ -3989,9 +3987,7 @@ fn fmtPathFile(
var tree = try std.zig.parse(fmt.gpa, source_code);
defer tree.deinit(fmt.gpa);
for (tree.errors) |parse_error| {
try printErrMsgToStdErr(fmt.gpa, fmt.arena, parse_error, tree, file_path, fmt.color);
}
try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree.errors, tree, file_path, fmt.color);
if (tree.errors.len != 0) {
fmt.any_error = true;
return;
@ -4071,66 +4067,95 @@ fn fmtPathFile(
}
}
fn printErrMsgToStdErr(
fn printErrsMsgToStdErr(
gpa: mem.Allocator,
arena: mem.Allocator,
parse_error: Ast.Error,
parse_errors: []const Ast.Error,
tree: Ast,
path: []const u8,
color: Color,
) !void {
const lok_token = parse_error.token;
const token_tags = tree.tokens.items(.tag);
const start_loc = tree.tokenLocation(0, lok_token);
const source_line = tree.source[start_loc.line_start..start_loc.line_end];
var i: usize = 0;
while (i < parse_errors.len) : (i += 1) {
const parse_error = parse_errors[i];
const lok_token = parse_error.token;
const token_tags = tree.tokens.items(.tag);
const start_loc = tree.tokenLocation(0, lok_token);
const source_line = tree.source[start_loc.line_start..start_loc.line_end];
var text_buf = std.ArrayList(u8).init(gpa);
defer text_buf.deinit();
const writer = text_buf.writer();
try tree.renderError(parse_error, writer);
const text = text_buf.items;
var text_buf = std.ArrayList(u8).init(gpa);
defer text_buf.deinit();
const writer = text_buf.writer();
try tree.renderError(parse_error, writer);
const text = text_buf.items;
var notes_buffer: [1]Compilation.AllErrors.Message = undefined;
var notes_len: usize = 0;
var notes_buffer: [2]Compilation.AllErrors.Message = undefined;
var notes_len: usize = 0;
if (token_tags[parse_error.token] == .invalid) {
const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token).len);
const byte_offset = @intCast(u32, start_loc.line_start) + bad_off;
notes_buffer[notes_len] = .{
if (token_tags[parse_error.token + @boolToInt(parse_error.token_is_prev)] == .invalid) {
const bad_off = @intCast(u32, tree.tokenSlice(parse_error.token + @boolToInt(parse_error.token_is_prev)).len);
const byte_offset = @intCast(u32, start_loc.line_start) + @intCast(u32, start_loc.column) + bad_off;
notes_buffer[notes_len] = .{
.src = .{
.src_path = path,
.msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{
std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),
}),
.byte_offset = byte_offset,
.line = @intCast(u32, start_loc.line),
.column = @intCast(u32, start_loc.column) + bad_off,
.source_line = source_line,
},
};
notes_len += 1;
} else if (parse_error.tag == .decl_between_fields) {
const prev_loc = tree.tokenLocation(0, parse_errors[i + 1].token);
notes_buffer[0] = .{
.src = .{
.src_path = path,
.msg = "field before declarations here",
.byte_offset = @intCast(u32, prev_loc.line_start),
.line = @intCast(u32, prev_loc.line),
.column = @intCast(u32, prev_loc.column),
.source_line = tree.source[prev_loc.line_start..prev_loc.line_end],
},
};
const next_loc = tree.tokenLocation(0, parse_errors[i + 2].token);
notes_buffer[1] = .{
.src = .{
.src_path = path,
.msg = "field after declarations here",
.byte_offset = @intCast(u32, next_loc.line_start),
.line = @intCast(u32, next_loc.line),
.column = @intCast(u32, next_loc.column),
.source_line = tree.source[next_loc.line_start..next_loc.line_end],
},
};
notes_len = 2;
i += 2;
}
const extra_offset = tree.errorOffset(parse_error);
const message: Compilation.AllErrors.Message = .{
.src = .{
.src_path = path,
.msg = try std.fmt.allocPrint(arena, "invalid byte: '{'}'", .{
std.zig.fmtEscapes(tree.source[byte_offset..][0..1]),
}),
.byte_offset = byte_offset,
.msg = text,
.byte_offset = @intCast(u32, start_loc.line_start) + extra_offset,
.line = @intCast(u32, start_loc.line),
.column = @intCast(u32, start_loc.column) + bad_off,
.column = @intCast(u32, start_loc.column) + extra_offset,
.source_line = source_line,
.notes = notes_buffer[0..notes_len],
},
};
notes_len += 1;
const ttyconf: std.debug.TTY.Config = switch (color) {
.auto => std.debug.detectTTYConfig(),
.on => .escape_codes,
.off => .no_color,
};
message.renderToStdErr(ttyconf);
}
const extra_offset = tree.errorOffset(parse_error.tag, parse_error.token);
const message: Compilation.AllErrors.Message = .{
.src = .{
.src_path = path,
.msg = text,
.byte_offset = @intCast(u32, start_loc.line_start) + extra_offset,
.line = @intCast(u32, start_loc.line),
.column = @intCast(u32, start_loc.column) + extra_offset,
.source_line = source_line,
.notes = notes_buffer[0..notes_len],
},
};
const ttyconf: std.debug.TTY.Config = switch (color) {
.auto => std.debug.detectTTYConfig(),
.on => .escape_codes,
.off => .no_color,
};
message.renderToStdErr(ttyconf);
}
pub const info_zen =
@ -4688,9 +4713,7 @@ pub fn cmdAstCheck(
file.tree_loaded = true;
defer file.tree.deinit(gpa);
for (file.tree.errors) |parse_error| {
try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, file.sub_file_path, color);
}
try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, file.sub_file_path, color);
if (file.tree.errors.len != 0) {
process.exit(1);
}
@ -4816,9 +4839,7 @@ pub fn cmdChangelist(
file.tree_loaded = true;
defer file.tree.deinit(gpa);
for (file.tree.errors) |parse_error| {
try printErrMsgToStdErr(gpa, arena, parse_error, file.tree, old_source_file, .auto);
}
try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, old_source_file, .auto);
if (file.tree.errors.len != 0) {
process.exit(1);
}
@ -4855,9 +4876,7 @@ pub fn cmdChangelist(
var new_tree = try std.zig.parse(gpa, new_source);
defer new_tree.deinit(gpa);
for (new_tree.errors) |parse_error| {
try printErrMsgToStdErr(gpa, arena, parse_error, new_tree, new_source_file, .auto);
}
try printErrsMsgToStdErr(gpa, arena, new_tree.errors, new_tree, new_source_file, .auto);
if (new_tree.errors.len != 0) {
process.exit(1);
}

View File

@ -7843,7 +7843,7 @@ static Stage1AirInst *ir_analyze_cast(IrAnalyze *ira, Scope *scope, AstNode *sou
bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
|| !actual_type->data.pointer.is_const);
if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
array_type->data.array.child_type, source_node,
!slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk &&
(slice_ptr_type->data.pointer.sentinel == nullptr ||
@ -7851,6 +7851,14 @@ static Stage1AirInst *ir_analyze_cast(IrAnalyze *ira, Scope *scope, AstNode *sou
const_values_equal(ira->codegen, array_type->data.array.sentinel,
slice_ptr_type->data.pointer.sentinel))))
{
if (!const_ok) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("cannot cast pointer to array literal to slice type '%s'",
buf_ptr(&wanted_type->name)));
add_error_note(ira->codegen, msg, source_node,
buf_sprintf("cast discards const qualifier"));
return ira->codegen->invalid_inst_gen;
}
// If the pointers both have ABI align, it works.
// Or if the array length is 0, alignment doesn't matter.
bool ok_align = array_type->data.array.len == 0 ||
@ -8208,8 +8216,16 @@ static Stage1AirInst *ir_analyze_cast(IrAnalyze *ira, Scope *scope, AstNode *sou
ZigType *wanted_child = wanted_type->data.pointer.child_type;
bool const_ok = (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const);
if (wanted_child->id == ZigTypeIdArray && (is_array_init || field_count == 0) &&
wanted_child->data.array.len == field_count && (const_ok || field_count == 0))
wanted_child->data.array.len == field_count)
{
if (!const_ok && field_count != 0) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("cannot cast pointer to array literal to '%s'",
buf_ptr(&wanted_type->name)));
add_error_note(ira->codegen, msg, source_node,
buf_sprintf("cast discards const qualifier"));
return ira->codegen->invalid_inst_gen;
}
Stage1AirInst *res = ir_analyze_struct_literal_to_array(ira, scope, source_node, value, anon_type, wanted_child);
if (res->value->type->id == ZigTypeIdPointer)
return res;
@ -8241,6 +8257,13 @@ static Stage1AirInst *ir_analyze_cast(IrAnalyze *ira, Scope *scope, AstNode *sou
res = ir_get_ref(ira, scope, source_node, res, actual_type->data.pointer.is_const, actual_type->data.pointer.is_volatile);
return ir_resolve_ptr_of_array_to_slice(ira, scope, source_node, res, wanted_type, nullptr);
} else if (!slice_type->data.pointer.is_const && actual_type->data.pointer.is_const && field_count != 0) {
ErrorMsg *msg = ir_add_error_node(ira, source_node,
buf_sprintf("cannot cast pointer to array literal to slice type '%s'",
buf_ptr(&wanted_type->name)));
add_error_note(ira->codegen, msg, source_node,
buf_sprintf("cast discards const qualifier"));
return ira->codegen->invalid_inst_gen;
}
}
}
@ -15068,7 +15091,7 @@ static Stage1AirInst *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, Stage1ZirI
return ira->codegen->invalid_inst_gen;
if (actual_array_type->id != ZigTypeIdArray) {
ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node,
buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'",
buf_sprintf("array literal requires address-of operator (&) to coerce to slice type '%s'",
buf_ptr(&actual_array_type->name)));
return ira->codegen->invalid_inst_gen;
}
@ -17473,7 +17496,7 @@ static Stage1AirInst *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
if (is_slice(container_type)) {
ir_add_error_node(ira, instruction->init_array_type_source_node,
buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'",
buf_sprintf("array literal requires address-of operator (&) to coerce to slice type '%s'",
buf_ptr(&container_type->name)));
return ira->codegen->invalid_inst_gen;
}

View File

@ -86,9 +86,12 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = c;
\\}
, &[_][]const u8{
"tmp.zig:2:31: error: expected type '[][]const u8', found '*const struct:2:31'",
"tmp.zig:6:33: error: expected type '*[2][]const u8', found '*const struct:6:33'",
"tmp.zig:2:31: error: cannot cast pointer to array literal to slice type '[][]const u8'",
"tmp.zig:2:31: note: cast discards const qualifier",
"tmp.zig:6:33: error: cannot cast pointer to array literal to '*[2][]const u8'",
"tmp.zig:6:33: note: cast discards const qualifier",
"tmp.zig:11:21: error: expected type '*S', found '*const struct:11:21'",
"tmp.zig:11:21: note: cast discards const qualifier",
});
ctx.objErrStage1("@Type() union payload is undefined",
@ -874,6 +877,8 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, &[_][]const u8{
"tmp.zig:6:5: error: declarations are not allowed between container fields",
"tmp.zig:5:5: note: field before declarations here",
"tmp.zig:9:5: note: field after declarations here",
});
ctx.objErrStage1("non-extern function with var args",
@ -1540,7 +1545,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ std.debug.assert(bad_float < 1.0);
\\}
, &[_][]const u8{
"tmp.zig:5:29: error: invalid token: '.'",
"tmp.zig:5:29: error: expected expression, found '.'",
});
ctx.objErrStage1("invalid exponent in float literal - 1",
@ -1549,7 +1554,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: 'a'",
});
@ -1559,7 +1564,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:29: note: invalid byte: 'F'",
});
@ -1569,7 +1574,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:23: note: invalid byte: '_'",
});
@ -1579,7 +1584,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:23: note: invalid byte: '.'",
});
@ -1589,7 +1594,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:25: note: invalid byte: ';'",
});
@ -1599,7 +1604,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:25: note: invalid byte: '_'",
});
@ -1609,7 +1614,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:26: note: invalid byte: '_'",
});
@ -1619,7 +1624,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:26: note: invalid byte: '_'",
});
@ -1629,7 +1634,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: ';'",
});
@ -1639,7 +1644,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:23: note: invalid byte: '_'",
});
@ -1649,7 +1654,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:25: note: invalid byte: '_'",
});
@ -1659,7 +1664,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: '_'",
});
@ -1669,7 +1674,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:23: note: invalid byte: 'x'",
});
@ -1679,7 +1684,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:23: note: invalid byte: '_'",
});
@ -1689,7 +1694,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:27: note: invalid byte: 'p'",
});
@ -1699,7 +1704,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:26: note: invalid byte: ';'",
});
@ -1709,7 +1714,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: ';'",
});
@ -1719,7 +1724,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: ';'",
});
@ -1729,7 +1734,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = bad;
\\}
, &[_][]const u8{
"tmp.zig:2:21: error: expected expression, found 'invalid'",
"tmp.zig:2:21: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:28: note: invalid byte: ';'",
});
@ -1962,7 +1967,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = geo_data;
\\}
, &[_][]const u8{
"tmp.zig:4:30: error: array literal requires address-of operator to coerce to slice type '[][2]f32'",
"tmp.zig:4:30: error: array literal requires address-of operator (&) to coerce to slice type '[][2]f32'",
});
ctx.objErrStage1("slicing of global undefined pointer",
@ -2171,7 +2176,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = x;
\\}
, &[_][]const u8{
"tmp.zig:3:6: error: expected ',' after field",
"tmp.zig:3:7: error: expected ',' after field",
});
ctx.objErrStage1("bad alignment type",
@ -2537,7 +2542,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = x;
\\}
, &[_][]const u8{
"tmp.zig:2:15: error: array literal requires address-of operator to coerce to slice type '[]u8'",
"tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'",
});
ctx.objErrStage1("slice passed as array init type",
@ -2546,7 +2551,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = x;
\\}
, &[_][]const u8{
"tmp.zig:2:15: error: array literal requires address-of operator to coerce to slice type '[]u8'",
"tmp.zig:2:15: error: array literal requires address-of operator (&) to coerce to slice type '[]u8'",
});
ctx.objErrStage1("inferred array size invalid here",
@ -3493,7 +3498,8 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = sliceA;
\\}
, &[_][]const u8{
"tmp.zig:3:27: error: expected type '[]u8', found '*const [1]u8'",
"tmp.zig:3:27: error: cannot cast pointer to array literal to slice type '[]u8'",
"tmp.zig:3:27: note: cast discards const qualifier",
});
ctx.objErrStage1("deref slice and get len field",
@ -4865,11 +4871,11 @@ pub fn addCases(ctx: *TestContext) !void {
\\export fn entry() void {
\\ while(true) {}
\\ var good = {};
\\ while(true) ({})
\\ while(true) 1
\\ var bad = {};
\\}
, &[_][]const u8{
"tmp.zig:4:21: error: expected ';' or 'else' after statement",
"tmp.zig:4:18: error: expected ';' or 'else' after statement",
});
ctx.objErrStage1("implicit semicolon - while expression",
@ -5733,7 +5739,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\const foo = "a
\\b";
, &[_][]const u8{
"tmp.zig:1:13: error: expected expression, found 'invalid'",
"tmp.zig:1:13: error: expected expression, found 'invalid bytes'",
"tmp.zig:1:15: note: invalid byte: '\\n'",
});
@ -7638,7 +7644,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ const a = '\U1234';
\\}
, &[_][]const u8{
"tmp.zig:2:15: error: expected expression, found 'invalid'",
"tmp.zig:2:15: error: expected expression, found 'invalid bytes'",
"tmp.zig:2:18: note: invalid byte: '1'",
});
@ -7654,7 +7660,7 @@ pub fn addCases(ctx: *TestContext) !void {
"fn foo() bool {\r\n" ++
" return true;\r\n" ++
"}\r\n", &[_][]const u8{
"tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid'",
"tmp.zig:1:1: error: expected test, comptime, var decl, or container field, found 'invalid bytes'",
"tmp.zig:1:1: note: invalid byte: '\\xff'",
});
@ -8717,7 +8723,8 @@ pub fn addCases(ctx: *TestContext) !void {
\\ comptime ignore(@typeInfo(MyStruct).Struct.fields[0]);
\\}
, &[_][]const u8{
":5:28: error: expected type '[]u8', found '*const [3:0]u8'",
":5:28: error: cannot cast pointer to array literal to slice type '[]u8'",
":5:28: note: cast discards const qualifier",
});
ctx.objErrStage1("integer underflow error",

View File

@ -693,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ _ = E1.a;
\\}
, &.{
":3:6: error: expected ',' after field",
":3:7: error: expected ',' after field",
});
// Redundant non-exhaustive enum mark.