about summary refs log tree commit diff
path: root/src/test/rustdoc-json
diff options
context:
space:
mode:
authorRune Tynan <runetynan@gmail.com>2021-01-15 20:34:15 -0500
committerRune Tynan <runetynan@gmail.com>2021-01-19 14:24:25 -0500
commit7715656edd201b8c6bbddf0040f424c27e4db4df (patch)
treea3c2f7b0835fd4f3728d85755df926566ee29f36 /src/test/rustdoc-json
parentf09fb488f70c5965ec4f64453a6e681fbfcff56c (diff)
downloadrust-7715656edd201b8c6bbddf0040f424c27e4db4df.tar.gz
rust-7715656edd201b8c6bbddf0040f424c27e4db4df.zip
Add jsondocck tool, and use it for rustdoc JSON
Diffstat (limited to 'src/test/rustdoc-json')
-rw-r--r--src/test/rustdoc-json/check_missing_items.py187
-rw-r--r--src/test/rustdoc-json/compare.py132
-rw-r--r--src/test/rustdoc-json/nested.expected196
-rw-r--r--src/test/rustdoc-json/nested.rs18
-rw-r--r--src/test/rustdoc-json/structs.expected456
-rw-r--r--src/test/rustdoc-json/structs.rs17
-rw-r--r--src/test/rustdoc-json/structs/plain_empty.rs7
-rw-r--r--src/test/rustdoc-json/structs/tuple.rs6
-rw-r--r--src/test/rustdoc-json/structs/unit.rs6
-rw-r--r--src/test/rustdoc-json/structs/with_generics.rs15
-rw-r--r--src/test/rustdoc-json/structs/with_primitives.rs11
11 files changed, 63 insertions, 988 deletions
diff --git a/src/test/rustdoc-json/check_missing_items.py b/src/test/rustdoc-json/check_missing_items.py
deleted file mode 100644
index 3a3bf7fa3ed..00000000000
--- a/src/test/rustdoc-json/check_missing_items.py
+++ /dev/null
@@ -1,187 +0,0 @@
-#!/usr/bin/env python
-
-# This test ensures that every ID in the produced json actually resolves to an item either in
-# `index` or `paths`. It DOES NOT check that the structure of the produced json is actually in
-# any way correct, for example an empty map would pass.
-
-import sys
-import json
-
-crate = json.load(open(sys.argv[1]))
-
-
-def get_local_item(item_id):
-    if item_id in crate["index"]:
-        return crate["index"][item_id]
-    print("Missing local ID:", item_id)
-    sys.exit(1)
-
-
-# local IDs have to be in `index`, external ones can sometimes be in `index` but otherwise have
-# to be in `paths`
-def valid_id(item_id):
-    return item_id in crate["index"] or item_id[0] != "0" and item_id in crate["paths"]
-
-
-def check_generics(generics):
-    for param in generics["params"]:
-        check_generic_param(param)
-    for where_predicate in generics["where_predicates"]:
-        if "bound_predicate" in where_predicate:
-            pred = where_predicate["bound_predicate"]
-            check_type(pred["ty"])
-            for bound in pred["bounds"]:
-                check_generic_bound(bound)
-        elif "region_predicate" in where_predicate:
-            pred = where_predicate["region_predicate"]
-            for bound in pred["bounds"]:
-                check_generic_bound(bound)
-        elif "eq_predicate" in where_predicate:
-            pred = where_predicate["eq_predicate"]
-            check_type(pred["rhs"])
-            check_type(pred["lhs"])
-
-
-def check_generic_param(param):
-    if "type" in param["kind"]:
-        ty = param["kind"]["type"]
-        if ty["default"]:
-            check_type(ty["default"])
-        for bound in ty["bounds"]:
-            check_generic_bound(bound)
-    elif "const" in param["kind"]:
-        check_type(param["kind"]["const"])
-
-
-def check_generic_bound(bound):
-    if "trait_bound" in bound:
-        for param in bound["trait_bound"]["generic_params"]:
-            check_generic_param(param)
-        check_type(bound["trait_bound"]["trait"])
-
-
-def check_decl(decl):
-    for (_name, ty) in decl["inputs"]:
-        check_type(ty)
-    if decl["output"]:
-        check_type(decl["output"])
-
-
-def check_type(ty):
-    if ty["kind"] == "resolved_path":
-        for bound in ty["inner"]["param_names"]:
-            check_generic_bound(bound)
-        args = ty["inner"]["args"]
-        if args:
-            if "angle_bracketed" in args:
-                for arg in args["angle_bracketed"]["args"]:
-                    if "type" in arg:
-                        check_type(arg["type"])
-                    elif "const" in arg:
-                        check_type(arg["const"]["type"])
-                for binding in args["angle_bracketed"]["bindings"]:
-                    if "equality" in binding["binding"]:
-                        check_type(binding["binding"]["equality"])
-                    elif "constraint" in binding["binding"]:
-                        for bound in binding["binding"]["constraint"]:
-                            check_generic_bound(bound)
-            elif "parenthesized" in args:
-                for ty in args["parenthesized"]["inputs"]:
-                    check_type(ty)
-                if args["parenthesized"]["output"]:
-                    check_type(args["parenthesized"]["output"])
-        if not valid_id(ty["inner"]["id"]):
-            print("Type contained an invalid ID:", ty["inner"]["id"])
-            sys.exit(1)
-    elif ty["kind"] == "tuple":
-        for ty in ty["inner"]:
-            check_type(ty)
-    elif ty["kind"] == "slice":
-        check_type(ty["inner"])
-    elif ty["kind"] == "impl_trait":
-        for bound in ty["inner"]:
-            check_generic_bound(bound)
-    elif ty["kind"] in ("raw_pointer", "borrowed_ref", "array"):
-        check_type(ty["inner"]["type"])
-    elif ty["kind"] == "function_pointer":
-        for param in ty["inner"]["generic_params"]:
-            check_generic_param(param)
-        check_decl(ty["inner"]["inner"])
-    elif ty["kind"] == "qualified_path":
-        check_type(ty["inner"]["self_type"])
-        check_type(ty["inner"]["trait"])
-
-
-work_list = set([crate["root"]])
-visited = work_list.copy()
-
-while work_list:
-    current = work_list.pop()
-    visited.add(current)
-    item = get_local_item(current)
-    # check intradoc links
-    for (_name, link) in item["links"].items():
-        if not valid_id(link):
-            print("Intra-doc link contains invalid ID:", link)
-
-    # check all fields that reference types such as generics as well as nested items
-    # (modules, structs, traits, and enums)
-    if item["kind"] == "module":
-        work_list |= set(item["inner"]["items"]) - visited
-    elif item["kind"] == "struct":
-        check_generics(item["inner"]["generics"])
-        work_list |= (
-            set(item["inner"]["fields"]) | set(item["inner"]["impls"])
-        ) - visited
-    elif item["kind"] == "struct_field":
-        check_type(item["inner"])
-    elif item["kind"] == "enum":
-        check_generics(item["inner"]["generics"])
-        work_list |= (
-            set(item["inner"]["variants"]) | set(item["inner"]["impls"])
-        ) - visited
-    elif item["kind"] == "variant":
-        if item["inner"]["variant_kind"] == "tuple":
-            for ty in item["inner"]["variant_inner"]:
-                check_type(ty)
-        elif item["inner"]["variant_kind"] == "struct":
-            work_list |= set(item["inner"]["variant_inner"]) - visited
-    elif item["kind"] in ("function", "method"):
-        check_generics(item["inner"]["generics"])
-        check_decl(item["inner"]["decl"])
-    elif item["kind"] in ("static", "constant", "assoc_const"):
-        check_type(item["inner"]["type"])
-    elif item["kind"] == "typedef":
-        check_type(item["inner"]["type"])
-        check_generics(item["inner"]["generics"])
-    elif item["kind"] == "opaque_ty":
-        check_generics(item["inner"]["generics"])
-        for bound in item["inner"]["bounds"]:
-            check_generic_bound(bound)
-    elif item["kind"] == "trait_alias":
-        check_generics(item["inner"]["params"])
-        for bound in item["inner"]["bounds"]:
-            check_generic_bound(bound)
-    elif item["kind"] == "trait":
-        check_generics(item["inner"]["generics"])
-        for bound in item["inner"]["bounds"]:
-            check_generic_bound(bound)
-        work_list |= (
-            set(item["inner"]["items"]) | set(item["inner"]["implementors"])
-        ) - visited
-    elif item["kind"] == "impl":
-        check_generics(item["inner"]["generics"])
-        if item["inner"]["trait"]:
-            check_type(item["inner"]["trait"])
-        if item["inner"]["blanket_impl"]:
-            check_type(item["inner"]["blanket_impl"])
-        check_type(item["inner"]["for"])
-        for assoc_item in item["inner"]["items"]:
-            if not valid_id(assoc_item):
-                print("Impl block referenced a missing ID:", assoc_item)
-                sys.exit(1)
-    elif item["kind"] == "assoc_type":
-        for bound in item["inner"]["bounds"]:
-            check_generic_bound(bound)
-        if item["inner"]["default"]:
-            check_type(item["inner"]["default"])
diff --git a/src/test/rustdoc-json/compare.py b/src/test/rustdoc-json/compare.py
deleted file mode 100644
index 6a921266336..00000000000
--- a/src/test/rustdoc-json/compare.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python
-
-# This script can check that an expected json blob is a subset of what actually gets produced.
-# The comparison is independent of the value of IDs (which are unstable) and instead uses their
-# relative ordering to check them against eachother by looking them up in their respective blob's
-# `index` or `paths` mappings. To add a new test run `rustdoc --output-format json -o . yourtest.rs`
-# and then create `yourtest.expected` by stripping unnecessary details from `yourtest.json`. If
-# you're on windows, replace `\` with `/`.
-
-# WARNING: The error messages produced by this may be misleading, in the case of list re-ordering
-#          it may point to apparently unrelated keys.
-
-import copy
-import sys
-import json
-import types
-
-# Used instead of the string ids when used as references.
-# Not used as keys in `index` or `paths`
-class ID(str):
-    pass
-
-
-class SubsetException(Exception):
-    def __init__(self, msg, trace):
-        self.msg = msg
-        self.trace = msg
-        super().__init__("{}: {}".format(trace, msg))
-
-
-def check_subset(expected_main, actual_main, base_dir):
-    expected_index = expected_main["index"]
-    expected_paths = expected_main["paths"]
-    actual_index = actual_main["index"]
-    actual_paths = actual_main["paths"]
-    already_checked = set()
-
-    def _check_subset(expected, actual, trace):
-        expected_type = type(expected)
-        actual_type = type(actual)
-
-        if actual_type is str:
-            actual = normalize(actual).replace(base_dir, "$TEST_BASE_DIR")
-
-        if expected_type is not actual_type:
-            raise SubsetException(
-                "expected type `{}`, got `{}`".format(expected_type, actual_type), trace
-            )
-
-
-        if expected_type in (int, bool, str) and expected != actual:
-            raise SubsetException("expected `{}`, got: `{}`".format(expected, actual), trace)
-        if expected_type is dict:
-            for key in expected:
-                if key not in actual:
-                    raise SubsetException(
-                        "Key `{}` not found in output".format(key), trace
-                    )
-                new_trace = copy.deepcopy(trace)
-                new_trace.append(key)
-                _check_subset(expected[key], actual[key], new_trace)
-        elif expected_type is list:
-            expected_elements = len(expected)
-            actual_elements = len(actual)
-            if expected_elements != actual_elements:
-                raise SubsetException(
-                    "Found {} items, expected {}".format(
-                        expected_elements, actual_elements
-                    ),
-                    trace,
-                )
-            for expected, actual in zip(expected, actual):
-                new_trace = copy.deepcopy(trace)
-                new_trace.append(expected)
-                _check_subset(expected, actual, new_trace)
-        elif expected_type is ID and expected not in already_checked:
-            already_checked.add(expected)
-            _check_subset(
-                expected_index.get(expected, {}), actual_index.get(actual, {}), trace
-            )
-            _check_subset(
-                expected_paths.get(expected, {}), actual_paths.get(actual, {}), trace
-            )
-
-    _check_subset(expected_main["root"], actual_main["root"], [])
-
-
-def rustdoc_object_hook(obj):
-    # No need to convert paths, index and external_crates keys to ids, since
-    # they are the target of resolution, and never a source itself.
-    if "id" in obj and obj["id"]:
-        obj["id"] = ID(obj["id"])
-    if "root" in obj:
-        obj["root"] = ID(obj["root"])
-    if "items" in obj:
-        obj["items"] = [ID(id) for id in obj["items"]]
-    if "variants" in obj:
-        obj["variants"] = [ID(id) for id in obj["variants"]]
-    if "fields" in obj:
-        obj["fields"] = [ID(id) for id in obj["fields"]]
-    if "impls" in obj:
-        obj["impls"] = [ID(id) for id in obj["impls"]]
-    if "implementors" in obj:
-        obj["implementors"] = [ID(id) for id in obj["implementors"]]
-    if "links" in obj:
-        obj["links"] = {s: ID(id) for s, id in obj["links"]}
-    if "variant_kind" in obj and obj["variant_kind"] == "struct":
-        obj["variant_inner"] = [ID(id) for id in obj["variant_inner"]]
-    return obj
-
-
-def main(expected_fpath, actual_fpath, base_dir):
-    print(
-        "checking that {} is a logical subset of {}".format(
-            expected_fpath, actual_fpath
-        )
-    )
-    with open(expected_fpath) as expected_file:
-        expected_main = json.load(expected_file, object_hook=rustdoc_object_hook)
-    with open(actual_fpath) as actual_file:
-        actual_main = json.load(actual_file, object_hook=rustdoc_object_hook)
-    check_subset(expected_main, actual_main, base_dir)
-    print("all checks passed")
-
-def normalize(s):
-    return s.replace('\\', '/')
-
-if __name__ == "__main__":
-    if len(sys.argv) < 4:
-        print("Usage: `compare.py expected.json actual.json test-dir`")
-    else:
-        main(sys.argv[1], sys.argv[2], normalize(sys.argv[3]))
diff --git a/src/test/rustdoc-json/nested.expected b/src/test/rustdoc-json/nested.expected
deleted file mode 100644
index 80070e75f1e..00000000000
--- a/src/test/rustdoc-json/nested.expected
+++ /dev/null
@@ -1,196 +0,0 @@
-{
-  "crate_version": null,
-  "external_crates": {},
-  "format_version": 1,
-  "includes_private": false,
-  "index": {
-    "0:0": {
-      "attrs": [],
-      "crate_id": 0,
-      "deprecation": null,
-      "docs": "",
-      "id": "0:0",
-      "inner": {
-        "is_crate": true,
-        "items": [
-          "0:3"
-        ]
-      },
-      "kind": "module",
-      "links": {},
-      "name": "nested",
-      "source": {
-        "begin": [
-          2,
-          0
-        ],
-        "end": [
-          7,
-          1
-        ],
-        "filename": "$TEST_BASE_DIR/nested.rs"
-      },
-      "visibility": "public"
-    },
-    "0:3": {
-      "attrs": [],
-      "crate_id": 0,
-      "deprecation": null,
-      "docs": "",
-      "id": "0:3",
-      "inner": {
-        "is_crate": false,
-        "items": [
-          "0:4",
-          "0:7"
-        ]
-      },
-      "kind": "module",
-      "links": {},
-      "name": "l1",
-      "source": {
-        "begin": [
-          2,
-          0
-        ],
-        "end": [
-          7,
-          1
-        ],
-        "filename": "$TEST_BASE_DIR/nested.rs"
-      },
-      "visibility": "public"
-    },
-    "0:4": {
-      "attrs": [],
-      "crate_id": 0,
-      "deprecation": null,
-      "docs": "",
-      "id": "0:4",
-      "inner": {
-        "is_crate": false,
-        "items": [
-          "0:5"
-        ]
-      },
-      "kind": "module",
-      "links": {},
-      "name": "l3",
-      "source": {
-        "begin": [
-          3,
-          4
-        ],
-        "end": [
-          5,
-          5
-        ],
-        "filename": "$TEST_BASE_DIR/nested.rs"
-      },
-      "visibility": "public"
-    },
-    "0:5": {
-      "attrs": [],
-      "crate_id": 0,
-      "deprecation": null,
-      "docs": "",
-      "id": "0:5",
-      "inner": {
-        "fields": [],
-        "fields_stripped": false,
-        "generics": {
-          "params": [],
-          "where_predicates": []
-        },
-        "impls": [
-          "0:10",
-          "0:11",
-          "0:12",
-          "0:14",
-          "0:15"
-        ],
-        "struct_type": "unit"
-      },
-      "kind": "struct",
-      "links": {},
-      "name": "L4",
-      "source": {
-        "begin": [
-          4,
-          8
-        ],
-        "end": [
-          4,
-          22
-        ],
-        "filename": "$TEST_BASE_DIR/nested.rs"
-      },
-      "visibility": "public"
-    },
-    "0:7": {
-      "attrs": [],
-      "crate_id": 0,
-      "deprecation": null,
-      "docs": "",
-      "id": "0:7",
-      "inner": {
-        "glob": false,
-        "id": "0:5",
-        "name": "L4",
-        "span": "l3::L4"
-      },
-      "kind": "import",
-      "links": {},
-      "name": null,
-      "source": {
-        "begin": [
-          6,
-          4
-        ],
-        "end": [
-          6,
-          19
-        ],
-        "filename": "$TEST_BASE_DIR/nested.rs"
-      },
-      "visibility": "public"
-    }
-  },
-  "paths": {
-    "0:0": {
-      "crate_id": 0,
-      "kind": "module",
-      "path": [
-        "nested"
-      ]
-    },
-    "0:3": {
-      "crate_id": 0,
-      "kind": "module",
-      "path": [
-        "nested",
-        "l1"
-      ]
-    },
-    "0:4": {
-      "crate_id": 0,
-      "kind": "module",
-      "path": [
-        "nested",
-        "l1",
-        "l3"
-      ]
-    },
-    "0:5": {
-      "crate_id": 0,
-      "kind": "struct",
-      "path": [
-        "nested",
-        "l1",
-        "l3",
-        "L4"
-      ]
-    }
-  },
-  "root": "0:0"
-}
\ No newline at end of file
diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs
index e460b343d37..2f32f4ef76d 100644
--- a/src/test/rustdoc-json/nested.rs
+++ b/src/test/rustdoc-json/nested.rs
@@ -1,7 +1,25 @@
 // edition:2018
