mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 13:58:27 +00:00
stage2: add decltests
This commit is contained in:
parent
0b7347fd18
commit
3bbe6a28e0
@ -2519,7 +2519,7 @@ pub const Node = struct {
|
||||
root,
|
||||
/// `usingnamespace lhs;`. rhs unused. main_token is `usingnamespace`.
|
||||
@"usingnamespace",
|
||||
/// lhs is test name token (must be string literal), if any.
|
||||
/// lhs is test name token (must be string literal or identifier), if any.
|
||||
/// rhs is the body node.
|
||||
test_decl,
|
||||
/// lhs is the index into extra_data.
|
||||
|
||||
@ -500,10 +500,16 @@ const Parser = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block
|
||||
/// TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
|
||||
fn expectTestDecl(p: *Parser) !Node.Index {
|
||||
const test_token = p.assertToken(.keyword_test);
|
||||
const name_token = p.eatToken(.string_literal);
|
||||
const name_token = switch (p.token_tags[p.nextToken()]) {
|
||||
.string_literal, .identifier => p.tok_i - 1,
|
||||
else => blk: {
|
||||
p.tok_i -= 1;
|
||||
break :blk null;
|
||||
},
|
||||
};
|
||||
const block_node = try p.parseBlock();
|
||||
if (block_node == 0) return p.fail(.expected_block);
|
||||
return p.addNode(.{
|
||||
|
||||
@ -151,7 +151,8 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac
|
||||
.test_decl => {
|
||||
const test_token = main_tokens[decl];
|
||||
try renderToken(ais, tree, test_token, .space);
|
||||
if (token_tags[test_token + 1] == .string_literal) {
|
||||
const test_name_tag = token_tags[test_token + 1];
|
||||
if (test_name_tag == .string_literal or test_name_tag == .identifier) {
|
||||
try renderToken(ais, tree, test_token + 1, .space);
|
||||
}
|
||||
try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
|
||||
|
||||
@ -105,8 +105,8 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
|
||||
};
|
||||
defer astgen.deinit(gpa);
|
||||
|
||||
// String table indexes 0 and 1 are reserved for special meaning.
|
||||
try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0 });
|
||||
// String table indexes 0, 1, 2 are reserved for special meaning.
|
||||
try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 });
|
||||
|
||||
// We expect at least as many ZIR instructions and extra data items
|
||||
// as AST nodes.
|
||||
@ -3736,13 +3736,78 @@ fn testDecl(
|
||||
};
|
||||
defer decl_block.unstack();
|
||||
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
const test_token = main_tokens[node];
|
||||
const test_name_token = test_token + 1;
|
||||
const test_name_token_tag = token_tags[test_name_token];
|
||||
const is_decltest = test_name_token_tag == .identifier;
|
||||
const test_name: u32 = blk: {
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
const test_token = main_tokens[node];
|
||||
const str_lit_token = test_token + 1;
|
||||
if (token_tags[str_lit_token] == .string_literal) {
|
||||
break :blk try astgen.testNameString(str_lit_token);
|
||||
if (test_name_token_tag == .string_literal) {
|
||||
break :blk try astgen.testNameString(test_name_token);
|
||||
} else if (test_name_token_tag == .identifier) {
|
||||
const ident_name_raw = tree.tokenSlice(test_name_token);
|
||||
|
||||
if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
|
||||
|
||||
// if not @"" syntax, just use raw token slice
|
||||
if (ident_name_raw[0] != '@') {
|
||||
if (primitives.get(ident_name_raw)) |_| return astgen.failTok(test_name_token, "cannot test a primitive", .{});
|
||||
|
||||
if (ident_name_raw.len >= 2) integer: {
|
||||
const first_c = ident_name_raw[0];
|
||||
if (first_c == 'i' or first_c == 'u') {
|
||||
_ = switch (first_c == 'i') {
|
||||
true => .signed,
|
||||
false => .unsigned,
|
||||
};
|
||||
_ = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
|
||||
error.Overflow => return astgen.failTok(
|
||||
test_name_token,
|
||||
"primitive integer type '{s}' exceeds maximum bit width of 65535",
|
||||
.{ident_name_raw},
|
||||
),
|
||||
error.InvalidCharacter => break :integer,
|
||||
};
|
||||
return astgen.failTok(test_name_token, "cannot test a primitive", .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Local variables, including function parameters.
|
||||
const name_str_index = try astgen.identAsString(test_name_token);
|
||||
var s = scope;
|
||||
var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
|
||||
var num_namespaces_out: u32 = 0;
|
||||
var capturing_namespace: ?*Scope.Namespace = null;
|
||||
while (true) switch (s.tag) {
|
||||
.local_val, .local_ptr => unreachable, // a test cannot be in a local scope
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(name_str_index)) |i| {
|
||||
if (found_already) |f| {
|
||||
return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
|
||||
try astgen.errNoteNode(f, "declared here", .{}),
|
||||
try astgen.errNoteNode(i, "also declared here", .{}),
|
||||
});
|
||||
}
|
||||
// We found a match but must continue looking for ambiguous references to decls.
|
||||
found_already = i;
|
||||
}
|
||||
num_namespaces_out += 1;
|
||||
capturing_namespace = ns;
|
||||
s = ns.parent;
|
||||
},
|
||||
.top => break,
|
||||
};
|
||||
if (found_already == null) {
|
||||
const ident_name = try astgen.identifierTokenString(test_name_token);
|
||||
return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
|
||||
}
|
||||
|
||||
break :blk name_str_index;
|
||||
}
|
||||
// String table index 1 has a special meaning here of test decl with no name.
|
||||
break :blk 1;
|
||||
@ -3804,9 +3869,15 @@ fn testDecl(
|
||||
const line_delta = decl_block.decl_line - gz.decl_line;
|
||||
wip_members.appendToDecl(line_delta);
|
||||
}
|
||||
wip_members.appendToDecl(test_name);
|
||||
if (is_decltest)
|
||||
wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
|
||||
else
|
||||
wip_members.appendToDecl(test_name);
|
||||
wip_members.appendToDecl(block_inst);
|
||||
wip_members.appendToDecl(0); // no doc comments on test decls
|
||||
if (is_decltest)
|
||||
wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name
|
||||
else
|
||||
wip_members.appendToDecl(0); // no doc comments on test decls
|
||||
}
|
||||
|
||||
fn structDeclInner(
|
||||
|
||||
@ -4170,6 +4170,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
const line_off = zir.extra[decl_sub_index + 4];
|
||||
const line = iter.parent_decl.relativeToLine(line_off);
|
||||
const decl_name_index = zir.extra[decl_sub_index + 5];
|
||||
const decl_doccomment_index = zir.extra[decl_sub_index + 7];
|
||||
const decl_index = zir.extra[decl_sub_index + 6];
|
||||
const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node;
|
||||
const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node);
|
||||
@ -4193,6 +4194,11 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
iter.unnamed_test_index += 1;
|
||||
break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i});
|
||||
},
|
||||
2 => name: {
|
||||
is_named_test = true;
|
||||
const test_name = zir.nullTerminatedString(decl_doccomment_index);
|
||||
break :name try std.fmt.allocPrintZ(gpa, "decltest.{s}", .{test_name});
|
||||
},
|
||||
else => name: {
|
||||
const raw_name = zir.nullTerminatedString(decl_name_index);
|
||||
if (raw_name.len == 0) {
|
||||
|
||||
@ -2579,10 +2579,11 @@ pub const Inst = struct {
|
||||
/// - 0 means comptime or usingnamespace decl.
|
||||
/// - if name == 0 `is_exported` determines which one: 0=comptime,1=usingnamespace
|
||||
/// - 1 means test decl with no name.
|
||||
/// - 2 means that the test is a decltest, doc_comment gives the name of the identifier
|
||||
/// - if there is a 0 byte at the position `name` indexes, it indicates
|
||||
/// this is a test decl, and the name starts at `name+1`.
|
||||
/// value: Index,
|
||||
/// doc_comment: u32, // 0 if no doc comment
|
||||
/// doc_comment: u32, 0 if no doc comment, if this is a decltest, doc_comment references the decl name in the string table
|
||||
/// align: Ref, // if corresponding bit is set
|
||||
/// link_section_or_address_space: { // if corresponding bit is set.
|
||||
/// link_section: Ref,
|
||||
|
||||
@ -1443,20 +1443,24 @@ const Writer = struct {
|
||||
} else if (decl_name_index == 1) {
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.writeAll("test");
|
||||
} else if (decl_name_index == 2) {
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.print("[{d}] decltest {s}", .{ sub_index, self.code.nullTerminatedString(doc_comment_index) });
|
||||
} else {
|
||||
const raw_decl_name = self.code.nullTerminatedString(decl_name_index);
|
||||
const decl_name = if (raw_decl_name.len == 0)
|
||||
self.code.nullTerminatedString(decl_name_index + 1)
|
||||
else
|
||||
raw_decl_name;
|
||||
const test_str = if (raw_decl_name.len == 0) "test " else "";
|
||||
const test_str = if (raw_decl_name.len == 0) "test \"" else "";
|
||||
const export_str = if (is_exported) "export " else "";
|
||||
|
||||
try self.writeDocComment(stream, doc_comment_index);
|
||||
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.print("[{d}] {s}{s}{s}{}", .{
|
||||
sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name),
|
||||
const endquote_if_test: []const u8 = if (raw_decl_name.len == 0) "\"" else "";
|
||||
try stream.print("[{d}] {s}{s}{s}{}{s}", .{
|
||||
sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name), endquote_if_test,
|
||||
});
|
||||
if (align_inst != .none) {
|
||||
try stream.writeAll(" align(");
|
||||
|
||||
@ -49,6 +49,11 @@ test {
|
||||
_ = @import("behavior/type.zig");
|
||||
_ = @import("behavior/var_args.zig");
|
||||
|
||||
// tests that don't pass for stage1
|
||||
if (builtin.zig_backend != .stage1) {
|
||||
_ = @import("behavior/decltest.zig");
|
||||
}
|
||||
|
||||
if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) {
|
||||
// Tests that pass (partly) for stage1, llvm backend, C backend, wasm backend.
|
||||
_ = @import("behavior/bitcast.zig");
|
||||
|
||||
7
test/behavior/decltest.zig
Normal file
7
test/behavior/decltest.zig
Normal file
@ -0,0 +1,7 @@
|
||||
pub fn the_add_function(a: u32, b: u32) u32 {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
test the_add_function {
|
||||
if (the_add_function(1, 2) != 3) unreachable;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user