mirror of
https://github.com/ziglang/zig.git
synced 2026-01-05 21:13:24 +00:00
Merge pull request #10913 from Vexu/err
further parser error improvements
This commit is contained in:
commit
53241f288e
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -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", .{});
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
137
src/main.zig
137
src/main.zig
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user