Commit 2511830442

Ian Johnson <ian@ianjohnson.dev>
2024-07-08 04:12:37
Autodoc: only group structs under "namespaces"
The old heuristic of checking only for the number of fields has the downside of classifying all opaque types, such as `std.c.FILE`, as "namespaces" rather than "types".
1 parent 854e86c
Changed files (4)
lib/docs/wasm/Decl.zig
@@ -115,7 +115,7 @@ pub fn categorize(decl: *const Decl) Walk.Category {
 pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index {
     switch (decl.categorize()) {
         .alias => |aliasee| return aliasee.get().get_child(name),
-        .namespace => |node| {
+        .namespace, .container => |node| {
             const file = decl.file.get();
             const scope = file.scopes.get(node) orelse return null;
             const child_node = scope.get_child(name) orelse return null;
@@ -128,7 +128,7 @@ pub fn get_child(decl: *const Decl, name: []const u8) ?Decl.Index {
 /// Looks up a decl by name accessible in `decl`'s namespace.
 pub fn lookup(decl: *const Decl, name: []const u8) ?Decl.Index {
     const namespace_node = switch (decl.categorize()) {
-        .namespace => |node| node,
+        .namespace, .container => |node| node,
         else => decl.parent.get().ast_node,
     };
     const file = decl.file.get();
lib/docs/wasm/main.zig
@@ -274,13 +274,6 @@ export fn fn_error_set_decl(decl_index: Decl.Index, node: Ast.Node.Index) Decl.I
     };
 }
 
-export fn decl_field_count(decl_index: Decl.Index) u32 {
-    switch (decl_index.get().categorize()) {
-        .namespace => |node| return decl_index.get().file.get().field_count(node),
-        else => return 0,
-    }
-}
-
 fn decl_error_set_fallible(decl_index: Decl.Index) Oom![]ErrorIdentifier {
     error_set_result.clearRetainingCapacity();
     try addErrorsFromDecl(decl_index, &error_set_result);
@@ -583,7 +576,7 @@ export fn decl_category_name(decl_index: Decl.Index) String {
     const ast = decl.file.get_ast();
     const token_tags = ast.tokens.items(.tag);
     const name = switch (decl.categorize()) {
-        .namespace => |node| {
+        .namespace, .container => |node| {
             const node_tags = ast.nodes.items(.tag);
             if (node_tags[decl.ast_node] == .root)
                 return String.init("struct");
lib/docs/wasm/Walk.zig
@@ -7,7 +7,10 @@ file: File.Index,
 
 /// keep in sync with "CAT_" constants in main.js
 pub const Category = union(enum(u8)) {
+    /// A struct type used only to group declarations.
     namespace: Ast.Node.Index,
+    /// A container type (struct, union, enum, opaque).
+    container: Ast.Node.Index,
     global_variable: Ast.Node.Index,
     /// A function that has not been detected as returning a type.
     function: Ast.Node.Index,
@@ -45,13 +48,6 @@ pub const File = struct {
         return file.node_decls.get(decl_node) orelse return .none;
     }
 
-    pub fn field_count(file: *const File, node: Ast.Node.Index) u32 {
-        const scope = file.scopes.get(node) orelse return 0;
-        if (scope.tag != .namespace) return 0;
-        const namespace: *Scope.Namespace = @alignCast(@fieldParentPtr("base", scope));
-        return namespace.field_count;
-    }
-
     pub const Index = enum(u32) {
         _,
 
@@ -87,7 +83,18 @@ pub const File = struct {
             const node_tags = ast.nodes.items(.tag);
             const token_tags = ast.tokens.items(.tag);
             switch (node_tags[node]) {
-                .root => return .{ .namespace = node },
+                .root => {
+                    for (ast.rootDecls()) |member| {
+                        switch (node_tags[member]) {
+                            .container_field_init,
+                            .container_field_align,
+                            .container_field,
+                            => return .{ .container = node },
+                            else => {},
+                        }
+                    }
+                    return .{ .namespace = node };
+                },
 
                 .global_var_decl,
                 .local_var_decl,
@@ -122,7 +129,7 @@ pub const File = struct {
             full: Ast.full.FnProto,
         ) Category {
             return switch (categorize_expr(file_index, full.ast.return_type)) {
-                .namespace, .error_set, .type_type => .{ .type_function = node },
+                .namespace, .container, .error_set, .type_type => .{ .type_function = node },
                 else => .{ .function = node },
             };
         }
@@ -140,6 +147,7 @@ pub const File = struct {
             const node_tags = ast.nodes.items(.tag);
             const node_datas = ast.nodes.items(.data);
             const main_tokens = ast.nodes.items(.main_token);
+            const token_tags = ast.tokens.items(.tag);
             //log.debug("categorize_expr tag {s}", .{@tagName(node_tags[node])});
             return switch (node_tags[node]) {
                 .container_decl,
@@ -154,7 +162,23 @@ pub const File = struct {
                 .tagged_union_enum_tag_trailing,
                 .tagged_union_two,
                 .tagged_union_two_trailing,
-                => .{ .namespace = node },
+                => {
+                    var buf: [2]Ast.Node.Index = undefined;
+                    const container_decl = ast.fullContainerDecl(&buf, node).?;
+                    if (token_tags[container_decl.ast.main_token] != .keyword_struct) {
+                        return .{ .container = node };
+                    }
+                    for (container_decl.ast.members) |member| {
+                        switch (node_tags[member]) {
+                            .container_field_init,
+                            .container_field_align,
+                            .container_field,
+                            => return .{ .container = node },
+                            else => {},
+                        }
+                    }
+                    return .{ .namespace = node };
+                },
 
                 .error_set_decl,
                 .merge_error_sets,
@@ -240,6 +264,7 @@ pub const File = struct {
                             return .{ .error_set = node };
                         } else if (then_cat == .type or else_cat == .type or
                             then_cat == .namespace or else_cat == .namespace or
+                            then_cat == .container or else_cat == .container or
                             then_cat == .error_set or else_cat == .error_set or
                             then_cat == .type_function or else_cat == .type_function)
                         {
@@ -346,7 +371,7 @@ pub const File = struct {
                         any_type = true;
                         all_type_type = false;
                     },
-                    .type, .namespace, .type_function => {
+                    .type, .namespace, .container, .type_function => {
                         any_type = true;
                         all_error_set = false;
                         all_type_type = false;
@@ -431,7 +456,6 @@ pub const Scope = struct {
         names: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
         doctests: std.StringArrayHashMapUnmanaged(Ast.Node.Index) = .{},
         decl_index: Decl.Index,
-        field_count: u32,
     };
 
     fn getNamespaceDecl(start_scope: *Scope) Decl.Index {
@@ -500,7 +524,6 @@ fn struct_decl(
     namespace.* = .{
         .parent = scope,
         .decl_index = parent_decl,
-        .field_count = 0,
     };
     try w.file.get().scopes.putNoClobber(gpa, node, &namespace.base);
     try w.scanDecls(namespace, container_decl.ast.members);
@@ -1061,14 +1084,6 @@ fn scanDecls(w: *Walk, namespace: *Scope.Namespace, members: []const Ast.Node.In
                 continue;
             },
 
-            .container_field_init,
-            .container_field_align,
-            .container_field,
-            => {
-                namespace.field_count += 1;
-                continue;
-            },
-
             else => continue,
         };
 
lib/docs/main.js
@@ -1,14 +1,15 @@
 (function() {
     const CAT_namespace = 0;
-    const CAT_global_variable = 1;
-    const CAT_function = 2;
-    const CAT_primitive = 3;
-    const CAT_error_set = 4;
-    const CAT_global_const = 5;
-    const CAT_alias = 6;
-    const CAT_type = 7;
-    const CAT_type_type = 8;
-    const CAT_type_function = 9;
+    const CAT_container = 1;
+    const CAT_global_variable = 2;
+    const CAT_function = 3;
+    const CAT_primitive = 4;
+    const CAT_error_set = 5;
+    const CAT_global_const = 6;
+    const CAT_alias = 7;
+    const CAT_type = 8;
+    const CAT_type_type = 9;
+    const CAT_type_function = 10;
 
     const domDocTestsCode = document.getElementById("docTestsCode");
     const domFnErrorsAnyError = document.getElementById("fnErrorsAnyError");
@@ -184,6 +185,7 @@
       const category = wasm_exports.categorize_decl(decl_index, 0);
       switch (category) {
         case CAT_namespace:
+        case CAT_container:
           return renderNamespacePage(decl_index);
         case CAT_global_variable:
         case CAT_primitive:
@@ -426,16 +428,12 @@
         while (true) {
           const member_category = wasm_exports.categorize_decl(member, 0);
           switch (member_category) {
-            case CAT_namespace:
-              if (wasm_exports.decl_field_count(member) > 0) {
-                typesList.push({original: original, member: member});
-              } else {
-                namespacesList.push({original: original, member: member});
-              }
-              continue member_loop;
             case CAT_namespace:
               namespacesList.push({original: original, member: member});
               continue member_loop;
+            case CAT_container:
+              typesList.push({original: original, member: member});
+              continue member_loop;
             case CAT_global_variable:
               varsList.push(member);
               continue member_loop;