Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/std/zig/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
.unattached_doc_comment => {
return stream.writeAll("unattached documentation comment");
},
.test_doc_comment => {
return stream.writeAll("documentation comments cannot be attached to tests");
},
.comptime_doc_comment => {
return stream.writeAll("documentation comments cannot be attached to comptime blocks");
},
.varargs_nonfinal => {
return stream.writeAll("function prototype has parameter after varargs");
},
Expand Down Expand Up @@ -2539,6 +2545,8 @@ pub const Error = struct {
invalid_bit_range,
same_line_doc_comment,
unattached_doc_comment,
test_doc_comment,
comptime_doc_comment,
varargs_nonfinal,
expected_continue_expr,
expected_semi_after_decl,
Expand Down
6 changes: 6 additions & 0 deletions lib/std/zig/parse.zig
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ const Parser = struct {

switch (p.token_tags[p.tok_i]) {
.keyword_test => {
if (doc_comment) |some| {
try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
}
const test_decl_node = try p.expectTestDeclRecoverable();
if (test_decl_node != 0) {
if (field_state == .seen) {
Expand Down Expand Up @@ -317,6 +320,9 @@ const Parser = struct {
}
},
.l_brace => {
if (doc_comment) |some| {
try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
}
const comptime_token = p.nextToken();
const block = p.parseBlock() catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
Expand Down
12 changes: 0 additions & 12 deletions lib/std/zig/parser_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,6 @@ test "zig fmt: file ends in comment after var decl" {
);
}

test "zig fmt: doc comments on test" {
try testCanonical(
\\/// hello
\\/// world
\\test "" {}
\\
);
}

test "zig fmt: if statment" {
try testCanonical(
\\test "" {
Expand Down Expand Up @@ -2700,9 +2691,6 @@ test "zig fmt: comments in statements" {

test "zig fmt: comments before test decl" {
try testCanonical(
\\/// top level doc comment
\\test "hi" {}
\\
\\// top level normal comment
\\test "hi" {}
\\
Expand Down
2 changes: 1 addition & 1 deletion lib/std/zig/system/darwin.zig
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ pub const DarwinSDK = struct {
}
};

test "" {
test {
_ = macos;
}
38 changes: 37 additions & 1 deletion src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3497,6 +3497,12 @@ fn fnDecl(

const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
const lib_name_str = try astgen.strLitAsString(lib_name_token);
const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
} else if (lib_name_str.len == 0) {
return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
}
break :blk lib_name_str.index;
} else 0;

Expand Down Expand Up @@ -3750,6 +3756,12 @@ fn globalVarDecl(

const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
const lib_name_str = try astgen.strLitAsString(lib_name_token);
const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
} else if (lib_name_str.len == 0) {
return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
}
break :blk lib_name_str.index;
} else 0;

Expand Down Expand Up @@ -7239,6 +7251,12 @@ fn builtinCall(
}
const str_lit_token = main_tokens[operand_node];
const str = try astgen.strLitAsString(str_lit_token);
const str_slice = astgen.string_bytes.items[str.index..][0..str.len];
if (mem.indexOfScalar(u8, str_slice, 0) != null) {
return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
} else if (str.len == 0) {
return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
}
const result = try gz.addStrTok(.import, str.index, str_lit_token);
const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
if (!gop.found_existing) {
Expand Down Expand Up @@ -9260,6 +9278,11 @@ fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]co
var buf: ArrayListUnmanaged(u8) = .{};
defer buf.deinit(astgen.gpa);
try astgen.parseStrLit(token, &buf, ident_name, 1);
if (mem.indexOfScalar(u8, buf.items, 0) != null) {
return astgen.failTok(token, "identifier cannot contain null bytes", .{});
} else if (buf.items.len == 0) {
return astgen.failTok(token, "identifier cannot be empty", .{});
}
const duped = try astgen.arena.dupe(u8, buf.items);
return duped;
}
Expand All @@ -9279,7 +9302,14 @@ fn appendIdentStr(
if (!mem.startsWith(u8, ident_name, "@")) {
return buf.appendSlice(astgen.gpa, ident_name);
} else {
return astgen.parseStrLit(token, buf, ident_name, 1);
const start = buf.items.len;
try astgen.parseStrLit(token, buf, ident_name, 1);
const slice = buf.items[start..];
if (mem.indexOfScalar(u8, slice, 0) != null) {
return astgen.failTok(token, "identifier cannot contain null bytes", .{});
} else if (slice.len == 0) {
return astgen.failTok(token, "identifier cannot be empty", .{});
}
}
}

Expand Down Expand Up @@ -9723,6 +9753,12 @@ fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !u32 {
const token_bytes = astgen.tree.tokenSlice(str_lit_token);
try string_bytes.append(gpa, 0); // Indicates this is a test.
try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
const slice = string_bytes.items[str_index + 1 ..];
if (mem.indexOfScalar(u8, slice, 0) != null) {
return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
} else if (slice.len == 0) {
return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
}
try string_bytes.append(gpa, 0);
return str_index;
}
Expand Down
60 changes: 57 additions & 3 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5939,10 +5939,11 @@ pub fn argSrc(
call_node_offset: i32,
gpa: Allocator,
decl: *Decl,
arg_i: usize,
start_arg_i: usize,
bound_arg_src: ?LazySrcLoc,
) LazySrcLoc {
if (arg_i == 0 and bound_arg_src != null) return bound_arg_src.?;
if (start_arg_i == 0 and bound_arg_src != null) return bound_arg_src.?;
const arg_i = start_arg_i - @boolToInt(bound_arg_src != null);
@setCold(true);
const tree = decl.getFileScope().getTree(gpa) catch |err| {
// In this case we emit a warning + a less precise source location.
Expand All @@ -5957,6 +5958,12 @@ pub fn argSrc(
const full = switch (node_tags[node]) {
.call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(&args, node),
.call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
.builtin_call => {
const node_datas = tree.nodes.items(.data);
const call_args_node = tree.extra_data[node_datas[node].rhs - 1];
const call_args_offset = decl.nodeIndexToRelative(call_args_node);
return initSrc(call_args_offset, gpa, decl, arg_i);
},
else => unreachable,
};
return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full.ast.params[arg_i]));
Expand Down Expand Up @@ -5989,7 +5996,7 @@ pub fn initSrc(
.struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node).ast.fields,
.struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node).ast.fields,
.struct_init, .struct_init_comma => tree.structInit(node).ast.fields,
else => unreachable,
else => return LazySrcLoc.nodeOffset(init_node_offset),
};
switch (node_tags[node]) {
.array_init_one,
Expand All @@ -6014,6 +6021,53 @@ pub fn initSrc(
}
}

pub fn optionsSrc(gpa: Allocator, decl: *Decl, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
@setCold(true);
const tree = decl.getFileScope().getTree(gpa) catch |err| {
// In this case we emit a warning + a less precise source location.
log.warn("unable to load {s}: {s}", .{
decl.getFileScope().sub_file_path, @errorName(err),
});
return LazySrcLoc.nodeOffset(0);
};

const o_i: struct { off: i32, i: u8 } = switch (base_src) {
.node_offset_builtin_call_arg0 => |n| .{ .off = n, .i = 0 },
.node_offset_builtin_call_arg1 => |n| .{ .off = n, .i = 1 },
else => unreachable,
};

const node = decl.relativeToNodeIndex(o_i.off);
const node_datas = tree.nodes.items(.data);
const node_tags = tree.nodes.items(.tag);
const arg_node = switch (node_tags[node]) {
.builtin_call_two, .builtin_call_two_comma => switch (o_i.i) {
0 => node_datas[node].lhs,
1 => node_datas[node].rhs,
else => unreachable,
},
.builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + o_i.i],
else => unreachable,
};
var buf: [2]std.zig.Ast.Node.Index = undefined;
const init_nodes = switch (node_tags[arg_node]) {
.struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], arg_node).ast.fields,
.struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, arg_node).ast.fields,
.struct_init_dot, .struct_init_dot_comma => tree.structInitDot(arg_node).ast.fields,
.struct_init, .struct_init_comma => tree.structInit(arg_node).ast.fields,
else => return base_src,
};
for (init_nodes) |init_node| {
// . IDENTIFIER = init_node
const name_token = tree.firstToken(init_node) - 2;
const name = tree.tokenSlice(name_token);
if (std.mem.eql(u8, name, wanted)) {
return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(init_node) };
}
}
return base_src;
}

/// Called from `performAllTheWork`, after all AstGen workers have finished,
/// and before the main semantic analysis loop begins.
pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
Expand Down
Loading