Commit ff0a15bb7a
Changed files (3)
lib
lib/std/zig/ast.zig
@@ -297,6 +297,9 @@ pub const Tree = struct {
.unattached_doc_comment => {
return stream.writeAll("unattached documentation comment");
},
+ .varargs_nonfinal => {
+ return stream.writeAll("function prototype has parameter after varargs");
+ },
.expected_token => {
const found_tag = token_tags[parse_error.token];
@@ -2414,6 +2417,7 @@ pub const Error = struct {
invalid_token,
same_line_doc_comment,
unattached_doc_comment,
+ varargs_nonfinal,
/// `expected_tag` is populated.
expected_token,
lib/std/zig/parse.zig
@@ -3553,11 +3553,15 @@ const Parser = struct {
_ = try p.expectToken(.l_paren);
const scratch_top = p.scratch.items.len;
defer p.scratch.shrinkRetainingCapacity(scratch_top);
+ var varargs: union(enum){ none, seen, nonfinal: TokenIndex } = .none;
while (true) {
if (p.eatToken(.r_paren)) |_| break;
+ if (varargs == .seen) varargs = .{ .nonfinal = p.tok_i };
const param = try p.expectParamDecl();
if (param != 0) {
try p.scratch.append(p.gpa, param);
+ } else if (p.token_tags[p.tok_i - 1] == .ellipsis3) {
+ if (varargs == .none) varargs = .seen;
}
switch (p.token_tags[p.nextToken()]) {
.comma => {},
@@ -3574,6 +3578,9 @@ const Parser = struct {
},
}
}
+ if (varargs == .nonfinal) {
+ try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = varargs.nonfinal });
+ }
const params = p.scratch.items[scratch_top..];
return switch (params.len) {
0 => SmallSpan { .zero_or_one = 0 },
lib/std/zig/parser_test.zig
@@ -5171,6 +5171,18 @@ test "recovery: missing while rbrace" {
});
}
+test "recovery: nonfinal varargs" {
+ try testError(
+ \\extern fn f(a: u32, ..., b: u32) void;
+ \\extern fn g(a: u32, ..., b: anytype) void;
+ \\extern fn h(a: u32, ..., ...) void;
+ , &[_]Error{
+ .varargs_nonfinal,
+ .varargs_nonfinal,
+ .varargs_nonfinal,
+ });
+}
+
const std = @import("std");
const mem = std.mem;
const print = std.debug.print;