+
+// @has nested.json "$.index.['0:0'].kind" \"module\"
+// @has - "$.index.['0:0'].inner.is_crate" true
+// @has - "$.index.['0:0'].inner.items[*]" \"0:3\"
+
+// @has nested.json "$.index.['0:3'].kind" \"module\"
+// @has - "$.index.['0:3'].inner.is_crate" false
+// @has - "$.index.['0:3'].inner.items[*]" \"0:4\"
+// @has - "$.index.['0:3'].inner.items[*]" \"0:7\"
 pub mod l1 {
+
+    // @has nested.json "$.index.['0:4'].kind" \"module\"
+    // @has - "$.index.['0:4'].inner.is_crate" false
+    // @has - "$.index.['0:4'].inner.items[*]" \"0:5\"
     pub mod l3 {
+
+        // @has nested.json "$.index.['0:5'].kind" \"struct\"
+        // @has - "$.index.['0:5'].inner.struct_type" \"unit\"
         pub struct L4;
     }
+    // @has nested.json "$.index.['0:7'].kind" \"import\"
+    // @has - "$.index.['0:7'].inner.glob" false
     pub use l3::L4;
 }
diff --git a/src/test/rustdoc-json/structs.expected b/src/test/rustdoc-json/structs.expected
deleted file mode 100644
index 799829de3fd..00000000000
--- a/src/test/rustdoc-json/structs.expected
+++ /dev/null
@@ -1,456 +0,0 @@
-{
-  "root": "0:0",
-  "version": null,
-  "includes_private": false,
-  "index": {
-    "0:9": {
-      "crate_id": 0,
-      "name": "Unit",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          7,
-          0
-        ],
-        "end": [
-          7,
-          16
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct",
-      "inner": {
-        "struct_type": "unit",
-        "generics": {
-          "params": [],
-          "where_predicates": []
-        },
-        "fields_stripped": false,
-        "fields": []
-      }
-    },
-    "0:8": {
-      "crate_id": 0,
-      "name": "1",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          5,
-          22
-        ],
-        "end": [
-          5,
-          28
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "resolved_path",
-        "inner": {
-          "name": "String",
-          "id": "5:5035",
-          "args": {
-            "angle_bracketed": {
-              "args": [],
-              "bindings": []
-            }
-          },
-          "param_names": []
-        }
-      }
-    },
-    "0:18": {
-      "crate_id": 0,
-      "name": "stuff",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          15,
-          4
-        ],
-        "end": [
-          15,
-          17
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "resolved_path",
-        "inner": {
-          "name": "Vec",
-          "id": "5:4322",
-          "args": {
-            "angle_bracketed": {
-              "args": [
-                {
-                  "type": {
-                    "kind": "generic",
-                    "inner": "T"
-                  }
-                }
-              ],
-              "bindings": []
-            }
-          },
-          "param_names": []
-        }
-      }
-    },
-    "0:11": {
-      "crate_id": 0,
-      "name": "WithPrimitives",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          9,
-          0
-        ],
-        "end": [
-          12,
-          1
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct",
-      "inner": {
-        "struct_type": "plain",
-        "generics": {
-          "params": [
-            {
-              "name": "'a",
-              "kind": "lifetime"
-            }
-          ],
-          "where_predicates": []
-        },
-        "fields_stripped": true
-      }
-    },
-    "0:14": {
-      "crate_id": 0,
-      "name": "s",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          11,
-          4
-        ],
-        "end": [
-          11,
-          14
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "borrowed_ref",
-        "inner": {
-          "lifetime": "'a",
-          "mutable": false,
-          "type": {
-            "kind": "primitive",
-            "inner": "str"
-          }
-        }
-      }
-    },
-    "0:19": {
-      "crate_id": 0,
-      "name": "things",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          16,
-          4
-        ],
-        "end": [
-          16,
-          25
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "resolved_path",
-        "inner": {
-          "name": "HashMap",
-          "id": "1:6600",
-          "args": {
-            "angle_bracketed": {
-              "args": [
-                {
-                  "type": {
-                    "kind": "generic",
-                    "inner": "U"
-                  }
-                },
-                {
-                  "type": {
-                    "kind": "generic",
-                    "inner": "U"
-                  }
-                }
-              ],
-              "bindings": []
-            }
-          },
-          "param_names": []
-        }
-      }
-    },
-    "0:15": {
-      "crate_id": 0,
-      "name": "WithGenerics",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          14,
-          0
-        ],
-        "end": [
-          17,
-          1
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct",
-      "inner": {
-        "struct_type": "plain",
-        "generics": {
-          "params": [
-            {
-              "name": "T",
-              "kind": {
-                "type": {
-                  "bounds": [],
-                  "default": null
-                }
-              }
-            },
-            {
-              "name": "U",
-              "kind": {
-                "type": {
-                  "bounds": [],
-                  "default": null
-                }
-              }
-            }
-          ],
-          "where_predicates": []
-        },
-        "fields_stripped": true
-      }
-    },
-    "0:0": {
-      "crate_id": 0,
-      "name": "structs",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          1,
-          0
-        ],
-        "end": [
-          17,
-          1
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "module",
-      "inner": {
-        "is_crate": true,
-        "items": [
-          "0:4",
-          "0:5",
-          "0:9",
-          "0:11",
-          "0:15"
-        ]
-      }
-    },
-    "0:13": {
-      "crate_id": 0,
-      "name": "num",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          10,
-          4
-        ],
-        "end": [
-          10,
-          12
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "primitive",
-        "inner": "u32"
-      }
-    },
-    "0:5": {
-      "crate_id": 0,
-      "name": "Tuple",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          5,
-          0
-        ],
-        "end": [
-          5,
-          30
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct",
-      "inner": {
-        "struct_type": "tuple",
-        "generics": {
-          "params": [],
-          "where_predicates": []
-        },
-        "fields_stripped": true
-      }
-    },
-    "0:4": {
-      "crate_id": 0,
-      "name": "PlainEmpty",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          3,
-          0
-        ],
-        "end": [
-          3,
-          24
-        ]
-      },
-      "visibility": "public",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct",
-      "inner": {
-        "struct_type": "plain",
-        "generics": {
-          "params": [],
-          "where_predicates": []
-        },
-        "fields_stripped": false,
-        "fields": []
-      }
-    },
-    "0:7": {
-      "crate_id": 0,
-      "name": "0",
-      "source": {
-        "filename": "$TEST_BASE_DIR/structs.rs",
-        "begin": [
-          5,
-          17
-        ],
-        "end": [
-          5,
-          20
-        ]
-      },
-      "visibility": "default",
-      "docs": "",
-      "links": {},
-      "attrs": [],
-      "deprecation": null,
-      "kind": "struct_field",
-      "inner": {
-        "kind": "primitive",
-        "inner": "u32"
-      }
-    }
-  },
-  "paths": {
-    "5:4322": {
-      "crate_id": 5,
-      "path": [
-        "alloc",
-        "vec",
-        "Vec"
-      ],
-      "kind": "struct"
-    },
-    "5:5035": {
-      "crate_id": 5,
-      "path": [
-        "alloc",
-        "string",
-        "String"
-      ],
-      "kind": "struct"
-    },
-    "1:6600": {
-      "crate_id": 1,
-      "path": [
-        "std",
-        "collections",
-        "hash",
-        "map",
-        "HashMap"
-      ],
-      "kind": "struct"
-    }
-  },
-  "external_crates": {
-    "1": {
-      "name": "std"
-    },
-    "5": {
-      "name": "alloc"
-    }
-  },
-  "format_version": 1
-}
diff --git a/src/test/rustdoc-json/structs.rs b/src/test/rustdoc-json/structs.rs
deleted file mode 100644
index 43fc4743503..00000000000
--- a/src/test/rustdoc-json/structs.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::collections::HashMap;
-
-pub struct PlainEmpty {}
-
-pub struct Tuple(u32, String);
-
-pub struct Unit;
-
-pub struct WithPrimitives<'a> {
-    num: u32,
-    s: &'a str,
-}
-
-pub struct WithGenerics<T, U> {
-    stuff: Vec<T>,
-    things: HashMap<U, U>,
-}
diff --git a/src/test/rustdoc-json/structs/plain_empty.rs b/src/test/rustdoc-json/structs/plain_empty.rs
new file mode 100644
index 00000000000..9e41b1b72a2
--- /dev/null
+++ b/src/test/rustdoc-json/structs/plain_empty.rs
@@ -0,0 +1,7 @@
+// @has plain_empty.json "$.index.['0:3'].name" \"PlainEmpty\"
+// @has - "$.index.['0:3'].visibility" \"public\"
+// @has - "$.index.['0:3'].kind" \"struct\"
+// @has - "$.index.['0:3'].inner.struct_type" \"plain\"
+// @has - "$.index.['0:3'].inner.fields_stripped" false
+// @has - "$.index.['0:3'].inner.fields" []
+pub struct PlainEmpty {}
diff --git a/src/test/rustdoc-json/structs/tuple.rs b/src/test/rustdoc-json/structs/tuple.rs
new file mode 100644
index 00000000000..de9a0da8e72
--- /dev/null
+++ b/src/test/rustdoc-json/structs/tuple.rs
@@ -0,0 +1,6 @@
+// @has tuple.json "$.index.['0:3'].name" \"Tuple\"
+// @has - "$.index.['0:3'].visibility" \"public\"
+// @has - "$.index.['0:3'].kind" \"struct\"
+// @has - "$.index.['0:3'].inner.struct_type" \"tuple\"
+// @has - "$.index.['0:3'].inner.fields_stripped" true
+pub struct Tuple(u32, String);
diff --git a/src/test/rustdoc-json/structs/unit.rs b/src/test/rustdoc-json/structs/unit.rs
new file mode 100644
index 00000000000..14a1a7ec5d4
--- /dev/null
+++ b/src/test/rustdoc-json/structs/unit.rs
@@ -0,0 +1,6 @@
+// @has unit.json "$.index.['0:3'].name" \"Unit\"
+// @has - "$.index.['0:3'].visibility" \"public\"
+// @has - "$.index.['0:3'].kind" \"struct\"
+// @has - "$.index.['0:3'].inner.struct_type" \"unit\"
+// @has - "$.index.['0:3'].inner.fields" []
+pub struct Unit;
diff --git a/src/test/rustdoc-json/structs/with_generics.rs b/src/test/rustdoc-json/structs/with_generics.rs
new file mode 100644
index 00000000000..549729b4ca7
--- /dev/null
+++ b/src/test/rustdoc-json/structs/with_generics.rs
@@ -0,0 +1,15 @@
+use std::collections::HashMap;
+
+// @has with_generics.json "$.index.['0:4'].name" \"WithGenerics\"
+// @has - "$.index.['0:4'].visibility" \"public\"
+// @has - "$.index.['0:4'].kind" \"struct\"
+// @has - "$.index.['0:4'].inner.generics.params[0].name" \"T\"
+// @has - "$.index.['0:4'].inner.generics.params[0].kind.type"
+// @has - "$.index.['0:4'].inner.generics.params[1].name" \"U\"
+// @has - "$.index.['0:4'].inner.generics.params[1].kind.type"
+// @has - "$.index.['0:4'].inner.struct_type" \"plain\"
+// @has - "$.index.['0:4'].inner.fields_stripped" true
+pub struct WithGenerics<T, U> {
+    stuff: Vec<T>,
+    things: HashMap<U, U>,
+}
diff --git a/src/test/rustdoc-json/structs/with_primitives.rs b/src/test/rustdoc-json/structs/with_primitives.rs
new file mode 100644
index 00000000000..f948b61f79b
--- /dev/null
+++ b/src/test/rustdoc-json/structs/with_primitives.rs
@@ -0,0 +1,11 @@
+// @has with_primitives.json "$.index.['0:3'].name" \"WithPrimitives\"
+// @has - "$.index.['0:3'].visibility" \"public\"
+// @has - "$.index.['0:3'].kind" \"struct\"
+// @has - "$.index.['0:3'].inner.generics.params[0].name" \"\'a\"
+// @has - "$.index.['0:3'].inner.generics.params[0].kind" \"lifetime\"
+// @has - "$.index.['0:3'].inner.struct_type" \"plain\"
+// @has - "$.index.['0:3'].inner.fields_stripped" true
+pub struct WithPrimitives<'a> {
+    num: u32,
+    s: &'a str,
+}