From 1098cce27acb2d52cb3b5ddbcf28c3a06e38dc7c Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Sun, 29 Nov 2020 16:16:25 +0000 Subject: Add tests for rustdoc json Move rustdoc/rustdoc-json to rustdoc-json Scaffold rustdoc-json test mode Implement run_rustdoc_json_test Fix up python Make tidy happy --- src/test/rustdoc-json/Makefile | 6 + src/test/rustdoc-json/check_missing_items.py | 187 +++++++++++ src/test/rustdoc-json/compare.py | 131 ++++++++ src/test/rustdoc-json/structs.expected | 468 +++++++++++++++++++++++++++ src/test/rustdoc-json/structs.rs | 17 + 5 files changed, 809 insertions(+) create mode 100644 src/test/rustdoc-json/Makefile create mode 100644 src/test/rustdoc-json/check_missing_items.py create mode 100644 src/test/rustdoc-json/compare.py create mode 100644 src/test/rustdoc-json/structs.expected create mode 100644 src/test/rustdoc-json/structs.rs (limited to 'src/test/rustdoc-json') diff --git a/src/test/rustdoc-json/Makefile b/src/test/rustdoc-json/Makefile new file mode 100644 index 00000000000..ad517ae95eb --- /dev/null +++ b/src/test/rustdoc-json/Makefile @@ -0,0 +1,6 @@ +-include ../tools.mk + +tests: *.rs + $(RUSTDOC) $< -o $(TMPDIR) --output-format json + $(PYTHON) check_missing_items.py $(TMPDIR)/$(basename $<).json + $(PYTHON) compare.py $(basename $<).expected $(TMPDIR)/$(basename $<).json diff --git a/src/test/rustdoc-json/check_missing_items.py b/src/test/rustdoc-json/check_missing_items.py new file mode 100644 index 00000000000..3a3bf7fa3ed --- /dev/null +++ b/src/test/rustdoc-json/check_missing_items.py @@ -0,0 +1,187 @@ +#!/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 new file mode 100644 index 00000000000..afc8066685c --- /dev/null +++ b/src/test/rustdoc-json/compare.py @@ -0,0 +1,131 @@ +#!/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`. + +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 expected_type is not actual_type: + raise SubsetException( + "expected type `{}`, got `{}`".format(expected_type, actual_type), trace + ) + if expected_type in (str, int, bool) and expected != actual: + if expected_type == str and actual.startswith(base_dir): + if actual.replace(base_dir + "/", "") != expected: + raise SubsetException( + "expected `{}`, got: `{}`".format( + expected, actual.replace(base_dir + "/", "") + ), + trace, + ) + else: + 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") + + +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], sys.argv[3]) diff --git a/src/test/rustdoc-json/structs.expected b/src/test/rustdoc-json/structs.expected new file mode 100644 index 00000000000..45b23534bc7 --- /dev/null +++ b/src/test/rustdoc-json/structs.expected @@ -0,0 +1,468 @@ +{ + "root": "0:0", + "version": null, + "includes_private": false, + "index": { + "0:9": { + "crate_id": 0, + "name": "Unit", + "source": { + "filename": "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": "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": "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": "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, + "fields": [ + "0:13", + "0:14" + ] + } + }, + "0:14": { + "crate_id": 0, + "name": "s", + "source": { + "filename": "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": "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": "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, + "fields": [ + "0:18", + "0:19" + ] + } + }, + "0:0": { + "crate_id": 0, + "name": "structs", + "source": { + "filename": "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": "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": "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, + "fields": [ + "0:7", + "0:8" + ] + } + }, + "0:4": { + "crate_id": 0, + "name": "PlainEmpty", + "source": { + "filename": "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": "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 new file mode 100644 index 00000000000..43fc4743503 --- /dev/null +++ b/src/test/rustdoc-json/structs.rs @@ -0,0 +1,17 @@ +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 { + stuff: Vec, + things: HashMap, +} -- cgit 1.4.1-3-g733a5 From 40b5470b0db1997fe28bd774171b4f958fa7d240 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Mon, 30 Nov 2020 20:24:48 +0000 Subject: Address review comments. Go back to CRATE_DEF_INDEX Minor niceness improvements Don't output hidden items Remove striped items from fields Add $TEST_BASE_DIR Small catch --- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/json/conversions.rs | 76 +++++++++++++++++----------------- src/librustdoc/json/mod.rs | 18 ++++---- src/librustdoc/json/types.rs | 3 -- src/librustdoc/lib.rs | 1 + src/test/rustdoc-json/Makefile | 6 --- src/test/rustdoc-json/compare.py | 21 ++++------ src/test/rustdoc-json/structs.expected | 42 +++++++------------ src/tools/compiletest/src/runtest.rs | 14 +++---- 9 files changed, 80 insertions(+), 103 deletions(-) delete mode 100644 src/test/rustdoc-json/Makefile (limited to 'src/test/rustdoc-json') diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9d42705da82..d294d8f02a8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2281,7 +2281,7 @@ impl Clean> for doctree::Import<'_> { name: None, attrs: self.attrs.clean(cx), source: self.span.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), + def_id: DefId::local(CRATE_DEF_INDEX), visibility: self.vis.clean(cx), stability: None, const_stability: None, diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 80192623d54..4d57b337879 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -12,43 +12,44 @@ use crate::doctree; use crate::formats::item_type::ItemType; use crate::json::types::*; -impl From for Item { +impl From for Option { fn from(item: clean::Item) -> Self { let item_type = ItemType::from(&item); let clean::Item { source, name, attrs, - kind: inner, + kind, visibility, def_id, stability: _, deprecation, } = item; - Item { - id: def_id.into(), - crate_id: def_id.krate.as_u32(), - name, - stripped: match inner { - clean::StrippedItem(_) => true, - _ => false, - }, - source: source.into(), - visibility: visibility.into(), - docs: attrs.collapsed_doc_value().unwrap_or_default(), - links: attrs - .links - .into_iter() - .filter_map(|clean::ItemLink { link, did, .. }| did.map(|did| (link, did.into()))) - .collect(), - attrs: attrs - .other_attrs - .iter() - .map(rustc_ast_pretty::pprust::attribute_to_string) - .collect(), - deprecation: deprecation.map(Into::into), - kind: item_type.into(), - inner: inner.into(), + match kind { + clean::StrippedItem(_) => None, + _ => Some(Item { + id: def_id.into(), + crate_id: def_id.krate.as_u32(), + name, + source: source.into(), + visibility: visibility.into(), + docs: attrs.collapsed_doc_value().unwrap_or_default(), + links: attrs + .links + .into_iter() + .filter_map(|clean::ItemLink { link, did, .. }| { + did.map(|did| (link, did.into())) + }) + .collect(), + attrs: attrs + .other_attrs + .iter() + .map(rustc_ast_pretty::pprust::attribute_to_string) + .collect(), + deprecation: deprecation.map(Into::into), + kind: item_type.into(), + inner: kind.into(), + }), } } } @@ -194,10 +195,7 @@ impl From for ItemEnum { impl From for Module { fn from(module: clean::Module) -> Self { - Module { - is_crate: module.is_crate, - items: module.items.into_iter().map(|i| i.def_id.into()).collect(), - } + Module { is_crate: module.is_crate, items: ids(module.items) } } } @@ -208,7 +206,7 @@ impl From for Struct { struct_type: struct_type.into(), generics: generics.into(), fields_stripped, - fields: fields.into_iter().map(|i| i.def_id.into()).collect(), + fields: ids(fields), impls: Vec::new(), // Added in JsonRenderer::item } } @@ -221,7 +219,7 @@ impl From for Struct { struct_type: struct_type.into(), generics: generics.into(), fields_stripped, - fields: fields.into_iter().map(|i| i.def_id.into()).collect(), + fields: ids(fields), impls: Vec::new(), // Added in JsonRenderer::item } } @@ -407,7 +405,7 @@ impl From for Trait { Trait { is_auto, is_unsafe: unsafety == rustc_hir::Unsafety::Unsafe, - items: items.into_iter().map(|i| i.def_id.into()).collect(), + items: ids(items), generics: generics.into(), bounds: bounds.into_iter().map(Into::into).collect(), implementors: Vec::new(), // Added in JsonRenderer::item @@ -434,7 +432,7 @@ impl From for Impl { provided_trait_methods: provided_trait_methods.into_iter().collect(), trait_: trait_.map(Into::into), for_: for_.into(), - items: items.into_iter().map(|i| i.def_id.into()).collect(), + items: ids(items), negative: polarity == Some(clean::ImplPolarity::Negative), synthetic, blanket_impl: blanket_impl.map(Into::into), @@ -460,7 +458,7 @@ impl From for Enum { Enum { generics: generics.into(), variants_stripped, - variants: variants.into_iter().map(|i| i.def_id.into()).collect(), + variants: ids(variants), impls: Vec::new(), // Added in JsonRenderer::item } } @@ -473,7 +471,7 @@ impl From for Struct { struct_type: struct_type.into(), generics: Default::default(), fields_stripped, - fields: fields.into_iter().map(|i| i.def_id.into()).collect(), + fields: ids(fields), impls: Vec::new(), } } @@ -485,7 +483,7 @@ impl From for Variant { match variant.kind { CLike => Variant::Plain, Tuple(t) => Variant::Tuple(t.into_iter().map(Into::into).collect()), - Struct(s) => Variant::Struct(s.fields.into_iter().map(|i| i.def_id.into()).collect()), + Struct(s) => Variant::Struct(ids(s.fields)), } } } @@ -594,3 +592,7 @@ impl From for ItemKind { } } } + +fn ids(items: impl IntoIterator) -> Vec { + items.into_iter().filter(|x| !x.is_stripped()).map(|i| i.def_id.into()).collect() +} diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 20a8de144ad..c080ad21c0f 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -99,7 +99,6 @@ impl JsonRenderer { .0 .last() .map(Clone::clone), - stripped: false, visibility: types::Visibility::Public, kind: types::ItemKind::Trait, inner: types::ItemEnum::TraitItem(trait_item.clone().into()), @@ -144,16 +143,17 @@ impl FormatRenderer for JsonRenderer { item.kind.inner_items().for_each(|i| self.item(i.clone(), cache).unwrap()); let id = item.def_id; - let mut new_item: types::Item = item.into(); - if let types::ItemEnum::TraitItem(ref mut t) = new_item.inner { - t.implementors = self.get_trait_implementors(id, cache) - } else if let types::ItemEnum::StructItem(ref mut s) = new_item.inner { - s.impls = self.get_impls(id, cache) - } else if let types::ItemEnum::EnumItem(ref mut e) = new_item.inner { - e.impls = self.get_impls(id, cache) + if let Some(mut new_item) = item.into(): Option { + if let types::ItemEnum::TraitItem(ref mut t) = new_item.inner { + t.implementors = self.get_trait_implementors(id, cache) + } else if let types::ItemEnum::StructItem(ref mut s) = new_item.inner { + s.impls = self.get_impls(id, cache) + } else if let types::ItemEnum::EnumItem(ref mut e) = new_item.inner { + e.impls = self.get_impls(id, cache) + } + self.index.borrow_mut().insert(id.into(), new_item); } - self.index.borrow_mut().insert(id.into(), new_item); Ok(()) } diff --git a/src/librustdoc/json/types.rs b/src/librustdoc/json/types.rs index 62705affafc..10bf2a2acc5 100644 --- a/src/librustdoc/json/types.rs +++ b/src/librustdoc/json/types.rs @@ -62,9 +62,6 @@ pub struct Item { pub crate_id: u32, /// Some items such as impls don't have names. pub name: Option, - /// Whether this item is meant to be omitted from the generated documentation due to `#doc(hidden)`, - /// because it is private, or because it was inlined. - pub stripped: bool, /// The source location of this item (absent if it came from a macro expansion or inline /// assembly). pub source: Option, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 751f2301053..80a9c3811cf 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -14,6 +14,7 @@ #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(once_cell)] +#![feature(type_ascription)] #![recursion_limit = "256"] #[macro_use] diff --git a/src/test/rustdoc-json/Makefile b/src/test/rustdoc-json/Makefile deleted file mode 100644 index ad517ae95eb..00000000000 --- a/src/test/rustdoc-json/Makefile +++ /dev/null @@ -1,6 +0,0 @@ --include ../tools.mk - -tests: *.rs - $(RUSTDOC) $< -o $(TMPDIR) --output-format json - $(PYTHON) check_missing_items.py $(TMPDIR)/$(basename $<).json - $(PYTHON) compare.py $(basename $<).expected $(TMPDIR)/$(basename $<).json diff --git a/src/test/rustdoc-json/compare.py b/src/test/rustdoc-json/compare.py index afc8066685c..422e48ea39a 100644 --- a/src/test/rustdoc-json/compare.py +++ b/src/test/rustdoc-json/compare.py @@ -34,23 +34,18 @@ def check_subset(expected_main, actual_main, base_dir): def _check_subset(expected, actual, trace): expected_type = type(expected) actual_type = type(actual) + + if actual_type is str: + actual = 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 (str, int, bool) and expected != actual: - if expected_type == str and actual.startswith(base_dir): - if actual.replace(base_dir + "/", "") != expected: - raise SubsetException( - "expected `{}`, got: `{}`".format( - expected, actual.replace(base_dir + "/", "") - ), - trace, - ) - else: - raise SubsetException( - "expected `{}`, got: `{}`".format(expected, actual), 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: diff --git a/src/test/rustdoc-json/structs.expected b/src/test/rustdoc-json/structs.expected index 45b23534bc7..799829de3fd 100644 --- a/src/test/rustdoc-json/structs.expected +++ b/src/test/rustdoc-json/structs.expected @@ -7,7 +7,7 @@ "crate_id": 0, "name": "Unit", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 7, 0 @@ -37,7 +37,7 @@ "crate_id": 0, "name": "1", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 5, 22 @@ -72,7 +72,7 @@ "crate_id": 0, "name": "stuff", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 15, 4 @@ -114,7 +114,7 @@ "crate_id": 0, "name": "WithPrimitives", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 9, 0 @@ -141,18 +141,14 @@ ], "where_predicates": [] }, - "fields_stripped": true, - "fields": [ - "0:13", - "0:14" - ] + "fields_stripped": true } }, "0:14": { "crate_id": 0, "name": "s", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 11, 4 @@ -184,7 +180,7 @@ "crate_id": 0, "name": "things", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 16, 4 @@ -232,7 +228,7 @@ "crate_id": 0, "name": "WithGenerics", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 14, 0 @@ -273,18 +269,14 @@ ], "where_predicates": [] }, - "fields_stripped": true, - "fields": [ - "0:18", - "0:19" - ] + "fields_stripped": true } }, "0:0": { "crate_id": 0, "name": "structs", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 1, 0 @@ -315,7 +307,7 @@ "crate_id": 0, "name": "num", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 10, 4 @@ -340,7 +332,7 @@ "crate_id": 0, "name": "Tuple", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 5, 0 @@ -362,18 +354,14 @@ "params": [], "where_predicates": [] }, - "fields_stripped": true, - "fields": [ - "0:7", - "0:8" - ] + "fields_stripped": true } }, "0:4": { "crate_id": 0, "name": "PlainEmpty", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 3, 0 @@ -403,7 +391,7 @@ "crate_id": 0, "name": "0", "source": { - "filename": "structs.rs", + "filename": "$TEST_BASE_DIR/structs.rs", "begin": [ 5, 17 diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 88cb8544e47..e9089b4b15b 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1565,7 +1565,7 @@ impl<'test> TestCx<'test> { self.compose_and_run_compiler(rustc, None) } - fn document(&self, out_dir: &Path, json: bool) -> ProcRes { + fn document(&self, out_dir: &Path) -> ProcRes { if self.props.build_aux_docs { for rel_ab in &self.props.aux_builds { let aux_testpaths = self.compute_aux_test_paths(rel_ab); @@ -1579,7 +1579,7 @@ impl<'test> TestCx<'test> { }; // Create the directory for the stdout/stderr files. create_dir_all(aux_cx.output_base_dir()).unwrap(); - let auxres = aux_cx.document(out_dir, json); + let auxres = aux_cx.document(out_dir); if !auxres.status.success() { return auxres; } @@ -1601,7 +1601,7 @@ impl<'test> TestCx<'test> { .arg(&self.testpaths.file) .args(&self.props.compile_flags); - if json { + if self.config.mode == RustdocJson { rustdoc.arg("--output-format").arg("json"); } @@ -2336,7 +2336,7 @@ impl<'test> TestCx<'test> { let _ = fs::remove_dir_all(&out_dir); create_dir_all(&out_dir).unwrap(); - let proc_res = self.document(&out_dir, false); + let proc_res = self.document(&out_dir); if !proc_res.status.success() { self.fatal_proc_rec("rustdoc failed!", &proc_res); } @@ -2392,7 +2392,7 @@ impl<'test> TestCx<'test> { rustc.arg("-L").arg(&new_rustdoc.aux_output_dir_name()); new_rustdoc.build_all_auxiliary(&mut rustc); - let proc_res = new_rustdoc.document(&compare_dir, false); + let proc_res = new_rustdoc.document(&compare_dir); if !proc_res.status.success() { proc_res.fatal(Some("failed to run nightly rustdoc"), || ()); } @@ -2482,7 +2482,7 @@ impl<'test> TestCx<'test> { let _ = fs::remove_dir_all(&out_dir); create_dir_all(&out_dir).unwrap(); - let proc_res = self.document(&out_dir, true); + let proc_res = self.document(&out_dir); if !proc_res.status.success() { self.fatal_proc_rec("rustdoc failed!", &proc_res); } @@ -3052,7 +3052,7 @@ impl<'test> TestCx<'test> { if let Some(nodejs) = &self.config.nodejs { let out_dir = self.output_base_dir(); - self.document(&out_dir, false); + self.document(&out_dir); let root = self.config.find_rust_src_root().unwrap(); let file_stem = -- cgit 1.4.1-3-g733a5 From d619271f2bf90a4fce327ef868920ef39ee22a4c Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 2 Dec 2020 20:41:20 +0000 Subject: Normalize windows path seperators. --- src/test/rustdoc-json/compare.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/test/rustdoc-json') diff --git a/src/test/rustdoc-json/compare.py b/src/test/rustdoc-json/compare.py index 422e48ea39a..b0c5b16a197 100644 --- a/src/test/rustdoc-json/compare.py +++ b/src/test/rustdoc-json/compare.py @@ -4,7 +4,8 @@ # 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`. +# and then create `yourtest.expected` by stripping unnecessary details from `yourtest.json`. If +# you're on windows, replace `\` with `/`. import copy import sys @@ -36,7 +37,7 @@ def check_subset(expected_main, actual_main, base_dir): actual_type = type(actual) if actual_type is str: - actual = actual.replace(base_dir, "$TEST_BASE_DIR") + actual = normalize(actual).replace(base_dir, "$TEST_BASE_DIR") if expected_type is not actual_type: raise SubsetException( @@ -118,9 +119,11 @@ def main(expected_fpath, actual_fpath, base_dir): 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], sys.argv[3]) + main(sys.argv[1], sys.argv[2], normalize(sys.argv[3])) -- cgit 1.4.1-3-g733a5