Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

parseContainerMembers

Parse.parseContainerMembers
fn parseContainerMembers(p: *Parse) Error!Members

File

lib/std/zig/Parse.zig:264

Code

fn parseContainerMembers(p: *Parse) Error!Members {
    const scratch_top = p.scratch.items.len;
    defer p.scratch.shrinkRetainingCapacity(scratch_top);

    var field_state: union(enum) {
        /// No fields have been seen.
        none,
        /// Currently parsing fields.
        seen,
        /// Saw fields and then a declaration after them.
        /// Payload is first token of previous declaration.
        end: Node.Index,
        /// There was a declaration between fields, don't report more errors.
        err,
    } = .none;

    var last_field: TokenIndex = undefined;

    // Skip container doc comments.
    while (p.eatToken(.container_doc_comment)) |_| {}

    var trailing = false;
    while (true) {
        const doc_comment = try p.eatDocComments();

        sw: switch (p.tokenTag(p.tok_i)) {
            .keyword_test => {
                if (doc_comment) |some| {
                    try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
                }
                const maybe_test_decl_node = try p.expectTestDeclRecoverable();
                if (maybe_test_decl_node) |test_decl_node| {
                    if (field_state == .seen) {
                        field_state = .{ .end = test_decl_node };
                    }
                    try p.scratch.append(p.gpa, test_decl_node);
                }
                trailing = false;
            },
            .keyword_comptime => switch (p.tokenTag(p.tok_i + 1)) {
                .l_brace => {
                    if (doc_comment) |some| {
                        try p.warnMsg(.{ .tag = .comptime_doc_comment, .token = some });
                    }
                    const comptime_token = p.nextToken();
                    const opt_block = p.parseBlock() catch |err| switch (err) {
                        error.OutOfMemory => |e| return e,
                        error.ParseError => blk: {
                            p.findNextContainerMember();
                            break :blk null;
                        },
                    };
                    if (opt_block) |block| {
                        const comptime_node = try p.addNode(.{
                            .tag = .@"comptime",
                            .main_token = comptime_token,
                            .data = .{ .node = block },
                        });
                        if (field_state == .seen) {
                            field_state = .{ .end = comptime_node };
                        }
                        try p.scratch.append(p.gpa, comptime_node);
                    }
                    trailing = false;
                },
                else => {
                    const identifier = p.tok_i;
                    defer last_field = identifier;
                    switch (field_state) {
                        .none => field_state = .seen,
                        .err, .seen => {},
                        .end => |node| {
                            try p.warnMsg(.{
                                .tag = .decl_between_fields,
                                .token = p.nodeMainToken(node),
                            });
                            try p.warnMsg(.{
                                .tag = .previous_field,
                                .is_note = true,
                                .token = last_field,
                            });
                            try p.warnMsg(.{
                                .tag = .next_field,
                                .is_note = true,
                                .token = identifier,
                            });
                            // Continue parsing; error will be reported later.
                            field_state = .err;
                        },
                    }
                    const container_field = p.expectContainerField() catch |err| switch (err) {
                        error.OutOfMemory => |e| return e,
                        error.ParseError => {
                            p.findNextContainerMember();
                            continue;
                        },
                    };
                    try p.scratch.append(p.gpa, container_field);
                    switch (p.tokenTag(p.tok_i)) {
                        .comma => {
                            p.tok_i += 1;
                            trailing = true;
                            continue;
                        },
                        .r_brace, .eof => {
                            trailing = false;
                            break;
                        },
                        else => {},
                    }
                    // There is not allowed to be a decl after a field with no comma.
                    // Report error but recover parser.
                    try p.warn(.expected_comma_after_field);
                    p.findNextContainerMember();
                },
            },
            .keyword_pub,
            .keyword_const,
            .keyword_var,
            .keyword_threadlocal,
            .keyword_export,
            .keyword_extern,
            .keyword_inline,
            .keyword_noinline,
            .keyword_fn,
            => |t| {
                if (t == .keyword_extern) {
                    switch (p.tokenTag(p.tok_i + 1)) {
                        .keyword_struct,
                        .keyword_union,
                        .keyword_enum,
                        .keyword_opaque,
                        => |ct| continue :sw ct,
                        else => {},
                    }
                }
                if (t == .keyword_inline) {
                    switch (p.tokenTag(p.tok_i + 1)) {
                        .keyword_for,
                        .keyword_while,
                        => |ct| continue :sw ct,
                        else => {},
                    }
                }

                p.tok_i += @intFromBool(t == .keyword_pub);
                const opt_top_level_decl = try p.expectTopLevelDeclRecoverable();
                if (opt_top_level_decl) |top_level_decl| {
                    if (field_state == .seen) {
                        field_state = .{ .end = top_level_decl };
                    }
                    try p.scratch.append(p.gpa, top_level_decl);
                }
                trailing = p.tokenTag(p.tok_i - 1) == .semicolon;
            },
            .eof, .r_brace => {
                if (doc_comment) |tok| {
                    try p.warnMsg(.{
                        .tag = .unattached_doc_comment,
                        .token = tok,
                    });
                }
                break;
            },
            else => {
                const c_container = p.parseCStyleContainer() catch |err| switch (err) {
                    error.OutOfMemory => |e| return e,
                    error.ParseError => false,
                };
                if (c_container) continue;

                const identifier = p.tok_i;
                defer last_field = identifier;
                switch (field_state) {
                    .none => field_state = .seen,
                    .err, .seen => {},
                    .end => |node| {
                        try p.warnMsg(.{
                            .tag = .decl_between_fields,
                            .token = p.nodeMainToken(node),
                        });
                        try p.warnMsg(.{
                            .tag = .previous_field,
                            .is_note = true,
                            .token = last_field,
                        });
                        try p.warnMsg(.{
                            .tag = .next_field,
                            .is_note = true,
                            .token = identifier,
                        });
                        // Continue parsing; error will be reported later.
                        field_state = .err;
                    },
                }
                const container_field = p.expectContainerField() catch |err| switch (err) {
                    error.OutOfMemory => |e| return e,
                    error.ParseError => {
                        p.findNextContainerMember();
                        continue;
                    },
                };
                try p.scratch.append(p.gpa, container_field);
                switch (p.tokenTag(p.tok_i)) {
                    .comma => {
                        p.tok_i += 1;
                        trailing = true;
                        continue;
                    },
                    .r_brace, .eof => {
                        trailing = false;
                        break;
                    },
                    else => {},
                }
                // There is not allowed to be a decl after a field with no comma.
                // Report error but recover parser.
                try p.warn(.expected_comma_after_field);
                if (p.tokenTag(p.tok_i) == .semicolon and p.tokenTag(identifier) == .identifier) {
                    try p.warnMsg(.{
                        .tag = .var_const_decl,
                        .is_note = true,
                        .token = identifier,
                    });
                }
                p.findNextContainerMember();
                continue;
            },
        }
    }

    const items = p.scratch.items[scratch_top..];
    if (items.len <= 2) {
        return Members{
            .len = items.len,
            .data = .{ .opt_node_and_opt_node = .{
                if (items.len >= 1) items[0].toOptional() else .none,
                if (items.len >= 2) items[1].toOptional() else .none,
            } },
            .trailing = trailing,
        };
    } else {
        return Members{
            .len = items.len,
            .data = .{ .extra_range = try p.listToSpan(items) },
            .trailing = trailing,
        };
    }
}