From fdf7ec8694f6d5df77e39434415072a450e54c3e Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Mon, 12 Sep 2022 14:56:04 +0100 Subject: [PATCH 01/18] Rustdoc-Json: Fix Type docs. Primitive doesn't include Array/Slice/Tuple, as they are their own variants. ResolvedPath doesn't include Traits, as they appear in the DynTrait variant. --- src/rustdoc-json-types/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 13bafa506e4a6..be1ff286efd10 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -542,12 +542,12 @@ pub enum Term { #[serde(rename_all = "snake_case")] #[serde(tag = "kind", content = "inner")] pub enum Type { - /// Structs, enums, and traits + /// Structs and enums ResolvedPath(Path), DynTrait(DynTrait), /// Parameterized types Generic(String), - /// Fixed-size numeric types (plus int/usize/float), char, arrays, slices, and tuples + /// Built in numberic (i*, u*, f*) types, bool, and char Primitive(String), /// `extern "ABI" fn` FunctionPointer(Box), From 3c184db386ee3e1c4a9ae1b46836d29d657d5f43 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Mon, 12 Sep 2022 14:03:19 -0700 Subject: [PATCH 02/18] Fix raw-dylib with link_name --- compiler/rustc_metadata/src/native_libs.rs | 7 ++- .../raw-dylib-alt-calling-convention/extern.c | 15 ++++++ .../raw-dylib-alt-calling-convention/lib.rs | 9 ++++ .../output.msvc.txt | 1 + .../output.txt | 2 + src/test/run-make/raw-dylib-c/extern_1.c | 5 ++ src/test/run-make/raw-dylib-c/lib.rs | 3 ++ src/test/run-make/raw-dylib-c/output.txt | 1 + .../raw-dylib-import-name-type/driver.rs | 46 +++++++++++++++++++ .../raw-dylib-import-name-type/extern.c | 38 +++++++++++++++ .../raw-dylib-import-name-type/extern.gnu.def | 3 ++ .../extern.msvc.def | 7 +++ .../raw-dylib-import-name-type/output.txt | 7 +++ 13 files changed, 140 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 3acabc4e69ce6..5017329200ef1 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -495,14 +495,13 @@ impl<'tcx> Collector<'tcx> { } }; - let import_name_type = self - .tcx - .codegen_fn_attrs(item.id.def_id) + let codegen_fn_attrs = self.tcx.codegen_fn_attrs(item.id.def_id); + let import_name_type = codegen_fn_attrs .link_ordinal .map_or(import_name_type, |ord| Some(PeImportNameType::Ordinal(ord))); DllImport { - name: item.ident.name, + name: codegen_fn_attrs.link_name.unwrap_or(item.ident.name), import_name_type, calling_convention, span: item.span, diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/extern.c b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c index 0c4d12af9b2ca..344d4a6bf5a8d 100644 --- a/src/test/run-make/raw-dylib-alt-calling-convention/extern.c +++ b/src/test/run-make/raw-dylib-alt-calling-convention/extern.c @@ -70,6 +70,11 @@ __declspec(dllexport) void __stdcall stdcall_fn_9(uint8_t x, double y) { fflush(stdout); } +__declspec(dllexport) void __stdcall stdcall_fn_10(int i) { + printf("stdcall_fn_10(%d)\n", i); + fflush(stdout); +} + __declspec(dllexport) void __fastcall fastcall_fn_1(int i) { printf("fastcall_fn_1(%d)\n", i); fflush(stdout); @@ -122,6 +127,11 @@ __declspec(dllexport) void __fastcall fastcall_fn_9(uint8_t x, double y) { fflush(stdout); } +__declspec(dllexport) void __fastcall fastcall_fn_10(int i) { + printf("fastcall_fn_10(%d)\n", i); + fflush(stdout); +} + // GCC doesn't support vectorcall: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 #ifdef _MSC_VER __declspec(dllexport) void __vectorcall vectorcall_fn_1(int i) { @@ -175,4 +185,9 @@ __declspec(dllexport) void __vectorcall vectorcall_fn_9(uint8_t x, double y) { printf("vectorcall_fn_9(%d, %.1f)\n", x, y); fflush(stdout); } + +__declspec(dllexport) void __vectorcall vectorcall_fn_10(int i) { + printf("vectorcall_fn_10(%d)\n", i); + fflush(stdout); +} #endif diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs index fe74fbfd2646f..22f222c12c39b 100644 --- a/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs +++ b/src/test/run-make/raw-dylib-alt-calling-convention/lib.rs @@ -32,6 +32,8 @@ extern "stdcall" { fn stdcall_fn_7(a: S2, b: i32); fn stdcall_fn_8(a: S3, b: S3); fn stdcall_fn_9(x: u8, y: f64); + #[link_name = "stdcall_fn_10"] + fn stdcall_fn_10_renamed(i: i32); } #[link(name = "extern", kind = "raw-dylib")] @@ -45,6 +47,8 @@ extern "fastcall" { fn fastcall_fn_7(a: S2, b: i32); fn fastcall_fn_8(a: S3, b: S3); fn fastcall_fn_9(x: u8, y: f64); + #[link_name = "fastcall_fn_10"] + fn fastcall_fn_10_renamed(i: i32); } #[cfg(target_env = "msvc")] @@ -59,6 +63,8 @@ extern "vectorcall" { fn vectorcall_fn_7(a: S2, b: i32); fn vectorcall_fn_8(a: S3, b: S3); fn vectorcall_fn_9(x: u8, y: f64); + #[link_name = "vectorcall_fn_10"] + fn vectorcall_fn_10_renamed(i: i32); } pub fn library_function(run_msvc_only: bool) { @@ -73,6 +79,7 @@ pub fn library_function(run_msvc_only: bool) { stdcall_fn_7(S2 { x: 15, y: 16 }, 3); stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); stdcall_fn_9(1, 3.0); + stdcall_fn_10_renamed(19); fastcall_fn_1(14); fastcall_fn_2(16, 3.5); @@ -81,6 +88,7 @@ pub fn library_function(run_msvc_only: bool) { fastcall_fn_6(Some(&S { x: 10, y: 12 })); fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); fastcall_fn_9(1, 3.0); + fastcall_fn_10_renamed(19); } else { // FIXME: 91167 // rustc generates incorrect code for the calls to fastcall_fn_5 and fastcall_fn_7 @@ -100,6 +108,7 @@ pub fn library_function(run_msvc_only: bool) { vectorcall_fn_7(S2 { x: 15, y: 16 }, 3); vectorcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }); vectorcall_fn_9(1, 3.0); + vectorcall_fn_10_renamed(19); } } } diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt index 9ddd1b1101625..a216835c4b62f 100644 --- a/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt +++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.msvc.txt @@ -9,3 +9,4 @@ vectorcall_fn_6(S { x: 10, y: 12 }) vectorcall_fn_7(S2 { x: 15, y: 16 }, 3) vectorcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) vectorcall_fn_9(1, 3.0) +vectorcall_fn_10(19) diff --git a/src/test/run-make/raw-dylib-alt-calling-convention/output.txt b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt index 348bad63ed0de..7622d31618b80 100644 --- a/src/test/run-make/raw-dylib-alt-calling-convention/output.txt +++ b/src/test/run-make/raw-dylib-alt-calling-convention/output.txt @@ -7,6 +7,7 @@ stdcall_fn_6(S { x: 10, y: 12 }) stdcall_fn_7(S2 { x: 15, y: 16 }, 3) stdcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) stdcall_fn_9(1, 3.0) +stdcall_fn_10(19) fastcall_fn_1(14) fastcall_fn_2(16, 3.5) fastcall_fn_3(3.5) @@ -14,3 +15,4 @@ fastcall_fn_4(1, 2, 3.0) fastcall_fn_6(S { x: 10, y: 12 }) fastcall_fn_8(S3 { x: [1, 2, 3, 4, 5] }, S3 { x: [6, 7, 8, 9, 10] }) fastcall_fn_9(1, 3.0) +fastcall_fn_10(19) diff --git a/src/test/run-make/raw-dylib-c/extern_1.c b/src/test/run-make/raw-dylib-c/extern_1.c index ab1dc3a4105b7..5d695547d0f5e 100644 --- a/src/test/run-make/raw-dylib-c/extern_1.c +++ b/src/test/run-make/raw-dylib-c/extern_1.c @@ -21,3 +21,8 @@ __declspec(dllexport) void extern_fn_with_long_name() { printf("extern_fn_with_long_name; got the rename\n"); fflush(stdout); } + +__declspec(dllexport) void extern_fn_4() { + printf("extern_fn_4\n"); + fflush(stdout); +} diff --git a/src/test/run-make/raw-dylib-c/lib.rs b/src/test/run-make/raw-dylib-c/lib.rs index 74e0d3813d946..005ffcdda5c2e 100644 --- a/src/test/run-make/raw-dylib-c/lib.rs +++ b/src/test/run-make/raw-dylib-c/lib.rs @@ -16,12 +16,15 @@ pub fn library_function() { fn extern_fn_2(); fn print_extern_variable(); static mut extern_variable: i32; + #[link_name = "extern_fn_4"] + fn extern_fn_4_renamed(); } unsafe { extern_fn_1(); extern_fn_2(); extern_fn_3(); + extern_fn_4_renamed(); extern_variable = 42; print_extern_variable(); extern_variable = -42; diff --git a/src/test/run-make/raw-dylib-c/output.txt b/src/test/run-make/raw-dylib-c/output.txt index cd9fe47bee473..cc970cef7bcaa 100644 --- a/src/test/run-make/raw-dylib-c/output.txt +++ b/src/test/run-make/raw-dylib-c/output.txt @@ -1,5 +1,6 @@ extern_fn_1 extern_fn_2; didn't get the rename extern_fn_3 +extern_fn_4 extern_variable value: 42 extern_variable value: -42 diff --git a/src/test/run-make/raw-dylib-import-name-type/driver.rs b/src/test/run-make/raw-dylib-import-name-type/driver.rs index 74e9a89fbdf32..a38849fc8130a 100644 --- a/src/test/run-make/raw-dylib-import-name-type/driver.rs +++ b/src/test/run-make/raw-dylib-import-name-type/driver.rs @@ -1,8 +1,11 @@ #![feature(raw_dylib)] +#![feature(abi_vectorcall)] #[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] extern "C" { fn cdecl_fn_undecorated(i: i32); + #[link_name = "cdecl_fn_undecorated2"] + fn cdecl_fn_undecorated_renamed(i: i32); static mut extern_variable_undecorated: i32; } @@ -21,6 +24,8 @@ extern "C" { #[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] extern "stdcall" { fn stdcall_fn_undecorated(i: i32); + #[link_name = "stdcall_fn_undecorated2"] + fn stdcall_fn_undecorated_renamed(i: i32); } #[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] @@ -36,6 +41,8 @@ extern "stdcall" { #[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] extern "fastcall" { fn fastcall_fn_undecorated(i: i32); + #[link_name = "fastcall_fn_undecorated2"] + fn fastcall_fn_undecorated_renamed(i: i32); } #[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] @@ -48,6 +55,26 @@ extern "fastcall" { fn fastcall_fn_decorated(i: i32); } +#[cfg(target_env = "msvc")] +#[link(name = "extern", kind = "raw-dylib", import_name_type = "undecorated")] +extern "vectorcall" { + fn vectorcall_fn_undecorated(i: i32); + #[link_name = "vectorcall_fn_undecorated2"] + fn vectorcall_fn_undecorated_renamed(i: i32); +} + +#[cfg(target_env = "msvc")] +#[link(name = "extern", kind = "raw-dylib", import_name_type = "noprefix")] +extern "vectorcall" { + fn vectorcall_fn_noprefix(i: i32); +} + +#[cfg(target_env = "msvc")] +#[link(name = "extern", kind = "raw-dylib", import_name_type = "decorated")] +extern "vectorcall" { + fn vectorcall_fn_decorated(i: i32); +} + #[link(name = "extern", kind = "raw-dylib")] extern { fn print_extern_variable_undecorated(); @@ -58,14 +85,17 @@ extern { pub fn main() { unsafe { cdecl_fn_undecorated(1); + cdecl_fn_undecorated_renamed(10); cdecl_fn_noprefix(2); cdecl_fn_decorated(3); stdcall_fn_undecorated(4); + stdcall_fn_undecorated_renamed(14); stdcall_fn_noprefix(5); stdcall_fn_decorated(6); fastcall_fn_undecorated(7); + fastcall_fn_undecorated_renamed(17); fastcall_fn_noprefix(8); fastcall_fn_decorated(9); @@ -75,5 +105,21 @@ pub fn main() { print_extern_variable_noprefix(); extern_variable_decorated = 44; print_extern_variable_decorated(); + + // GCC doesn't support vectorcall: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 + #[cfg(target_env = "msvc")] + { + vectorcall_fn_undecorated(10); + vectorcall_fn_undecorated_renamed(20); + vectorcall_fn_noprefix(11); + vectorcall_fn_decorated(12); + } + #[cfg(not(target_env = "msvc"))] + { + println!("vectorcall_fn_undecorated(10)"); + println!("vectorcall_fn_undecorated2(20)"); + println!("vectorcall_fn_noprefix(11)"); + println!("vectorcall_fn_decorated(12)"); + } } } diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.c b/src/test/run-make/raw-dylib-import-name-type/extern.c index 1102158e2494e..195126d51294c 100644 --- a/src/test/run-make/raw-dylib-import-name-type/extern.c +++ b/src/test/run-make/raw-dylib-import-name-type/extern.c @@ -6,6 +6,11 @@ void _cdecl cdecl_fn_undecorated(int i) { fflush(stdout); } +void _cdecl cdecl_fn_undecorated2(int i) { + printf("cdecl_fn_undecorated2(%d)\n", i); + fflush(stdout); +} + void _cdecl cdecl_fn_noprefix(int i) { printf("cdecl_fn_noprefix(%d)\n", i); fflush(stdout); @@ -21,6 +26,11 @@ void __stdcall stdcall_fn_undecorated(int i) { fflush(stdout); } +void __stdcall stdcall_fn_undecorated2(int i) { + printf("stdcall_fn_undecorated2(%d)\n", i); + fflush(stdout); +} + void __stdcall stdcall_fn_noprefix(int i) { printf("stdcall_fn_noprefix(%d)\n", i); fflush(stdout); @@ -36,6 +46,11 @@ void __fastcall fastcall_fn_undecorated(int i) { fflush(stdout); } +void __fastcall fastcall_fn_undecorated2(int i) { + printf("fastcall_fn_undecorated2(%d)\n", i); + fflush(stdout); +} + void __fastcall fastcall_fn_noprefix(int i) { printf("fastcall_fn_noprefix(%d)\n", i); fflush(stdout); @@ -63,3 +78,26 @@ __declspec(dllexport) void print_extern_variable_decorated() { printf("extern_variable_decorated value: %d\n", extern_variable_decorated); fflush(stdout); } + +// GCC doesn't support vectorcall: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89485 +#ifdef _MSC_VER +void __vectorcall vectorcall_fn_undecorated(int i) { + printf("vectorcall_fn_undecorated(%d)\n", i); + fflush(stdout); +} + +void __vectorcall vectorcall_fn_undecorated2(int i) { + printf("vectorcall_fn_undecorated2(%d)\n", i); + fflush(stdout); +} + +void __vectorcall vectorcall_fn_noprefix(int i) { + printf("vectorcall_fn_noprefix(%d)\n", i); + fflush(stdout); +} + +void __vectorcall vectorcall_fn_decorated(int i) { + printf("vectorcall_fn_decorated(%d)\n", i); + fflush(stdout); +} +#endif diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def b/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def index f06ce67e0308b..a523c959a4744 100644 --- a/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def +++ b/src/test/run-make/raw-dylib-import-name-type/extern.gnu.def @@ -1,11 +1,14 @@ LIBRARY extern EXPORTS cdecl_fn_undecorated + cdecl_fn_undecorated2 cdecl_fn_noprefix cdecl_fn_decorated stdcall_fn_undecorated + stdcall_fn_undecorated2 stdcall_fn_noprefix@4 fastcall_fn_undecorated + fastcall_fn_undecorated2 @fastcall_fn_decorated@4 ;ld doesn't handle fully-decorated stdcall, or no-prefix fastcall diff --git a/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def b/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def index 9dc333707cb0a..dbff32d4c90b0 100644 --- a/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def +++ b/src/test/run-make/raw-dylib-import-name-type/extern.msvc.def @@ -1,12 +1,19 @@ LIBRARY extern EXPORTS cdecl_fn_undecorated + cdecl_fn_undecorated2 cdecl_fn_noprefix cdecl_fn_decorated stdcall_fn_undecorated + stdcall_fn_undecorated2 _stdcall_fn_decorated@4 fastcall_fn_undecorated + fastcall_fn_undecorated2 @fastcall_fn_decorated@4 + vectorcall_fn_undecorated + vectorcall_fn_undecorated2 + vectorcall_fn_decorated@@4 + vectorcall_fn_noprefix@@4 ;MSVC doesn't seem to recognize the "no prefix" syntax. stdcall_fn_noprefix@4=_stdcall_fn_noprefix@4 diff --git a/src/test/run-make/raw-dylib-import-name-type/output.txt b/src/test/run-make/raw-dylib-import-name-type/output.txt index 855b20a864575..707faf403aeca 100644 --- a/src/test/run-make/raw-dylib-import-name-type/output.txt +++ b/src/test/run-make/raw-dylib-import-name-type/output.txt @@ -1,12 +1,19 @@ cdecl_fn_undecorated(1) +cdecl_fn_undecorated2(10) cdecl_fn_noprefix(2) cdecl_fn_decorated(3) stdcall_fn_undecorated(4) +stdcall_fn_undecorated2(14) stdcall_fn_noprefix(5) stdcall_fn_decorated(6) fastcall_fn_undecorated(7) +fastcall_fn_undecorated2(17) fastcall_fn_noprefix(8) fastcall_fn_decorated(9) extern_variable_undecorated value: 42 extern_variable_noprefix value: 43 extern_variable_decorated value: 44 +vectorcall_fn_undecorated(10) +vectorcall_fn_undecorated2(20) +vectorcall_fn_noprefix(11) +vectorcall_fn_decorated(12) From 7794ea5854094ffa15e933750196ef7c7e0e2f3b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 13 Sep 2022 10:00:10 +0000 Subject: [PATCH 03/18] Prefer explict closure sig types over expected ones --- compiler/rustc_typeck/src/check/closure.rs | 60 ++++++++++++------- ...e#0}.SimplifyCfg-elaborate-drops.after.mir | 2 +- .../expect-fn-supply-fn.stderr | 12 ++-- ...er-vars-supply-ty-with-bound-region.stderr | 2 +- .../ui/type-alias-impl-trait/closure_args.rs | 16 +++++ .../ui/type-alias-impl-trait/closure_args2.rs | 23 +++++++ .../type-alias-impl-trait/issue-60371.stderr | 2 +- 7 files changed, 87 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/closure_args.rs create mode 100644 src/test/ui/type-alias-impl-trait/closure_args2.rs diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 55cbaf71e7cfd..9b943b160f393 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -30,7 +30,12 @@ struct ExpectedSig<'tcx> { } struct ClosureSignatures<'tcx> { + /// The signature users of the closure see. bound_sig: ty::PolyFnSig<'tcx>, + /// The signature within the function body. + /// This mostly differs in the sense that lifetimes are now early bound and any + /// opaque types from the signature expectation are overriden in case there are + /// explicit hidden types written by the user in the closure signature. liberated_sig: ty::FnSig<'tcx>, } @@ -444,18 +449,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Along the way, it also writes out entries for types that the user // wrote into our typeck results, which are then later used by the privacy // check. - match self.check_supplied_sig_against_expectation( + match self.merge_supplied_sig_with_expectation( hir_id, expr_def_id, decl, body, - &closure_sigs, + closure_sigs, ) { Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), + Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), } - - closure_sigs } fn sig_of_closure_with_mismatched_number_of_arguments( @@ -497,21 +500,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Enforce the user's types against the expectation. See /// `sig_of_closure_with_expectation` for details on the overall /// strategy. - fn check_supplied_sig_against_expectation( + #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))] + fn merge_supplied_sig_with_expectation( &self, hir_id: hir::HirId, expr_def_id: DefId, decl: &hir::FnDecl<'_>, body: &hir::Body<'_>, - expected_sigs: &ClosureSignatures<'tcx>, - ) -> InferResult<'tcx, ()> { + mut expected_sigs: ClosureSignatures<'tcx>, + ) -> InferResult<'tcx, ClosureSignatures<'tcx>> { // Get the signature S that the user gave. // // (See comment on `sig_of_closure_with_expectation` for the // meaning of these letters.) let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); - debug!("check_supplied_sig_against_expectation: supplied_sig={:?}", supplied_sig); + debug!(?supplied_sig); // FIXME(#45727): As discussed in [this comment][c1], naively // forcing equality here actually results in suboptimal error @@ -529,23 +533,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 self.commit_if_ok(|_| { let mut all_obligations = vec![]; + let inputs: Vec<_> = iter::zip( + decl.inputs, + supplied_sig.inputs().skip_binder(), // binder moved to (*) below + ) + .map(|(hir_ty, &supplied_ty)| { + // Instantiate (this part of..) S to S', i.e., with fresh variables. + self.replace_bound_vars_with_fresh_vars( + hir_ty.span, + LateBoundRegionConversionTime::FnCall, + // (*) binder moved to here + supplied_sig.inputs().rebind(supplied_ty), + ) + }) + .collect(); // The liberated version of this signature should be a subtype // of the liberated form of the expectation. for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( - iter::zip( - decl.inputs, - supplied_sig.inputs().skip_binder(), // binder moved to (*) below - ), + iter::zip(decl.inputs, &inputs), expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. ) { - // Instantiate (this part of..) S to S', i.e., with fresh variables. - let supplied_ty = self.replace_bound_vars_with_fresh_vars( - hir_ty.span, - LateBoundRegionConversionTime::FnCall, - supplied_sig.inputs().rebind(supplied_ty), - ); // recreated from (*) above - // Check that E' = S'. let cause = self.misc(hir_ty.span); let InferOk { value: (), obligations } = @@ -564,7 +572,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); - Ok(InferOk { value: (), obligations: all_obligations }) + let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty)); + + expected_sigs.liberated_sig = self.tcx.mk_fn_sig( + inputs, + supplied_output_ty, + expected_sigs.liberated_sig.c_variadic, + hir::Unsafety::Normal, + Abi::RustCall, + ); + + Ok(InferOk { value: expected_sigs, obligations: all_obligations }) }) } diff --git a/src/test/mir-opt/retag.main-{closure#0}.SimplifyCfg-elaborate-drops.after.mir b/src/test/mir-opt/retag.main-{closure#0}.SimplifyCfg-elaborate-drops.after.mir index 44b1a267b3434..96fc7e6493ab9 100644 --- a/src/test/mir-opt/retag.main-{closure#0}.SimplifyCfg-elaborate-drops.after.mir +++ b/src/test/mir-opt/retag.main-{closure#0}.SimplifyCfg-elaborate-drops.after.mir @@ -14,7 +14,7 @@ fn main::{closure#0}(_1: &[closure@main::{closure#0}], _2: &i32) -> &i32 { StorageLive(_3); // scope 0 at $DIR/retag.rs:+1:13: +1:15 _3 = _2; // scope 0 at $DIR/retag.rs:+1:18: +1:19 Retag(_3); // scope 0 at $DIR/retag.rs:+1:18: +1:19 - _0 = _2; // scope 1 at $DIR/retag.rs:+2:9: +2:10 + _0 = &(*_2); // scope 1 at $DIR/retag.rs:+2:9: +2:10 Retag(_0); // scope 1 at $DIR/retag.rs:+2:9: +2:10 StorageDead(_3); // scope 0 at $DIR/retag.rs:+3:5: +3:6 return; // scope 0 at $DIR/retag.rs:+3:6: +3:6 diff --git a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr index 26f47eb684dfc..284fc1c21f5f9 100644 --- a/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr +++ b/src/test/ui/closure-expected-type/expect-fn-supply-fn.stderr @@ -25,8 +25,8 @@ error[E0308]: mismatched types LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {}); | ^ one type is more general than the other | - = note: expected fn pointer `for<'r> fn(&'r u32)` - found fn pointer `fn(&u32)` + = note: expected fn pointer `fn(&u32)` + found fn pointer `for<'r> fn(&'r u32)` error[E0308]: mismatched types --> $DIR/expect-fn-supply-fn.rs:39:50 @@ -34,8 +34,8 @@ error[E0308]: mismatched types LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {}); | ^ one type is more general than the other | - = note: expected fn pointer `fn(&'x u32)` - found fn pointer `for<'r> fn(&'r u32)` + = note: expected fn pointer `for<'r> fn(&'r u32)` + found fn pointer `fn(&u32)` error[E0308]: mismatched types --> $DIR/expect-fn-supply-fn.rs:48:50 @@ -43,8 +43,8 @@ error[E0308]: mismatched types LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| { | ^ one type is more general than the other | - = note: expected fn pointer `fn(&u32)` - found fn pointer `for<'r> fn(&'r u32)` + = note: expected fn pointer `for<'r> fn(&'r u32)` + found fn pointer `fn(&u32)` error: aborting due to 5 previous errors diff --git a/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr b/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr index db7586bee4953..d5432755cfed3 100644 --- a/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr +++ b/src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr @@ -6,7 +6,7 @@ LL | with_closure(|x: u32, y| {}); | help: consider giving this closure parameter an explicit type | -LL | with_closure(|x: u32, y: B| {}); +LL | with_closure(|x: u32, y: _| {}); | +++ error: aborting due to previous error diff --git a/src/test/ui/type-alias-impl-trait/closure_args.rs b/src/test/ui/type-alias-impl-trait/closure_args.rs new file mode 100644 index 0000000000000..c5e7af81d3dd0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/closure_args.rs @@ -0,0 +1,16 @@ +// check-pass + +// regression test for https://github.com/rust-lang/rust/issues/100800 + +#![feature(type_alias_impl_trait)] + +trait Anything {} +impl Anything for T {} +type Input = impl Anything; +fn run ()>(f: F, i: Input) { + f(i); +} + +fn main() { + run(|x: u32| {println!("{x}");}, 0); +} diff --git a/src/test/ui/type-alias-impl-trait/closure_args2.rs b/src/test/ui/type-alias-impl-trait/closure_args2.rs new file mode 100644 index 0000000000000..82386c280a8e3 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/closure_args2.rs @@ -0,0 +1,23 @@ +// run-pass + +#![feature(type_alias_impl_trait)] + +trait Foo { + // This was reachable in https://github.com/rust-lang/rust/issues/100800 + fn foo(&self) { unreachable!() } +} +impl Foo for T {} + +struct B; +impl B { + fn foo(&self) {} +} + +type Input = impl Foo; +fn run1(f: F, i: Input) {f(i)} +fn run2(f: F, i: B) {f(i)} + +fn main() { + run1(|x: B| {x.foo()}, B); + run2(|x: B| {x.foo()}, B); +} diff --git a/src/test/ui/type-alias-impl-trait/issue-60371.stderr b/src/test/ui/type-alias-impl-trait/issue-60371.stderr index 082b0f0c30973..d0c04371bd793 100644 --- a/src/test/ui/type-alias-impl-trait/issue-60371.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-60371.stderr @@ -11,7 +11,7 @@ error[E0277]: the trait bound `(): Bug` is not satisfied --> $DIR/issue-60371.rs:10:40 | LL | const FUN: fn() -> Self::Item = || (); - | ^ the trait `Bug` is not implemented for `()` + | ^^ the trait `Bug` is not implemented for `()` | = help: the trait `Bug` is implemented for `&()` From 2506aa0394fa1f03d09a40cdabc5ade1215b0969 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Tue, 23 Aug 2022 14:49:37 +0100 Subject: [PATCH 04/18] jsondoclint: New Tool --- Cargo.lock | 4 ++++ Cargo.toml | 1 + src/bootstrap/test.rs | 2 ++ src/bootstrap/tool.rs | 1 + src/tools/compiletest/src/common.rs | 3 +++ src/tools/compiletest/src/main.rs | 2 ++ src/tools/compiletest/src/runtest.rs | 8 ++++++++ src/tools/jsondoclint/Cargo.toml | 8 ++++++++ src/tools/jsondoclint/src/main.rs | 1 + triagebot.toml | 2 ++ 10 files changed, 32 insertions(+) create mode 100644 src/tools/jsondoclint/Cargo.toml create mode 100644 src/tools/jsondoclint/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4e0e72d34153f..93b622627b417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1891,6 +1891,10 @@ dependencies = [ "shlex", ] +[[package]] +name = "jsondoclint" +version = "0.1.0" + [[package]] name = "jsonpath_lib" version = "0.2.6" diff --git a/Cargo.toml b/Cargo.toml index 5753730053f4c..e49fe5e2f6356 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ members = [ "src/tools/unicode-table-generator", "src/tools/expand-yaml-anchors", "src/tools/jsondocck", + "src/tools/jsondoclint", "src/tools/html-checker", "src/tools/bump-stage0", "src/tools/replace-version-placeholder", diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index f61c95830858f..9d286ddd6d164 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -1341,6 +1341,8 @@ note: if you're sure you want to do this, please open an issue as to why. In the let json_compiler = compiler.with_stage(0); cmd.arg("--jsondocck-path") .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target })); + cmd.arg("--jsondoclint-path") + .arg(builder.ensure(tool::JsonDocLint { compiler: json_compiler, target })); } if mode == "run-make" { diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index c3b04a9bbce3f..7d4ed24b64850 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -376,6 +376,7 @@ bootstrap_tool!( ExpandYamlAnchors, "src/tools/expand-yaml-anchors", "expand-yaml-anchors"; LintDocs, "src/tools/lint-docs", "lint-docs"; JsonDocCk, "src/tools/jsondocck", "jsondocck"; + JsonDocLint, "src/tools/jsondoclint", "jsondoclint"; HtmlChecker, "src/tools/html-checker", "html-checker"; BumpStage0, "src/tools/bump-stage0", "bump-stage0"; ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder"; diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 6f17b9e1be9a3..64df76e27720d 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -203,6 +203,9 @@ pub struct Config { /// The jsondocck executable. pub jsondocck_path: Option, + /// The jsondoclint executable. + pub jsondoclint_path: Option, + /// The LLVM `FileCheck` binary path. pub llvm_filecheck: Option, diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs index 0e2cc52a645bc..38c7b87fc0d9d 100644 --- a/src/tools/compiletest/src/main.rs +++ b/src/tools/compiletest/src/main.rs @@ -64,6 +64,7 @@ pub fn parse_config(args: Vec) -> Config { .optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH") + .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH") .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM") .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind") .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH") @@ -226,6 +227,7 @@ pub fn parse_config(args: Vec) -> Config { rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), jsondocck_path: matches.opt_str("jsondocck-path"), + jsondoclint_path: matches.opt_str("jsondoclint-path"), valgrind_path: matches.opt_str("valgrind-path"), force_valgrind: matches.opt_present("force-valgrind"), run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"), diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index e2afa5ef59020..9cbb6b7c393bd 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2572,6 +2572,14 @@ impl<'test> TestCx<'test> { if !res.status.success() { self.fatal_proc_rec("check_missing_items failed!", &res); } + + let res = self.cmd2procres( + Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), + ); + + if !res.status.success() { + self.fatal_proc_rec("jsondoclint failed!", &res); + } } fn get_lines>( diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml new file mode 100644 index 0000000000000..42bb77fde41af --- /dev/null +++ b/src/tools/jsondoclint/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "jsondoclint" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/src/tools/jsondoclint/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/triagebot.toml b/triagebot.toml index 4b2dcc246e4ee..11caedbb9597a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -132,6 +132,7 @@ trigger_files = [ "src/etc/htmldocck.py", "src/etc/check_missing_items.py", "src/tools/jsondocck", + "src/tools/jsondoclint", "src/tools/rustdoc-gui", "src/tools/rustdoc-js", "src/tools/rustdoc-themes", @@ -147,6 +148,7 @@ trigger_files = [ "src/rustdoc-json-types", "src/test/rustdoc-json", "src/tools/jsondocck", + "src/tools/jsondoclint", ] [autolabel."T-compiler"] From 404b60bf6b2e761188db0e0e5984a4065afaf9cf Mon Sep 17 00:00:00 2001 From: onestacked Date: Wed, 14 Sep 2022 01:42:40 +0200 Subject: [PATCH 05/18] Constify impl Fn* &(mut) Fn* --- library/core/src/ops/function.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index c5a194b7d0a41..827851c62ae7b 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -250,9 +250,10 @@ pub trait FnOnce { mod impls { #[stable(feature = "rust1", since = "1.0.0")] - impl Fn for &F + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + impl const Fn for &F where - F: Fn, + F: ~const Fn, { extern "rust-call" fn call(&self, args: A) -> F::Output { (**self).call(args) @@ -260,9 +261,10 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl FnMut for &F + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + impl const FnMut for &F where - F: Fn, + F: ~const Fn, { extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { (**self).call(args) @@ -270,9 +272,10 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl FnOnce for &F + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + impl const FnOnce for &F where - F: Fn, + F: ~const Fn, { type Output = F::Output; @@ -282,9 +285,10 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl FnMut for &mut F + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + impl const FnMut for &mut F where - F: FnMut, + F: ~const FnMut, { extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output { (*self).call_mut(args) @@ -292,9 +296,10 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl FnOnce for &mut F + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + impl const FnOnce for &mut F where - F: FnMut, + F: ~const FnMut, { type Output = F::Output; extern "rust-call" fn call_once(self, args: A) -> F::Output { From 478c471ce8aabb07e115f4caa18f4f1ca9acbc49 Mon Sep 17 00:00:00 2001 From: onestacked Date: Wed, 14 Sep 2022 15:10:02 +0200 Subject: [PATCH 06/18] Added Tracking Issue number. --- library/core/src/ops/function.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 827851c62ae7b..8fdf22cf6f2c8 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -250,7 +250,7 @@ pub trait FnOnce { mod impls { #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl const Fn for &F where F: ~const Fn, @@ -261,7 +261,7 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl const FnMut for &F where F: ~const Fn, @@ -272,7 +272,7 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl const FnOnce for &F where F: ~const Fn, @@ -285,7 +285,7 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl const FnMut for &mut F where F: ~const FnMut, @@ -296,7 +296,7 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "none")] + #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")] impl const FnOnce for &mut F where F: ~const FnMut, From a7a4fe9ffac69ec96b8ba6ec3da8fd4673dfef6b Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 17:32:39 +0100 Subject: [PATCH 07/18] jsondoclint: Tree Walk Validator --- Cargo.lock | 18 +- src/tools/jsondoclint/Cargo.toml | 4 + src/tools/jsondoclint/src/item_kind.rs | 29 ++ src/tools/jsondoclint/src/main.rs | 34 ++- src/tools/jsondoclint/src/validator.rs | 391 +++++++++++++++++++++++++ 5 files changed, 469 insertions(+), 7 deletions(-) create mode 100644 src/tools/jsondoclint/src/item_kind.rs create mode 100644 src/tools/jsondoclint/src/validator.rs diff --git a/Cargo.lock b/Cargo.lock index 93b622627b417..69f96bcbe63b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.60" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794e162a5eff65c72ef524dfe393eb923c354e350bb78b9c7383df13f3bc142" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "array_tool" @@ -1362,9 +1362,9 @@ dependencies = [ [[package]] name = "fs-err" -version = "2.5.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431" +checksum = "64db3e262960f0662f43a6366788d5f10f7f244b8f7d7d987f560baf5ded5c50" [[package]] name = "fs_extra" @@ -1894,6 +1894,12 @@ dependencies = [ [[package]] name = "jsondoclint" version = "0.1.0" +dependencies = [ + "anyhow", + "fs-err", + "rustdoc-json-types", + "serde_json", +] [[package]] name = "jsonpath_lib" @@ -4449,9 +4455,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "indexmap", "itoa", diff --git a/src/tools/jsondoclint/Cargo.toml b/src/tools/jsondoclint/Cargo.toml index 42bb77fde41af..84a6c7f96c464 100644 --- a/src/tools/jsondoclint/Cargo.toml +++ b/src/tools/jsondoclint/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.62" +fs-err = "2.8.1" +rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" } +serde_json = "1.0.85" diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs new file mode 100644 index 0000000000000..7c6c0b5437928 --- /dev/null +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -0,0 +1,29 @@ +use rustdoc_json_types::ItemEnum; + +pub(crate) fn can_appear_in_mod(kind: &ItemEnum) -> bool { + match kind { + ItemEnum::Module(_) => true, + ItemEnum::ExternCrate { .. } => true, + ItemEnum::Import(_) => true, + ItemEnum::Union(_) => true, + ItemEnum::Struct(_) => true, + ItemEnum::StructField(_) => false, // Only in structs or variants + ItemEnum::Enum(_) => true, + ItemEnum::Variant(_) => false, // Only in enums + ItemEnum::Function(_) => true, + ItemEnum::Trait(_) => true, + ItemEnum::TraitAlias(_) => true, + ItemEnum::Method(_) => false, // Only in traits + ItemEnum::Impl(_) => true, + ItemEnum::Typedef(_) => true, + ItemEnum::OpaqueTy(_) => todo!("IDK"), // On + ItemEnum::Constant(_) => true, + ItemEnum::Static(_) => true, + ItemEnum::ForeignType => todo!("IDK"), + ItemEnum::Macro(_) => true, + ItemEnum::ProcMacro(_) => true, + ItemEnum::PrimitiveType(_) => todo!("IDK"), + ItemEnum::AssocConst { .. } => false, // Trait Only + ItemEnum::AssocType { .. } => false, // Trait only + } +} diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index f328e4d9d04c3..b98601f7d89f9 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -1 +1,33 @@ -fn main() {} +use std::env; + +use anyhow::{anyhow, bail, Result}; +use fs_err as fs; +use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; + +pub(crate) mod item_kind; +mod validator; + +#[derive(Debug)] +struct Error { + message: String, + id: Id, +} + +fn main() -> Result<()> { + let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; + let contents = fs::read_to_string(path)?; + let krate: Crate = serde_json::from_str(&contents)?; + assert_eq!(krate.format_version, FORMAT_VERSION); + + let mut validator = validator::Validator::new(&krate); + validator.check_crate(); + + if !validator.errs.is_empty() { + for err in validator.errs { + eprintln!("`{}`: `{}`", err.id.0, err.message); + } + bail!("Errors validating json"); + } + + Ok(()) +} diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs new file mode 100644 index 0000000000000..2bb63bc3a44e5 --- /dev/null +++ b/src/tools/jsondoclint/src/validator.rs @@ -0,0 +1,391 @@ +use std::collections::HashSet; +use std::hash::Hash; + +use rustdoc_json_types::{ + Constant, Crate, DynTrait, Enum, FnDecl, Function, FunctionPointer, GenericArg, GenericArgs, + GenericBound, GenericParamDef, Generics, Id, Impl, Import, ItemEnum, Method, Module, OpaqueTy, + Path, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding, + TypeBindingKind, Typedef, Union, Variant, WherePredicate, +}; + +use crate::{item_kind::can_appear_in_mod, Error}; + +#[derive(Debug)] +pub struct Validator<'a> { + pub(crate) errs: Vec, + krate: &'a Crate, + seen_ids: HashSet<&'a Id>, + todo: HashSet<&'a Id>, +} + +fn set_remove(set: &mut HashSet) -> Option { + if let Some(id) = set.iter().next() { + let id = id.clone(); + set.take(&id) + } else { + None + } +} + +impl<'a> Validator<'a> { + pub fn new(krate: &'a Crate) -> Self { + Self { krate, errs: Vec::new(), seen_ids: HashSet::new(), todo: HashSet::new() } + } + + pub fn check_crate(&mut self) { + let root = &self.krate.root; + self.add_mod_id(root); + while let Some(id) = set_remove(&mut self.todo) { + self.seen_ids.insert(id); + self.check_item(id); + } + } + + fn check_item(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + match &item.inner { + ItemEnum::Import(x) => self.check_import(x), + ItemEnum::Union(x) => self.check_union(x), + ItemEnum::Struct(x) => self.check_struct(x), + ItemEnum::StructField(x) => self.check_struct_field(x), + ItemEnum::Enum(x) => self.check_enum(x), + ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Trait(x) => self.check_trait(x), + ItemEnum::TraitAlias(x) => self.check_trait_alias(x), + ItemEnum::Method(x) => self.check_method(x), + ItemEnum::Impl(x) => self.check_impl(x), + ItemEnum::Typedef(x) => self.check_typedef(x), + ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), + ItemEnum::Constant(x) => self.check_constant(x), + ItemEnum::Static(x) => self.check_static(x), + ItemEnum::ForeignType => todo!(), + ItemEnum::Macro(x) => self.check_macro(x), + ItemEnum::ProcMacro(x) => self.check_proc_macro(x), + ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), + ItemEnum::Module(x) => self.check_module(x), + + ItemEnum::ExternCrate { .. } => todo!(), + ItemEnum::AssocConst { .. } => todo!(), + ItemEnum::AssocType { .. } => todo!(), + } + } + + // Core checkers + fn check_module(&mut self, module: &'a Module) { + module.items.iter().for_each(|i| self.add_mod_item_id(i)); + } + + fn check_import(&mut self, x: &'a Import) { + if x.glob { + self.add_mod_id(x.id.as_ref().unwrap()); + } else if let Some(id) = &x.id { + self.add_mod_item_id(id); + } + } + + fn check_union(&mut self, x: &'a Union) { + self.check_generics(&x.generics); + x.fields.iter().for_each(|i| self.add_field_id(i)); + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_struct(&mut self, x: &'a Struct) { + self.check_generics(&x.generics); + match &x.kind { + StructKind::Unit => {} + StructKind::Tuple(fields) => fields.iter().flatten().for_each(|f| self.add_field_id(f)), + StructKind::Plain { fields, fields_stripped: _ } => { + fields.iter().for_each(|f| self.add_field_id(f)) + } + } + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_struct_field(&mut self, x: &'a Type) { + self.check_type(x); + } + + fn check_enum(&mut self, x: &'a Enum) { + self.check_generics(&x.generics); + x.variants.iter().for_each(|i| self.add_variant_id(i)); + x.impls.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_variant(&mut self, x: &'a Variant) { + match x { + Variant::Plain(_discriminant) => {} // TODO: Check discriminant value parses + Variant::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)), + Variant::Struct { fields, fields_stripped: _ } => { + fields.iter().for_each(|f| self.add_field_id(f)) + } + } + } + + fn check_function(&mut self, x: &'a Function) { + self.check_generics(&x.generics); + self.check_fn_decl(&x.decl); + } + + fn check_trait(&mut self, x: &'a Trait) { + self.check_generics(&x.generics); + x.items.iter().for_each(|i| self.add_trait_item_id(i)); + x.bounds.iter().for_each(|i| self.check_generic_bound(i)); + x.implementations.iter().for_each(|i| self.add_impl_id(i)); + } + + fn check_trait_alias(&mut self, x: &'a TraitAlias) { + self.check_generics(&x.generics); + x.params.iter().for_each(|i| self.check_generic_bound(i)); + } + + fn check_method(&mut self, x: &'a Method) { + self.check_fn_decl(&x.decl); + self.check_generics(&x.generics); + } + + fn check_impl(&mut self, x: &'a Impl) { + self.check_generics(&x.generics); + if let Some(path) = &x.trait_ { + self.check_path(path); // TODO: Check is trait. + } + self.check_type(&x.for_); + x.items.iter().for_each(|i| self.add_trait_item_id(i)); + if let Some(blanket_impl) = &x.blanket_impl { + self.check_type(blanket_impl) + } + } + + fn check_typedef(&mut self, x: &'a Typedef) { + self.check_generics(&x.generics); + self.check_type(&x.type_); + } + + fn check_opaque_ty(&mut self, x: &'a OpaqueTy) { + x.bounds.iter().for_each(|b| self.check_generic_bound(b)); + self.check_generics(&x.generics); + } + + fn check_constant(&mut self, x: &'a Constant) { + self.check_type(&x.type_); + } + + fn check_static(&mut self, x: &'a Static) { + self.check_type(&x.type_); + } + + fn check_macro(&mut self, _: &'a str) { + // TODO + } + + fn check_proc_macro(&mut self, _: &'a ProcMacro) { + // TODO + } + + fn check_primitive_type(&mut self, _: &'a str) { + // TODO + } + + fn check_generics(&mut self, x: &'a Generics) { + x.params.iter().for_each(|p| self.check_generic_param_def(p)); + x.where_predicates.iter().for_each(|w| self.check_where_predicate(w)); + } + + fn check_type(&mut self, x: &'a Type) { + match x { + Type::ResolvedPath(path) => self.check_path(path), + Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait), + Type::Generic(_) => {} + Type::Primitive(_) => {} + Type::FunctionPointer(fp) => self.check_function_pointer(&**fp), + Type::Tuple(tys) => tys.iter().for_each(|ty| self.check_type(ty)), + Type::Slice(inner) => self.check_type(&**inner), + Type::Array { type_, len: _ } => self.check_type(&**type_), + Type::ImplTrait(bounds) => bounds.iter().for_each(|b| self.check_generic_bound(b)), + Type::Infer => {} + Type::RawPointer { mutable: _, type_ } => self.check_type(&**type_), + Type::BorrowedRef { lifetime: _, mutable: _, type_ } => self.check_type(&**type_), + Type::QualifiedPath { name: _, args, self_type, trait_ } => { + self.check_generic_args(&**args); + self.check_type(&**self_type); + self.check_path(trait_); + } + } + } + + fn check_fn_decl(&mut self, x: &'a FnDecl) { + x.inputs.iter().for_each(|(_name, ty)| self.check_type(ty)); + if let Some(output) = &x.output { + self.check_type(output); + } + } + + fn check_generic_bound(&mut self, x: &'a GenericBound) { + match x { + GenericBound::TraitBound { trait_, generic_params, modifier: _ } => { + self.check_path(trait_); + generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + GenericBound::Outlives(_) => todo!(), + } + } + + fn check_path(&mut self, x: &'a Path) { + self.add_id(&x.id); // TODO: What kinds are allowed here. + if let Some(args) = &x.args { + self.check_generic_args(&**args); + } + } + + fn check_generic_args(&mut self, x: &'a GenericArgs) { + match x { + GenericArgs::AngleBracketed { args, bindings } => { + args.iter().for_each(|arg| self.check_generic_arg(arg)); + bindings.iter().for_each(|bind| self.check_type_binding(bind)); + } + GenericArgs::Parenthesized { inputs, output } => { + inputs.iter().for_each(|ty| self.check_type(ty)); + if let Some(o) = output { + self.check_type(o); + } + } + } + } + + fn check_generic_param_def(&mut self, gpd: &'a GenericParamDef) { + match &gpd.kind { + rustdoc_json_types::GenericParamDefKind::Lifetime { outlives: _ } => {} + rustdoc_json_types::GenericParamDefKind::Type { bounds, default, synthetic: _ } => { + bounds.iter().for_each(|b| self.check_generic_bound(b)); + if let Some(ty) = default { + self.check_type(ty); + } + } + rustdoc_json_types::GenericParamDefKind::Const { type_, default: _ } => { + self.check_type(type_) + } + } + } + + fn check_generic_arg(&mut self, arg: &'a GenericArg) { + match arg { + GenericArg::Lifetime(_) => {} + GenericArg::Type(ty) => self.check_type(ty), + GenericArg::Const(c) => self.check_constant(c), + GenericArg::Infer => {} + } + } + + fn check_type_binding(&mut self, bind: &'a TypeBinding) { + self.check_generic_args(&bind.args); + match &bind.binding { + TypeBindingKind::Equality(term) => self.check_term(term), + TypeBindingKind::Constraint(bounds) => { + bounds.iter().for_each(|b| self.check_generic_bound(b)) + } + } + } + + fn check_term(&mut self, term: &'a Term) { + match term { + Term::Type(ty) => self.check_type(ty), + Term::Constant(con) => self.check_constant(con), + } + } + + fn check_where_predicate(&mut self, w: &'a WherePredicate) { + match w { + WherePredicate::BoundPredicate { type_, bounds, generic_params } => { + self.check_type(type_); + bounds.iter().for_each(|b| self.check_generic_bound(b)); + generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + WherePredicate::RegionPredicate { lifetime: _, bounds } => { + bounds.iter().for_each(|b| self.check_generic_bound(b)); + } + WherePredicate::EqPredicate { lhs, rhs } => { + self.check_type(lhs); + self.check_term(rhs); + } + } + } + + fn check_dyn_trait(&mut self, dyn_trait: &'a DynTrait) { + for pt in &dyn_trait.traits { + self.check_path(&pt.trait_); + pt.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + } + + fn check_function_pointer(&mut self, fp: &'a FunctionPointer) { + self.check_fn_decl(&fp.decl); + fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); + } + + // Aux functions + fn add_id(&mut self, id: &'a Id) { + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } + } + + fn add_field_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting field"); + } + } + + fn add_mod_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::Module(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting module"); + } + } + + fn add_impl_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting impl"); + } + } + + fn add_variant_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if let ItemEnum::StructField(_) = item.inner { + self.add_id(id); + } else { + self.fail(id, "Expecting variant"); + } + } + + /// Add an Id that appeared in a trait + fn add_trait_item_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if !can_appear_in_mod(&item.inner) { + self.fail(id, "Expecting item that can appear in trait"); + } else { + self.add_id(id); + } + } + + /// Add an Id that appeared in a mod + fn add_mod_item_id(&mut self, id: &'a Id) { + let item = &self.krate.index[id]; + if can_appear_in_mod(&item.inner) { + self.add_id(id); + } else { + self.fail(id, "Expecting item that can appear in trait"); + } + } + + fn fail(&mut self, id: &Id, msg: &str) { + self.errs.push(Error { id: id.clone(), message: msg.to_string() }); + } +} From bb1911db393047382ae040c23598e25984244644 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 19:44:32 +0100 Subject: [PATCH 08/18] jsondoclint: Add `Kind` abstraction --- src/tools/jsondoclint/src/item_kind.rs | 205 +++++++++++++++++++++---- src/tools/jsondoclint/src/validator.rs | 74 +++++---- 2 files changed, 213 insertions(+), 66 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 7c6c0b5437928..15866ab6950f3 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -1,29 +1,180 @@ -use rustdoc_json_types::ItemEnum; - -pub(crate) fn can_appear_in_mod(kind: &ItemEnum) -> bool { - match kind { - ItemEnum::Module(_) => true, - ItemEnum::ExternCrate { .. } => true, - ItemEnum::Import(_) => true, - ItemEnum::Union(_) => true, - ItemEnum::Struct(_) => true, - ItemEnum::StructField(_) => false, // Only in structs or variants - ItemEnum::Enum(_) => true, - ItemEnum::Variant(_) => false, // Only in enums - ItemEnum::Function(_) => true, - ItemEnum::Trait(_) => true, - ItemEnum::TraitAlias(_) => true, - ItemEnum::Method(_) => false, // Only in traits - ItemEnum::Impl(_) => true, - ItemEnum::Typedef(_) => true, - ItemEnum::OpaqueTy(_) => todo!("IDK"), // On - ItemEnum::Constant(_) => true, - ItemEnum::Static(_) => true, - ItemEnum::ForeignType => todo!("IDK"), - ItemEnum::Macro(_) => true, - ItemEnum::ProcMacro(_) => true, - ItemEnum::PrimitiveType(_) => todo!("IDK"), - ItemEnum::AssocConst { .. } => false, // Trait Only - ItemEnum::AssocType { .. } => false, // Trait only +use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary}; + +// We want a univeral way to represent an `ItemEnum` or `ItemKind` + +#[derive(Debug)] +pub(crate) enum Kind { + Module, + ExternCrate, + Import, + Struct, + StructField, + Union, + Enum, + Variant, + Function, + Typedef, + OpaqueTy, + Constant, + Trait, + TraitAlias, + Method, + Impl, + Static, + ForeignType, + Macro, + ProcAttribute, + ProcDerive, + AssocConst, + AssocType, + Primitive, + Keyword, + // Not in ItemKind + ProcMacro, + PrimitiveType, +} + +impl Kind { + pub fn can_appear_in_mod(self) -> bool { + use Kind::*; + match self { + Module => true, + ExternCrate => true, + Import => true, + Union => true, + Struct => true, + Enum => true, + Function => true, + Trait => true, + TraitAlias => true, + Impl => true, + Typedef => true, + Constant => true, + Static => true, + Macro => true, + ProcMacro => true, + + ForeignType => todo!("IDK"), + Keyword => todo!("IDK"), + OpaqueTy => todo!("IDK"), + Primitive => todo!("IDK"), + PrimitiveType => todo!("IDK"), + ProcAttribute => todo!("IDK"), + ProcDerive => todo!("IDK"), + + // Only in traits + AssocConst => false, + AssocType => false, + Method => false, + + StructField => false, // Only in structs or variants + Variant => false, // Only in enums + } + } + + pub fn can_appear_in_trait(self) -> bool { + match self { + Kind::AssocConst => true, + Kind::AssocType => true, + Kind::Method => true, + + Kind::Module => false, + Kind::ExternCrate => false, + Kind::Import => false, + Kind::Struct => false, + Kind::StructField => false, + Kind::Union => false, + Kind::Enum => false, + Kind::Variant => false, + Kind::Function => false, + Kind::Typedef => false, + Kind::OpaqueTy => false, + Kind::Constant => false, + Kind::Trait => false, + Kind::TraitAlias => false, + Kind::Impl => false, + Kind::Static => false, + Kind::ForeignType => false, + Kind::Macro => false, + Kind::ProcAttribute => false, + Kind::ProcDerive => false, + Kind::Primitive => false, + Kind::Keyword => false, + Kind::ProcMacro => false, + Kind::PrimitiveType => false, + } + } + + pub fn is_struct_field(self) -> bool { + matches!(self, Kind::StructField) + } + pub fn is_module(self) -> bool { + matches!(self, Kind::Module) + } + pub fn is_impl(self) -> bool { + matches!(self, Kind::Impl) + } + pub fn is_variant(self) -> bool { + matches!(self, Kind::Variant) + } + + pub fn from_item(i: &Item) -> Self { + use Kind::*; + match i.inner { + ItemEnum::Module(_) => Module, + ItemEnum::Import(_) => Import, + ItemEnum::Union(_) => Union, + ItemEnum::Struct(_) => Struct, + ItemEnum::StructField(_) => StructField, + ItemEnum::Enum(_) => Enum, + ItemEnum::Variant(_) => Variant, + ItemEnum::Function(_) => Function, + ItemEnum::Trait(_) => Trait, + ItemEnum::TraitAlias(_) => TraitAlias, + ItemEnum::Method(_) => Method, + ItemEnum::Impl(_) => Impl, + ItemEnum::Typedef(_) => Typedef, + ItemEnum::OpaqueTy(_) => OpaqueTy, + ItemEnum::Constant(_) => Constant, + ItemEnum::Static(_) => Static, + ItemEnum::Macro(_) => Macro, + ItemEnum::ProcMacro(_) => ProcMacro, + ItemEnum::PrimitiveType(_) => PrimitiveType, + ItemEnum::ForeignType => ForeignType, + ItemEnum::ExternCrate { .. } => ExternCrate, + ItemEnum::AssocConst { .. } => AssocConst, + ItemEnum::AssocType { .. } => AssocType, + } + } + + pub fn from_summary(s: &ItemSummary) -> Self { + use Kind::*; + match s.kind { + ItemKind::AssocConst => AssocConst, + ItemKind::AssocType => AssocType, + ItemKind::Constant => Constant, + ItemKind::Enum => Enum, + ItemKind::ExternCrate => ExternCrate, + ItemKind::ForeignType => ForeignType, + ItemKind::Function => Function, + ItemKind::Impl => Impl, + ItemKind::Import => Import, + ItemKind::Keyword => Keyword, + ItemKind::Macro => Macro, + ItemKind::Method => Method, + ItemKind::Module => Module, + ItemKind::OpaqueTy => OpaqueTy, + ItemKind::Primitive => Primitive, + ItemKind::ProcAttribute => ProcAttribute, + ItemKind::ProcDerive => ProcDerive, + ItemKind::Static => Static, + ItemKind::Struct => Struct, + ItemKind::StructField => StructField, + ItemKind::Trait => Trait, + ItemKind::TraitAlias => TraitAlias, + ItemKind::Typedef => Typedef, + ItemKind::Union => Union, + ItemKind::Variant => Variant, + } } } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 2bb63bc3a44e5..b0e12479f92c6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -8,7 +8,7 @@ use rustdoc_json_types::{ TypeBindingKind, Typedef, Union, Variant, WherePredicate, }; -use crate::{item_kind::can_appear_in_mod, Error}; +use crate::{item_kind::Kind, Error}; #[derive(Debug)] pub struct Validator<'a> { @@ -329,63 +329,59 @@ impl<'a> Validator<'a> { } } - fn add_field_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); + fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) { + if let Some(kind) = self.kind_of(id) { + if valid(kind) { + self.add_id(id); + } else { + self.fail_expecting(id, expected); + } } else { - self.fail(id, "Expecting field"); + self.fail(id, "Not found") } } - fn add_mod_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::Module(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting module"); - } + fn add_field_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_struct_field, "StructField"); } + fn add_mod_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_module, "Module"); + } fn add_impl_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting impl"); - } + self.add_id_checked(id, Kind::is_impl, "Impl"); } fn add_variant_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if let ItemEnum::StructField(_) = item.inner { - self.add_id(id); - } else { - self.fail(id, "Expecting variant"); - } + self.add_id_checked(id, Kind::is_variant, "Variant"); } /// Add an Id that appeared in a trait fn add_trait_item_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if !can_appear_in_mod(&item.inner) { - self.fail(id, "Expecting item that can appear in trait"); - } else { - self.add_id(id); - } + self.add_id_checked(id, Kind::can_appear_in_trait, "Trait inner item"); } /// Add an Id that appeared in a mod fn add_mod_item_id(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - if can_appear_in_mod(&item.inner) { - self.add_id(id); - } else { - self.fail(id, "Expecting item that can appear in trait"); - } + self.add_id_checked(id, Kind::can_appear_in_mod, "Module inner item") + } + + fn fail_expecting(&mut self, id: &Id, expected: &str) { + let kind = self.kind_of(id).unwrap(); // We know it has a kind, as it's wrong. + self.fail(id, format!("Expected {expected} but found {kind:?}")); } - fn fail(&mut self, id: &Id, msg: &str) { - self.errs.push(Error { id: id.clone(), message: msg.to_string() }); + fn fail(&mut self, id: &Id, message: impl Into) { + self.errs.push(Error { id: id.clone(), message: message.into() }); + } + + fn kind_of(&mut self, id: &Id) -> Option { + if let Some(item) = self.krate.index.get(id) { + Some(Kind::from_item(item)) + } else if let Some(summary) = self.krate.paths.get(id) { + Some(Kind::from_summary(summary)) + } else { + None + } } } From 5f1bc6fc5e362c5955f6a06a7cf21e62b97c86c7 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 20:09:37 +0100 Subject: [PATCH 09/18] jsondocck: Better errors --- src/tools/jsondoclint/src/item_kind.rs | 19 +++--- src/tools/jsondoclint/src/main.rs | 13 +++- src/tools/jsondoclint/src/validator.rs | 83 ++++++++++++++++---------- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 15866ab6950f3..f4153245e0b7f 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -31,7 +31,6 @@ pub(crate) enum Kind { Keyword, // Not in ItemKind ProcMacro, - PrimitiveType, } impl Kind { @@ -54,13 +53,13 @@ impl Kind { Macro => true, ProcMacro => true, - ForeignType => todo!("IDK"), - Keyword => todo!("IDK"), - OpaqueTy => todo!("IDK"), - Primitive => todo!("IDK"), - PrimitiveType => todo!("IDK"), - ProcAttribute => todo!("IDK"), - ProcDerive => todo!("IDK"), + // FIXME(adotinthevoid): I'm not sure if these are corrent + ForeignType => false, + Keyword => false, + OpaqueTy => false, + Primitive => false, + ProcAttribute => false, + ProcDerive => false, // Only in traits AssocConst => false, @@ -101,7 +100,6 @@ impl Kind { Kind::Primitive => false, Kind::Keyword => false, Kind::ProcMacro => false, - Kind::PrimitiveType => false, } } @@ -139,7 +137,8 @@ impl Kind { ItemEnum::Static(_) => Static, ItemEnum::Macro(_) => Macro, ItemEnum::ProcMacro(_) => ProcMacro, - ItemEnum::PrimitiveType(_) => PrimitiveType, + // https://github.com/rust-lang/rust/issues/100961 + ItemEnum::PrimitiveType(_) => Primitive, ItemEnum::ForeignType => ForeignType, ItemEnum::ExternCrate { .. } => ExternCrate, ItemEnum::AssocConst { .. } => AssocConst, diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index b98601f7d89f9..4df8fbc29a2bb 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -9,10 +9,16 @@ mod validator; #[derive(Debug)] struct Error { - message: String, + kind: ErrorKind, id: Id, } +#[derive(Debug)] +enum ErrorKind { + NotFound, + Custom(String), +} + fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; let contents = fs::read_to_string(path)?; @@ -24,7 +30,10 @@ fn main() -> Result<()> { if !validator.errs.is_empty() { for err in validator.errs { - eprintln!("`{}`: `{}`", err.id.0, err.message); + match err.kind { + ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0), + ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), + } } bail!("Errors validating json"); } diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index b0e12479f92c6..06b114f6c2fe6 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -8,13 +8,14 @@ use rustdoc_json_types::{ TypeBindingKind, Typedef, Union, Variant, WherePredicate, }; -use crate::{item_kind::Kind, Error}; +use crate::{item_kind::Kind, Error, ErrorKind}; #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, krate: &'a Crate, seen_ids: HashSet<&'a Id>, + missing_ids: HashSet<&'a Id>, todo: HashSet<&'a Id>, } @@ -29,7 +30,13 @@ fn set_remove(set: &mut HashSet) -> Option { impl<'a> Validator<'a> { pub fn new(krate: &'a Crate) -> Self { - Self { krate, errs: Vec::new(), seen_ids: HashSet::new(), todo: HashSet::new() } + Self { + krate, + errs: Vec::new(), + seen_ids: HashSet::new(), + todo: HashSet::new(), + missing_ids: HashSet::new(), + } } pub fn check_crate(&mut self) { @@ -42,32 +49,39 @@ impl<'a> Validator<'a> { } fn check_item(&mut self, id: &'a Id) { - let item = &self.krate.index[id]; - match &item.inner { - ItemEnum::Import(x) => self.check_import(x), - ItemEnum::Union(x) => self.check_union(x), - ItemEnum::Struct(x) => self.check_struct(x), - ItemEnum::StructField(x) => self.check_struct_field(x), - ItemEnum::Enum(x) => self.check_enum(x), - ItemEnum::Variant(x) => self.check_variant(x), - ItemEnum::Function(x) => self.check_function(x), - ItemEnum::Trait(x) => self.check_trait(x), - ItemEnum::TraitAlias(x) => self.check_trait_alias(x), - ItemEnum::Method(x) => self.check_method(x), - ItemEnum::Impl(x) => self.check_impl(x), - ItemEnum::Typedef(x) => self.check_typedef(x), - ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), - ItemEnum::Constant(x) => self.check_constant(x), - ItemEnum::Static(x) => self.check_static(x), - ItemEnum::ForeignType => todo!(), - ItemEnum::Macro(x) => self.check_macro(x), - ItemEnum::ProcMacro(x) => self.check_proc_macro(x), - ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), - ItemEnum::Module(x) => self.check_module(x), - - ItemEnum::ExternCrate { .. } => todo!(), - ItemEnum::AssocConst { .. } => todo!(), - ItemEnum::AssocType { .. } => todo!(), + if let Some(item) = &self.krate.index.get(id) { + match &item.inner { + ItemEnum::Import(x) => self.check_import(x), + ItemEnum::Union(x) => self.check_union(x), + ItemEnum::Struct(x) => self.check_struct(x), + ItemEnum::StructField(x) => self.check_struct_field(x), + ItemEnum::Enum(x) => self.check_enum(x), + ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Function(x) => self.check_function(x), + ItemEnum::Trait(x) => self.check_trait(x), + ItemEnum::TraitAlias(x) => self.check_trait_alias(x), + ItemEnum::Method(x) => self.check_method(x), + ItemEnum::Impl(x) => self.check_impl(x), + ItemEnum::Typedef(x) => self.check_typedef(x), + ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), + ItemEnum::Constant(x) => self.check_constant(x), + ItemEnum::Static(x) => self.check_static(x), + ItemEnum::ForeignType => todo!(), + ItemEnum::Macro(x) => self.check_macro(x), + ItemEnum::ProcMacro(x) => self.check_proc_macro(x), + ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), + ItemEnum::Module(x) => self.check_module(x), + // FIXME: Why don't these have their own structs? + ItemEnum::ExternCrate { .. } => {} + ItemEnum::AssocConst { type_, default: _ } => self.check_type(type_), + ItemEnum::AssocType { generics, bounds, default } => { + self.check_generics(generics); + bounds.iter().for_each(|b| self.check_generic_bound(b)); + if let Some(ty) = default { + self.check_type(ty); + } + } + } } } @@ -226,7 +240,7 @@ impl<'a> Validator<'a> { self.check_path(trait_); generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - GenericBound::Outlives(_) => todo!(), + GenericBound::Outlives(_) => {} } } @@ -337,7 +351,10 @@ impl<'a> Validator<'a> { self.fail_expecting(id, expected); } } else { - self.fail(id, "Not found") + if !self.missing_ids.contains(id) { + self.missing_ids.insert(id); + self.fail(id, ErrorKind::NotFound) + } } } @@ -368,11 +385,11 @@ impl<'a> Validator<'a> { fn fail_expecting(&mut self, id: &Id, expected: &str) { let kind = self.kind_of(id).unwrap(); // We know it has a kind, as it's wrong. - self.fail(id, format!("Expected {expected} but found {kind:?}")); + self.fail(id, ErrorKind::Custom(format!("Expected {expected} but found {kind:?}"))); } - fn fail(&mut self, id: &Id, message: impl Into) { - self.errs.push(Error { id: id.clone(), message: message.into() }); + fn fail(&mut self, id: &Id, kind: ErrorKind) { + self.errs.push(Error { id: id.clone(), kind }); } fn kind_of(&mut self, id: &Id) -> Option { From 41d35a97f9f33e265de53b8f04abe307d7616641 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 24 Aug 2022 23:18:53 +0100 Subject: [PATCH 10/18] jsondocck: Find path to Id's not in index --- src/tools/jsondoclint/src/json_find.rs | 74 ++++++++++++++++++++++++++ src/tools/jsondoclint/src/main.rs | 28 ++++++++-- 2 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 src/tools/jsondoclint/src/json_find.rs diff --git a/src/tools/jsondoclint/src/json_find.rs b/src/tools/jsondoclint/src/json_find.rs new file mode 100644 index 0000000000000..95ea8866609d0 --- /dev/null +++ b/src/tools/jsondoclint/src/json_find.rs @@ -0,0 +1,74 @@ +use std::fmt::Write; + +use serde_json::Value; + +#[derive(Debug, Clone)] +pub enum SelectorPart { + Field(String), + Index(usize), +} + +pub type Selector = Vec; + +pub fn to_jsonpath(sel: &Selector) -> String { + let mut s = String::from("$"); + for part in sel { + match part { + SelectorPart::Field(name) => { + if is_jsonpath_safe(name) { + write!(&mut s, ".{}", name).unwrap(); + } else { + // This is probably wrong in edge cases, but all Id's are + // just ascii alphanumerics, `-` `_`, and `:` + write!(&mut s, "[{name:?}]").unwrap(); + } + } + SelectorPart::Index(idx) => write!(&mut s, "[{idx}]").unwrap(), + } + } + s +} + +fn is_jsonpath_safe(s: &str) -> bool { + s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') +} + +pub fn find_selector(haystack: &Value, needle: &Value) -> Vec { + let mut result = Vec::new(); + let mut sel = Selector::new(); + find_selector_recursive(haystack, needle, &mut result, &mut sel); + result +} + +fn find_selector_recursive( + haystack: &Value, + needle: &Value, + result: &mut Vec, + pos: &mut Selector, +) { + if needle == haystack { + result.push(pos.clone()); + // Haystack cant both contain needle and be needle + } else { + match haystack { + Value::Null => {} + Value::Bool(_) => {} + Value::Number(_) => {} + Value::String(_) => {} + Value::Array(arr) => { + for (idx, subhaystack) in arr.iter().enumerate() { + pos.push(SelectorPart::Index(idx)); + find_selector_recursive(subhaystack, needle, result, pos); + pos.pop().unwrap(); + } + } + Value::Object(obj) => { + for (key, subhaystack) in obj { + pos.push(SelectorPart::Field(key.clone())); + find_selector_recursive(subhaystack, needle, result, pos); + pos.pop().unwrap(); + } + } + } + } +} diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index 4df8fbc29a2bb..1d02482421ba4 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -3,8 +3,10 @@ use std::env; use anyhow::{anyhow, bail, Result}; use fs_err as fs; use rustdoc_json_types::{Crate, Id, FORMAT_VERSION}; +use serde_json::Value; pub(crate) mod item_kind; +mod json_find; mod validator; #[derive(Debug)] @@ -21,8 +23,10 @@ enum ErrorKind { fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; - let contents = fs::read_to_string(path)?; + let contents = fs::read_to_string(&path)?; let krate: Crate = serde_json::from_str(&contents)?; + // TODO: Only load if nessessary. + let krate_json: Value = serde_json::from_str(&contents)?; assert_eq!(krate.format_version, FORMAT_VERSION); let mut validator = validator::Validator::new(&krate); @@ -31,11 +35,29 @@ fn main() -> Result<()> { if !validator.errs.is_empty() { for err in validator.errs { match err.kind { - ErrorKind::NotFound => eprintln!("{}: Not Found", err.id.0), + ErrorKind::NotFound => { + let sels = + json_find::find_selector(&krate_json, &Value::String(err.id.0.clone())); + match &sels[..] { + [] => unreachable!( + "id must be in crate, or it wouldn't be reported as not found" + ), + [sel] => eprintln!( + "{} not in index or paths, but refered to at '{}'", + err.id.0, + json_find::to_jsonpath(&sel) + ), + [sel, ..] => eprintln!( + "{} not in index or paths, but refered to at '{}' and more", + err.id.0, + json_find::to_jsonpath(&sel) + ), + } + } ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg), } } - bail!("Errors validating json"); + bail!("Errors validating json {path}"); } Ok(()) From c98c7cbfa5e9d9f414101f17abd4639b9541c9e7 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Tue, 30 Aug 2022 14:16:38 +0100 Subject: [PATCH 11/18] Primitives can appear in modules. --- src/tools/jsondoclint/src/item_kind.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index f4153245e0b7f..f46da20fd5814 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -52,12 +52,12 @@ impl Kind { Static => true, Macro => true, ProcMacro => true, + Primitive => true, // FIXME(adotinthevoid): I'm not sure if these are corrent ForeignType => false, Keyword => false, OpaqueTy => false, - Primitive => false, ProcAttribute => false, ProcDerive => false, From 5956b56ab251b9aafecd2ad6f431c42e069b3058 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 14:47:25 +0100 Subject: [PATCH 12/18] jsondoclint: Document validator --- src/tools/jsondoclint/src/item_kind.rs | 3 +- src/tools/jsondoclint/src/main.rs | 4 +-- src/tools/jsondoclint/src/validator.rs | 44 +++++++++++++++----------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index f46da20fd5814..b3e88a9081383 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -1,7 +1,6 @@ use rustdoc_json_types::{Item, ItemEnum, ItemKind, ItemSummary}; -// We want a univeral way to represent an `ItemEnum` or `ItemKind` - +/// A univeral way to represent an [`ItemEnum`] or [`ItemKind`] #[derive(Debug)] pub(crate) enum Kind { Module, diff --git a/src/tools/jsondoclint/src/main.rs b/src/tools/jsondoclint/src/main.rs index 1d02482421ba4..70d7a82a57605 100644 --- a/src/tools/jsondoclint/src/main.rs +++ b/src/tools/jsondoclint/src/main.rs @@ -25,8 +25,6 @@ fn main() -> Result<()> { let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?; let contents = fs::read_to_string(&path)?; let krate: Crate = serde_json::from_str(&contents)?; - // TODO: Only load if nessessary. - let krate_json: Value = serde_json::from_str(&contents)?; assert_eq!(krate.format_version, FORMAT_VERSION); let mut validator = validator::Validator::new(&krate); @@ -36,6 +34,8 @@ fn main() -> Result<()> { for err in validator.errs { match err.kind { ErrorKind::NotFound => { + let krate_json: Value = serde_json::from_str(&contents)?; + let sels = json_find::find_selector(&krate_json, &Value::String(err.id.0.clone())); match &sels[..] { diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 06b114f6c2fe6..efe2c165b6c4e 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -10,22 +10,24 @@ use rustdoc_json_types::{ use crate::{item_kind::Kind, Error, ErrorKind}; +/// The Validator walks over the JSON tree, and ensures it is well formed. +/// It is made of several parts. +/// +/// - `check_*`: These take a type from [`rustdoc_json_types`], and check that +/// it is well formed. This involves calling `check_*` functions on +/// fields of that item, and `add_*` functions on [`Id`]s. +/// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if +/// the `Id` is a kind expected in this suituation. #[derive(Debug)] pub struct Validator<'a> { pub(crate) errs: Vec, krate: &'a Crate, + /// Worklist of Ids to check. + todo: HashSet<&'a Id>, + /// Ids that have already been visited, so don't need to be checked again. seen_ids: HashSet<&'a Id>, + /// Ids that have already been reported missing. missing_ids: HashSet<&'a Id>, - todo: HashSet<&'a Id>, -} - -fn set_remove(set: &mut HashSet) -> Option { - if let Some(id) = set.iter().next() { - let id = id.clone(); - set.take(&id) - } else { - None - } } impl<'a> Validator<'a> { @@ -82,6 +84,8 @@ impl<'a> Validator<'a> { } } } + } else { + assert!(self.krate.paths.contains_key(id)); } } @@ -336,17 +340,12 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - // Aux functions - fn add_id(&mut self, id: &'a Id) { - if !self.seen_ids.contains(id) { - self.todo.insert(id); - } - } - fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) { if let Some(kind) = self.kind_of(id) { if valid(kind) { - self.add_id(id); + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } } else { self.fail_expecting(id, expected); } @@ -402,3 +401,12 @@ impl<'a> Validator<'a> { } } } + +fn set_remove(set: &mut HashSet) -> Option { + if let Some(id) = set.iter().next() { + let id = id.clone(); + set.take(&id) + } else { + None + } +} From 393792da8d8a931643baf855369df0a009acdb19 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 14:51:18 +0100 Subject: [PATCH 13/18] Remove check_missing_items.py --- src/etc/check_missing_items.py | 202 --------------------------- src/tools/compiletest/src/runtest.rs | 9 -- triagebot.toml | 2 - 3 files changed, 213 deletions(-) delete mode 100644 src/etc/check_missing_items.py diff --git a/src/etc/check_missing_items.py b/src/etc/check_missing_items.py deleted file mode 100644 index 0026c4cbdca2c..0000000000000 --- a/src/etc/check_missing_items.py +++ /dev/null @@ -1,202 +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. - -# FIXME: Better error output - -import sys -import json - -crate = json.load(open(sys.argv[1], encoding="utf-8")) - - -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["type"]) - 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_path(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_path(path): - args = path["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"]: - term = binding["binding"]["equality"] - if "type" in term: check_type(term["type"]) - elif "const" in term: check_type(term["const"]) - elif "constraint" in binding["binding"]: - for bound in binding["binding"]["constraint"]: - check_generic_bound(bound) - elif "parenthesized" in args: - for input_ty in args["parenthesized"]["inputs"]: - check_type(input_ty) - if args["parenthesized"]["output"]: - check_type(args["parenthesized"]["output"]) - - if path["id"] in crate["index"]: - work_list.add(path["id"]) - elif path["id"] not in crate["paths"]: - print("Id not in index or paths:", path["id"]) - sys.exit(1) - -def check_type(ty): - if ty["kind"] == "resolved_path": - check_path(ty["inner"]) - 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"]["decl"]) - elif ty["kind"] == "qualified_path": - check_type(ty["inner"]["self_type"]) - check_path(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"]["impls"]) - visited - if "tuple" in item["inner"]["kind"]: - work_list |= set(filter(None, item["inner"]["kind"]["tuple"])) - visited - elif "plain" in item["inner"]["kind"]: - work_list |= set(item["inner"]["kind"]["plain"]["fields"]) - 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 field_id in filter(None, item["inner"]["variant_inner"]): - work_list.add(field_id) - elif item["inner"]["variant_kind"] == "struct": - work_list |= set(item["inner"]["variant_inner"]["fields"]) - 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"]["implementations"]) - ) - visited - elif item["kind"] == "impl": - check_generics(item["inner"]["generics"]) - if item["inner"]["trait"]: - check_path(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"]) - elif item["kind"] == "import": - if item["inner"]["id"]: - inner_id = item["inner"]["id"] - assert valid_id(inner_id) - if inner_id in crate["index"] and inner_id not in visited: - work_list.add(inner_id) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 9cbb6b7c393bd..8f289876f7307 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2563,15 +2563,6 @@ impl<'test> TestCx<'test> { let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); json_out.set_extension("json"); - let res = self.cmd2procres( - Command::new(&self.config.python) - .arg(root.join("src/etc/check_missing_items.py")) - .arg(&json_out), - ); - - if !res.status.success() { - self.fatal_proc_rec("check_missing_items failed!", &res); - } let res = self.cmd2procres( Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), diff --git a/triagebot.toml b/triagebot.toml index 11caedbb9597a..12a55fda7ef4d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -130,7 +130,6 @@ trigger_files = [ # Internal tooling "src/etc/htmldocck.py", - "src/etc/check_missing_items.py", "src/tools/jsondocck", "src/tools/jsondoclint", "src/tools/rustdoc-gui", @@ -143,7 +142,6 @@ exclude_labels = [ [autolabel."A-rustdoc-json"] trigger_files = [ - "src/etc/check_missing_items.py", "src/librustdoc/json/", "src/rustdoc-json-types", "src/test/rustdoc-json", From 24c751b2bad4a3cb6020462d3769f735762ef5b3 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:08:56 +0100 Subject: [PATCH 14/18] Rustdoc-Json: Add test for extern_types --- src/test/rustdoc-json/type/extern.rs | 10 ++++++++++ src/tools/jsondoclint/src/item_kind.rs | 2 +- src/tools/jsondoclint/src/validator.rs | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 src/test/rustdoc-json/type/extern.rs diff --git a/src/test/rustdoc-json/type/extern.rs b/src/test/rustdoc-json/type/extern.rs new file mode 100644 index 0000000000000..d287d5ebec543 --- /dev/null +++ b/src/test/rustdoc-json/type/extern.rs @@ -0,0 +1,10 @@ +#![feature(extern_types)] + +extern { + /// No inner information + pub type Foo; +} + +// @is "$.index[*][?(@.docs=='No inner information')].name" '"Foo"' +// @is "$.index[*][?(@.docs=='No inner information')].kind" '"foreign_type"' +// @!has "$.index[*][?(@.docs=='No inner information')].inner" diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index b3e88a9081383..65d7143d13385 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -52,9 +52,9 @@ impl Kind { Macro => true, ProcMacro => true, Primitive => true, + ForeignType => true, // FIXME(adotinthevoid): I'm not sure if these are corrent - ForeignType => false, Keyword => false, OpaqueTy => false, ProcAttribute => false, diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index efe2c165b6c4e..d7bf6fe9e3ca4 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -68,7 +68,7 @@ impl<'a> Validator<'a> { ItemEnum::OpaqueTy(x) => self.check_opaque_ty(x), ItemEnum::Constant(x) => self.check_constant(x), ItemEnum::Static(x) => self.check_static(x), - ItemEnum::ForeignType => todo!(), + ItemEnum::ForeignType => {} // nop ItemEnum::Macro(x) => self.check_macro(x), ItemEnum::ProcMacro(x) => self.check_proc_macro(x), ItemEnum::PrimitiveType(x) => self.check_primitive_type(x), @@ -340,6 +340,13 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } + // TODO: Remove + fn add_id(&mut self, id: &'a Id) { + if !self.seen_ids.contains(id) { + self.todo.insert(id); + } + } + fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) { if let Some(kind) = self.kind_of(id) { if valid(kind) { From 6e21a28ddaf31bf5c6c78d83d051214d2212e803 Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:31:45 +0100 Subject: [PATCH 15/18] jsondoclint: More precise `Path` checks --- src/tools/jsondoclint/src/item_kind.rs | 6 +++++ src/tools/jsondoclint/src/validator.rs | 37 ++++++++++++++++---------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs index 65d7143d13385..ad8e96a0bd81d 100644 --- a/src/tools/jsondoclint/src/item_kind.rs +++ b/src/tools/jsondoclint/src/item_kind.rs @@ -114,6 +114,12 @@ impl Kind { pub fn is_variant(self) -> bool { matches!(self, Kind::Variant) } + pub fn is_trait(self) -> bool { + matches!(self, Kind::Trait) + } + pub fn is_struct_enum_union(self) -> bool { + matches!(self, Kind::Struct | Kind::Enum | Kind::Union) + } pub fn from_item(i: &Item) -> Self { use Kind::*; diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index d7bf6fe9e3ca4..3226ea4f334ef 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -30,6 +30,11 @@ pub struct Validator<'a> { missing_ids: HashSet<&'a Id>, } +enum PathKind { + Trait, + StructEnumUnion, +} + impl<'a> Validator<'a> { pub fn new(krate: &'a Crate) -> Self { Self { @@ -165,7 +170,7 @@ impl<'a> Validator<'a> { fn check_impl(&mut self, x: &'a Impl) { self.check_generics(&x.generics); if let Some(path) = &x.trait_ { - self.check_path(path); // TODO: Check is trait. + self.check_path(path, PathKind::Trait); } self.check_type(&x.for_); x.items.iter().for_each(|i| self.add_trait_item_id(i)); @@ -211,7 +216,7 @@ impl<'a> Validator<'a> { fn check_type(&mut self, x: &'a Type) { match x { - Type::ResolvedPath(path) => self.check_path(path), + Type::ResolvedPath(path) => self.check_path(path, PathKind::StructEnumUnion), Type::DynTrait(dyn_trait) => self.check_dyn_trait(dyn_trait), Type::Generic(_) => {} Type::Primitive(_) => {} @@ -226,7 +231,7 @@ impl<'a> Validator<'a> { Type::QualifiedPath { name: _, args, self_type, trait_ } => { self.check_generic_args(&**args); self.check_type(&**self_type); - self.check_path(trait_); + self.check_path(trait_, PathKind::Trait); } } } @@ -241,15 +246,18 @@ impl<'a> Validator<'a> { fn check_generic_bound(&mut self, x: &'a GenericBound) { match x { GenericBound::TraitBound { trait_, generic_params, modifier: _ } => { - self.check_path(trait_); + self.check_path(trait_, PathKind::Trait); generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } GenericBound::Outlives(_) => {} } } - fn check_path(&mut self, x: &'a Path) { - self.add_id(&x.id); // TODO: What kinds are allowed here. + fn check_path(&mut self, x: &'a Path, kind: PathKind) { + match kind { + PathKind::Trait => self.add_trait_id(&x.id), + PathKind::StructEnumUnion => self.add_struct_enum_union_id(&x.id), + } if let Some(args) = &x.args { self.check_generic_args(&**args); } @@ -330,7 +338,7 @@ impl<'a> Validator<'a> { fn check_dyn_trait(&mut self, dyn_trait: &'a DynTrait) { for pt in &dyn_trait.traits { - self.check_path(&pt.trait_); + self.check_path(&pt.trait_, PathKind::Trait); pt.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } } @@ -340,13 +348,6 @@ impl<'a> Validator<'a> { fp.generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd)); } - // TODO: Remove - fn add_id(&mut self, id: &'a Id) { - if !self.seen_ids.contains(id) { - self.todo.insert(id); - } - } - fn add_id_checked(&mut self, id: &'a Id, valid: fn(Kind) -> bool, expected: &str) { if let Some(kind) = self.kind_of(id) { if valid(kind) { @@ -379,6 +380,14 @@ impl<'a> Validator<'a> { self.add_id_checked(id, Kind::is_variant, "Variant"); } + fn add_trait_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_trait, "Trait"); + } + + fn add_struct_enum_union_id(&mut self, id: &'a Id) { + self.add_id_checked(id, Kind::is_struct_enum_union, "Struct or Enum or Union"); + } + /// Add an Id that appeared in a trait fn add_trait_item_id(&mut self, id: &'a Id) { self.add_id_checked(id, Kind::can_appear_in_trait, "Trait inner item"); From f69a6c2a8018ac69db62446996e815893b70dfcd Mon Sep 17 00:00:00 2001 From: Nixon Enraght-Moony Date: Wed, 14 Sep 2022 15:41:54 +0100 Subject: [PATCH 16/18] jsondoclint: Fix TODO's --- src/tools/jsondoclint/src/validator.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs index 3226ea4f334ef..a0e77127dc2ca 100644 --- a/src/tools/jsondoclint/src/validator.rs +++ b/src/tools/jsondoclint/src/validator.rs @@ -63,7 +63,7 @@ impl<'a> Validator<'a> { ItemEnum::Struct(x) => self.check_struct(x), ItemEnum::StructField(x) => self.check_struct_field(x), ItemEnum::Enum(x) => self.check_enum(x), - ItemEnum::Variant(x) => self.check_variant(x), + ItemEnum::Variant(x) => self.check_variant(x, id), ItemEnum::Function(x) => self.check_function(x), ItemEnum::Trait(x) => self.check_trait(x), ItemEnum::TraitAlias(x) => self.check_trait_alias(x), @@ -135,9 +135,23 @@ impl<'a> Validator<'a> { x.impls.iter().for_each(|i| self.add_impl_id(i)); } - fn check_variant(&mut self, x: &'a Variant) { + fn check_variant(&mut self, x: &'a Variant, id: &'a Id) { match x { - Variant::Plain(_discriminant) => {} // TODO: Check discriminant value parses + Variant::Plain(discr) => { + if let Some(discr) = discr { + if let (Err(_), Err(_)) = + (discr.value.parse::(), discr.value.parse::()) + { + self.fail( + id, + ErrorKind::Custom(format!( + "Failed to parse discriminant value `{}`", + discr.value + )), + ); + } + } + } Variant::Tuple(tys) => tys.iter().flatten().for_each(|t| self.add_field_id(t)), Variant::Struct { fields, fields_stripped: _ } => { fields.iter().for_each(|f| self.add_field_id(f)) @@ -198,15 +212,15 @@ impl<'a> Validator<'a> { } fn check_macro(&mut self, _: &'a str) { - // TODO + // nop } fn check_proc_macro(&mut self, _: &'a ProcMacro) { - // TODO + // nop } fn check_primitive_type(&mut self, _: &'a str) { - // TODO + // nop } fn check_generics(&mut self, x: &'a Generics) { From 4cdf264e6fe58325dcedb2f571cbac4aed095eb7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 9 Sep 2022 15:34:11 +0000 Subject: [PATCH 17/18] cache collect_trait_impl_trait_tys --- compiler/rustc_middle/src/arena.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 3 ++- compiler/rustc_query_impl/src/on_disk_cache.rs | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 9b1fedd0b533c..6bdf5a023b6c3 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -102,7 +102,7 @@ macro_rules! arena_types { [] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>, - [] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap>, + [decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap>, ]); ) } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 0fe22a26cd503..5cc2b7984d709 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -164,7 +164,8 @@ rustc_queries! { query collect_trait_impl_trait_tys(key: DefId) -> Result<&'tcx FxHashMap>, ErrorGuaranteed> { - desc { "better description please" } + desc { "compare an impl and trait method signature, inferring any hidden `impl Trait` types in the process" } + cache_on_disk_if { key.is_local() } separate_provide_extern } diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 5ef95911f562d..0e93f3ce1d646 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -798,6 +798,12 @@ impl<'a, 'tcx> Decodable> for &'tcx FxHashSet } } +impl<'a, 'tcx> Decodable> for &'tcx FxHashMap> { + fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self { + RefDecodable::decode(d) + } +} + impl<'a, 'tcx> Decodable> for &'tcx IndexVec> { From 7f1734cd94eaeda71af5dc807a8ec58b3209d220 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 15 Sep 2022 09:59:27 -0700 Subject: [PATCH 18/18] rustdoc: remove no-op CSS `h1-4 { color: --main-color }` Headers already inherit the font color they need from their parents. This rule dates back to earlier versions of the rustdoc theme, where headers and body had different text colors. https://github.com/rust-lang/rust/blob/68c15be8b5a28297ae58ea030adf49f265e41127/src/librustdoc/html/static/main.css#L72-L98 Nowadays, since the two have exactly the same color (specified by the `--main-color` variable), this rule does nothing. --- src/librustdoc/html/static/css/rustdoc.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 8bfaaf21c8e57..b0dcd36d6b344 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -222,7 +222,6 @@ details.rustdoc-toggle.non-exhaustive > summary::before, font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif; } -h1, h2, h3, h4, a#toggle-all-docs, a.anchor, .small-section-header a,