From 4ecb49eba3770e4826346486252b9e7a1b5fcb33 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 21 Oct 2021 22:06:40 +0100 Subject: [PATCH 01/17] Handle cross-crate module `ExpnId`s consistently - Always use the ExpnId serialized to `tables` - Use the Id for traits and enums from other crates in resolution. --- compiler/rustc_metadata/src/rmeta/decoder.rs | 9 +++++---- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 +++- compiler/rustc_metadata/src/rmeta/mod.rs | 1 - compiler/rustc_resolve/src/build_reduced_graph.rs | 7 +------ src/test/ui/proc-macro/meta-macro-hygiene.stdout | 2 ++ src/test/ui/proc-macro/nonterminal-token-hygiene.stdout | 2 ++ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index ca9daa49aa2d2..1e2b4aaef805f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1219,10 +1219,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn module_expansion(&self, id: DefIndex, sess: &Session) -> ExpnId { - if let EntryKind::Mod(m) = self.kind(id) { - m.decode((self, sess)).expansion - } else { - panic!("Expected module, found {:?}", self.local_def_id(id)) + match self.kind(id) { + EntryKind::Mod(_) | EntryKind::Enum(_) | EntryKind::Trait(_) => { + self.get_expn_that_defined(id, sess) + } + _ => panic!("Expected module, found {:?}", self.local_def_id(id)), } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 20f7b059b5600..08d9db381cf06 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1086,11 +1086,13 @@ impl EncodeContext<'a, 'tcx> { Lazy::empty() }; - let data = ModData { reexports, expansion: tcx.expn_that_defined(local_def_id) }; + let data = ModData { reexports }; record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); if self.is_proc_macro { record!(self.tables.children[def_id] <- &[]); + // Encode this here because we don't do it in encode_def_ids. + record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); } else { record!(self.tables.children[def_id] <- md.item_ids.iter().map(|item_id| { item_id.def_id.local_def_index diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 42855e9d9d12f..40c497044ded1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -367,7 +367,6 @@ struct RenderedConst(String); #[derive(MetadataEncodable, MetadataDecodable)] struct ModData { reexports: Lazy<[Export]>, - expansion: ExpnId, } #[derive(MetadataEncodable, MetadataDecodable)] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 2a562a06cb3cd..c215de5560ad4 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -145,12 +145,7 @@ impl<'a> Resolver<'a> { } else { def_key.disambiguated_data.data.get_opt_name().expect("module without name") }; - let expn_id = if def_kind == DefKind::Mod { - self.cstore().module_expansion_untracked(def_id, &self.session) - } else { - // FIXME: Parent expansions for enums and traits are not kept in metadata. - ExpnId::root() - }; + let expn_id = self.cstore().module_expansion_untracked(def_id, &self.session); Some(self.new_module( parent, diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout index 7f7a1009c909a..5d04fe1e3de5f 100644 --- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout +++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout @@ -49,6 +49,8 @@ crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it") crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site") crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #5, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy") +crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive") +crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive") crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "include") crate2::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) diff --git a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout index 0780386381373..709b2a2169e08 100644 --- a/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/src/test/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -73,6 +73,8 @@ crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "outer") crate0::{{expn3}}: parent: crate0::{{expn2}}, call_site_ctxt: #4, def_site_ctxt: #4, kind: Macro(Bang, "inner") crate0::{{expn4}}: parent: crate0::{{expn3}}, call_site_ctxt: #6, def_site_ctxt: #0, kind: Macro(Bang, "print_bang") +crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive") +crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Attr, "derive") crate1::{{expnNNN}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "include") crate2::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports) From 1536d7220b847d8a843a3357cc62ef5704f2c690 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 21 Oct 2021 23:36:51 +0100 Subject: [PATCH 02/17] Don't suggest importing items with hygienic names This will potentially hide a few correct suggestions, but importing these items from another module is not generally possible. --- compiler/rustc_resolve/src/diagnostics.rs | 2 ++ compiler/rustc_span/src/hygiene.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 63000a9d13d41..aefb3f2cb9c83 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -831,9 +831,11 @@ impl<'a> Resolver<'a> { // collect results based on the filter function // avoid suggesting anything from the same module in which we are resolving + // avoid suggesting anything with a hygienic name if ident.name == lookup_ident.name && ns == namespace && !ptr::eq(in_module, parent_scope.module) + && !ident.span.normalize_to_macros_2_0().from_expansion() { let res = name_binding.res(); if filter_fn(res) { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index aa15febe8853d..724d1904dc33c 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -709,7 +709,7 @@ impl SyntaxContext { /// pub fn f() {} // `f`'s `SyntaxContext` has a single `ExpnId` from `m`. /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. /// } - /// n(f); + /// n!(f); /// macro n($j:ident) { /// use foo::*; /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` From fabede185151d10019e41273d056b36990d76399 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 23 Oct 2021 12:06:58 +0100 Subject: [PATCH 03/17] Add more tests for cross-crate hygiene --- src/test/ui/hygiene/auxiliary/fields.rs | 73 ++++++++ src/test/ui/hygiene/auxiliary/methods.rs | 160 ++++++++++++++++++ src/test/ui/hygiene/auxiliary/pub_hygiene.rs | 7 + src/test/ui/hygiene/auxiliary/use_by_macro.rs | 15 ++ src/test/ui/hygiene/auxiliary/variants.rs | 36 ++++ .../ui/hygiene/cross-crate-define-and-use.rs | 14 ++ src/test/ui/hygiene/cross-crate-fields.rs | 21 +++ .../ui/hygiene/cross-crate-glob-hygiene.rs | 18 ++ .../hygiene/cross-crate-glob-hygiene.stderr | 11 ++ src/test/ui/hygiene/cross-crate-methods.rs | 29 ++++ ...giene.rs => cross-crate-name-collision.rs} | 0 .../ui/hygiene/cross-crate-name-hiding-2.rs | 12 ++ .../hygiene/cross-crate-name-hiding-2.stderr | 9 + .../ui/hygiene/cross-crate-name-hiding.rs | 10 ++ .../ui/hygiene/cross-crate-name-hiding.stderr | 9 + src/test/ui/hygiene/cross-crate-redefine.rs | 11 ++ .../ui/hygiene/cross-crate-redefine.stderr | 15 ++ src/test/ui/hygiene/cross-crate-variants.rs | 15 ++ 18 files changed, 465 insertions(+) create mode 100644 src/test/ui/hygiene/auxiliary/fields.rs create mode 100644 src/test/ui/hygiene/auxiliary/methods.rs create mode 100644 src/test/ui/hygiene/auxiliary/pub_hygiene.rs create mode 100644 src/test/ui/hygiene/auxiliary/use_by_macro.rs create mode 100644 src/test/ui/hygiene/auxiliary/variants.rs create mode 100644 src/test/ui/hygiene/cross-crate-define-and-use.rs create mode 100644 src/test/ui/hygiene/cross-crate-fields.rs create mode 100644 src/test/ui/hygiene/cross-crate-glob-hygiene.rs create mode 100644 src/test/ui/hygiene/cross-crate-glob-hygiene.stderr create mode 100644 src/test/ui/hygiene/cross-crate-methods.rs rename src/test/ui/hygiene/{cross_crate_hygiene.rs => cross-crate-name-collision.rs} (100%) create mode 100644 src/test/ui/hygiene/cross-crate-name-hiding-2.rs create mode 100644 src/test/ui/hygiene/cross-crate-name-hiding-2.stderr create mode 100644 src/test/ui/hygiene/cross-crate-name-hiding.rs create mode 100644 src/test/ui/hygiene/cross-crate-name-hiding.stderr create mode 100644 src/test/ui/hygiene/cross-crate-redefine.rs create mode 100644 src/test/ui/hygiene/cross-crate-redefine.stderr create mode 100644 src/test/ui/hygiene/cross-crate-variants.rs diff --git a/src/test/ui/hygiene/auxiliary/fields.rs b/src/test/ui/hygiene/auxiliary/fields.rs new file mode 100644 index 0000000000000..733d11a9e8229 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/fields.rs @@ -0,0 +1,73 @@ +#![feature(decl_macro)] + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Field { + RootCtxt, + MacroCtxt, +} + +#[rustfmt::skip] +macro x( + $macro_name:ident, + $macro2_name:ident, + $type_name:ident, + $field_name:ident, + $const_name:ident +) { + #[derive(Copy, Clone)] + pub struct $type_name { + pub field: Field, + pub $field_name: Field, + } + + pub const $const_name: $type_name = + $type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt }; + + #[macro_export] + macro_rules! $macro_name { + (check_fields_of $e:expr) => {{ + let e = $e; + assert_eq!(e.field, Field::MacroCtxt); + assert_eq!(e.$field_name, Field::RootCtxt); + }}; + (check_fields) => {{ + assert_eq!($const_name.field, Field::MacroCtxt); + assert_eq!($const_name.$field_name, Field::RootCtxt); + }}; + (construct) => { + $type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt } + }; + } + + pub macro $macro2_name { + (check_fields_of $e:expr) => {{ + let e = $e; + assert_eq!(e.field, Field::MacroCtxt); + assert_eq!(e.$field_name, Field::RootCtxt); + }}, + (check_fields) => {{ + assert_eq!($const_name.field, Field::MacroCtxt); + assert_eq!($const_name.$field_name, Field::RootCtxt); + }}, + (construct) => { + $type_name { field: Field::MacroCtxt, $field_name: Field::RootCtxt } + } + } +} + +x!(test_fields, test_fields2, MyStruct, field, MY_CONST); + +pub fn check_fields(s: MyStruct) { + test_fields!(check_fields_of s); +} + +pub fn check_fields_local() { + test_fields!(check_fields); + test_fields2!(check_fields); + + let s1 = test_fields!(construct); + test_fields!(check_fields_of s1); + + let s2 = test_fields2!(construct); + test_fields2!(check_fields_of s2); +} diff --git a/src/test/ui/hygiene/auxiliary/methods.rs b/src/test/ui/hygiene/auxiliary/methods.rs new file mode 100644 index 0000000000000..23b9c61cfc058 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/methods.rs @@ -0,0 +1,160 @@ +#![feature(decl_macro)] + +#[derive(PartialEq, Eq, Debug)] +pub enum Method { + DefaultMacroCtxt, + DefaultRootCtxt, + OverrideMacroCtxt, + OverrideRootCtxt, +} + +#[rustfmt::skip] +macro x($macro_name:ident, $macro2_name:ident, $trait_name:ident, $method_name:ident) { + pub trait $trait_name { + fn method(&self) -> Method { + Method::DefaultMacroCtxt + } + + fn $method_name(&self) -> Method { + Method::DefaultRootCtxt + } + } + + impl $trait_name for () {} + impl $trait_name for bool { + fn method(&self) -> Method { + Method::OverrideMacroCtxt + } + + fn $method_name(&self) -> Method { + Method::OverrideRootCtxt + } + } + + #[macro_export] + macro_rules! $macro_name { + (check_resolutions) => { + assert_eq!(().method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&()), Method::DefaultMacroCtxt); + assert_eq!(().$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&()), Method::DefaultRootCtxt); + + assert_eq!(false.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&false), Method::OverrideMacroCtxt); + assert_eq!(false.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&false), Method::OverrideRootCtxt); + + assert_eq!('a'.method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&'a'), Method::DefaultMacroCtxt); + assert_eq!('a'.$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&'a'), Method::DefaultRootCtxt); + + assert_eq!(1i32.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&1i32), Method::OverrideMacroCtxt); + assert_eq!(1i32.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&1i32), Method::OverrideRootCtxt); + + assert_eq!(1i64.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&1i64), Method::OverrideMacroCtxt); + assert_eq!(1i64.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&1i64), Method::OverrideRootCtxt); + }; + (assert_no_override $v:expr) => { + assert_eq!($v.method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&$v), Method::DefaultMacroCtxt); + assert_eq!($v.$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&$v), Method::DefaultRootCtxt); + }; + (assert_override $v:expr) => { + assert_eq!($v.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&$v), Method::OverrideMacroCtxt); + assert_eq!($v.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&$v), Method::OverrideRootCtxt); + }; + (impl for $t:ty) => { + impl $trait_name for $t { + fn method(&self) -> Method { + Method::OverrideMacroCtxt + } + + fn $method_name(&self) -> Method { + Method::OverrideRootCtxt + } + } + }; + } + + pub macro $macro2_name { + (check_resolutions) => { + assert_eq!(().method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&()), Method::DefaultMacroCtxt); + assert_eq!(().$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&()), Method::DefaultRootCtxt); + + assert_eq!(false.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&false), Method::OverrideMacroCtxt); + assert_eq!(false.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&false), Method::OverrideRootCtxt); + + assert_eq!('a'.method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&'a'), Method::DefaultMacroCtxt); + assert_eq!('a'.$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&'a'), Method::DefaultRootCtxt); + + assert_eq!(1i32.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&1i32), Method::OverrideMacroCtxt); + assert_eq!(1i32.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&1i32), Method::OverrideRootCtxt); + + assert_eq!(1i64.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&1i64), Method::OverrideMacroCtxt); + assert_eq!(1i64.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&1i64), Method::OverrideRootCtxt); + }, + (assert_no_override $v:expr) => { + assert_eq!($v.method(), Method::DefaultMacroCtxt); + assert_eq!($trait_name::method(&$v), Method::DefaultMacroCtxt); + assert_eq!($v.$method_name(), Method::DefaultRootCtxt); + assert_eq!($trait_name::$method_name(&$v), Method::DefaultRootCtxt); + }, + (assert_override $v:expr) => { + assert_eq!($v.method(), Method::OverrideMacroCtxt); + assert_eq!($trait_name::method(&$v), Method::OverrideMacroCtxt); + assert_eq!($v.$method_name(), Method::OverrideRootCtxt); + assert_eq!($trait_name::$method_name(&$v), Method::OverrideRootCtxt); + }, + (impl for $t:ty) => { + impl $trait_name for $t { + fn method(&self) -> Method { + Method::OverrideMacroCtxt + } + + fn $method_name(&self) -> Method { + Method::OverrideRootCtxt + } + } + } + } +} + +x!(test_trait, test_trait2, MyTrait, method); + +impl MyTrait for char {} +test_trait!(impl for i32); +test_trait2!(impl for i64); + +pub fn check_crate_local() { + test_trait!(check_resolutions); + test_trait2!(check_resolutions); +} + +// Check that any comparison of idents at monomorphization time is correct +pub fn check_crate_local_generic(t: T, u: U) { + test_trait!(check_resolutions); + test_trait2!(check_resolutions); + + test_trait!(assert_no_override t); + test_trait2!(assert_no_override t); + test_trait!(assert_override u); + test_trait2!(assert_override u); +} diff --git a/src/test/ui/hygiene/auxiliary/pub_hygiene.rs b/src/test/ui/hygiene/auxiliary/pub_hygiene.rs new file mode 100644 index 0000000000000..47e76a629c8b1 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/pub_hygiene.rs @@ -0,0 +1,7 @@ +#![feature(decl_macro)] + +macro x() { + pub struct MyStruct; +} + +x!(); diff --git a/src/test/ui/hygiene/auxiliary/use_by_macro.rs b/src/test/ui/hygiene/auxiliary/use_by_macro.rs new file mode 100644 index 0000000000000..791cf0358952a --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/use_by_macro.rs @@ -0,0 +1,15 @@ +#![feature(decl_macro)] + +macro x($macro_name:ident) { + #[macro_export] + macro_rules! $macro_name { + (define) => { + pub struct MyStruct; + }; + (create) => { + MyStruct {} + }; + } +} + +x!(my_struct); diff --git a/src/test/ui/hygiene/auxiliary/variants.rs b/src/test/ui/hygiene/auxiliary/variants.rs new file mode 100644 index 0000000000000..dbfcce17d47f1 --- /dev/null +++ b/src/test/ui/hygiene/auxiliary/variants.rs @@ -0,0 +1,36 @@ +#![feature(decl_macro)] + +#[rustfmt::skip] +macro x($macro_name:ident, $macro2_name:ident, $type_name:ident, $variant_name:ident) { + #[repr(u8)] + pub enum $type_name { + Variant = 0, + $variant_name = 1, + } + + #[macro_export] + macro_rules! $macro_name { + () => {{ + assert_eq!($type_name::Variant as u8, 0); + assert_eq!($type_name::$variant_name as u8, 1); + assert_eq!(<$type_name>::Variant as u8, 0); + assert_eq!(<$type_name>::$variant_name as u8, 1); + }}; + } + + pub macro $macro2_name { + () => {{ + assert_eq!($type_name::Variant as u8, 0); + assert_eq!($type_name::$variant_name as u8, 1); + assert_eq!(<$type_name>::Variant as u8, 0); + assert_eq!(<$type_name>::$variant_name as u8, 1); + }}, + } +} + +x!(test_variants, test_variants2, MyEnum, Variant); + +pub fn check_variants() { + test_variants!(); + test_variants2!(); +} diff --git a/src/test/ui/hygiene/cross-crate-define-and-use.rs b/src/test/ui/hygiene/cross-crate-define-and-use.rs new file mode 100644 index 0000000000000..9bb8d804940cd --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-define-and-use.rs @@ -0,0 +1,14 @@ +// check-pass +// aux-build:use_by_macro.rs + +#![feature(type_name_of_val)] +extern crate use_by_macro; + +use use_by_macro::*; + +enum MyStruct {} +my_struct!(define); + +fn main() { + let x = my_struct!(create); +} diff --git a/src/test/ui/hygiene/cross-crate-fields.rs b/src/test/ui/hygiene/cross-crate-fields.rs new file mode 100644 index 0000000000000..96ed412a6258a --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-fields.rs @@ -0,0 +1,21 @@ +// run-pass +// aux-build:fields.rs + +extern crate fields; + +use fields::*; + +fn main() { + check_fields_local(); + + test_fields!(check_fields); + test_fields2!(check_fields); + + let s1 = test_fields!(construct); + check_fields(s1); + test_fields!(check_fields_of s1); + + let s2 = test_fields2!(construct); + check_fields(s2); + test_fields2!(check_fields_of s2); +} diff --git a/src/test/ui/hygiene/cross-crate-glob-hygiene.rs b/src/test/ui/hygiene/cross-crate-glob-hygiene.rs new file mode 100644 index 0000000000000..ebd632d2f66d8 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-glob-hygiene.rs @@ -0,0 +1,18 @@ +// aux-build:use_by_macro.rs + +extern crate use_by_macro; + +use use_by_macro::*; + +mod m { + use use_by_macro::*; + + my_struct!(define); +} + +use m::*; + +fn main() { + let x = my_struct!(create); + //~^ ERROR cannot find struct, variant or union type `MyStruct` in this scope +} diff --git a/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr b/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr new file mode 100644 index 0000000000000..8f8ed8307b9e2 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr @@ -0,0 +1,11 @@ +error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope + --> $DIR/cross-crate-glob-hygiene.rs:16:13 + | +LL | let x = my_struct!(create); + | ^^^^^^^^^^^^^^^^^^ not found in this scope + | + = note: this error originates in the macro `my_struct` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0422`. diff --git a/src/test/ui/hygiene/cross-crate-methods.rs b/src/test/ui/hygiene/cross-crate-methods.rs new file mode 100644 index 0000000000000..f27ad893a1e44 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-methods.rs @@ -0,0 +1,29 @@ +// run-pass +// aux-build:methods.rs + +extern crate methods; + +use methods::*; + +struct A; +struct B; +struct C; + +impl MyTrait for A {} +test_trait!(impl for B); +test_trait2!(impl for C); + +fn main() { + check_crate_local(); + check_crate_local_generic(A, B); + check_crate_local_generic(A, C); + + test_trait!(check_resolutions); + test_trait2!(check_resolutions); + test_trait!(assert_no_override A); + test_trait2!(assert_no_override A); + test_trait!(assert_override B); + test_trait2!(assert_override B); + test_trait!(assert_override C); + test_trait2!(assert_override C); +} diff --git a/src/test/ui/hygiene/cross_crate_hygiene.rs b/src/test/ui/hygiene/cross-crate-name-collision.rs similarity index 100% rename from src/test/ui/hygiene/cross_crate_hygiene.rs rename to src/test/ui/hygiene/cross-crate-name-collision.rs diff --git a/src/test/ui/hygiene/cross-crate-name-hiding-2.rs b/src/test/ui/hygiene/cross-crate-name-hiding-2.rs new file mode 100644 index 0000000000000..8d416b0210422 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding-2.rs @@ -0,0 +1,12 @@ +// aux-build:use_by_macro.rs + +extern crate use_by_macro; + +use use_by_macro::*; + +my_struct!(define); + +fn main() { + let x = MyStruct {}; + //~^ ERROR cannot find struct, variant or union type `MyStruct` in this scope +} diff --git a/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr b/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr new file mode 100644 index 0000000000000..1a7e4900b8f33 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr @@ -0,0 +1,9 @@ +error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope + --> $DIR/cross-crate-name-hiding-2.rs:10:13 + | +LL | let x = MyStruct {}; + | ^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0422`. diff --git a/src/test/ui/hygiene/cross-crate-name-hiding.rs b/src/test/ui/hygiene/cross-crate-name-hiding.rs new file mode 100644 index 0000000000000..87d011dfe8731 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding.rs @@ -0,0 +1,10 @@ +// aux-build:pub_hygiene.rs + +extern crate pub_hygiene; + +use pub_hygiene::*; + +fn main() { + let x = MyStruct {}; + //~^ ERROR cannot find struct, variant or union type `MyStruct` in this scope +} diff --git a/src/test/ui/hygiene/cross-crate-name-hiding.stderr b/src/test/ui/hygiene/cross-crate-name-hiding.stderr new file mode 100644 index 0000000000000..149d7eec4ef4c --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding.stderr @@ -0,0 +1,9 @@ +error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope + --> $DIR/cross-crate-name-hiding.rs:8:13 + | +LL | let x = MyStruct {}; + | ^^^^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0422`. diff --git a/src/test/ui/hygiene/cross-crate-redefine.rs b/src/test/ui/hygiene/cross-crate-redefine.rs new file mode 100644 index 0000000000000..c79a6be5133d6 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-redefine.rs @@ -0,0 +1,11 @@ +// aux-build:use_by_macro.rs + +extern crate use_by_macro; + +use use_by_macro::*; + +my_struct!(define); +//~^ ERROR the name `MyStruct` is defined multiple times +my_struct!(define); + +fn main() {} diff --git a/src/test/ui/hygiene/cross-crate-redefine.stderr b/src/test/ui/hygiene/cross-crate-redefine.stderr new file mode 100644 index 0000000000000..6d0c95117ddfb --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-redefine.stderr @@ -0,0 +1,15 @@ +error[E0428]: the name `MyStruct` is defined multiple times + --> $DIR/cross-crate-redefine.rs:7:1 + | +LL | my_struct!(define); + | ^^^^^^^^^^^^^^^^^^ `MyStruct` redefined here +LL | +LL | my_struct!(define); + | ------------------ previous definition of the type `MyStruct` here + | + = note: `MyStruct` must be defined only once in the type namespace of this module + = note: this error originates in the macro `my_struct` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/src/test/ui/hygiene/cross-crate-variants.rs b/src/test/ui/hygiene/cross-crate-variants.rs new file mode 100644 index 0000000000000..6c5671f324958 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-variants.rs @@ -0,0 +1,15 @@ +// run-pass +// aux-build:variants.rs + +extern crate variants; + +use variants::*; + +fn main() { + check_variants(); + + test_variants!(); + test_variants2!(); + + assert_eq!(MyEnum::Variant as u8, 1); +} From 6c61db44073ce96176850ea5db5dc99624823d91 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Thu, 28 Oct 2021 12:49:46 +0000 Subject: [PATCH 04/17] Use `is_global` in `candidate_should_be_dropped_in_favor_of` This manifistated in #90195 with compiler being unable to keep one candidate for a trait impl, if where is a global impl and more than one trait bound in the where clause. Before #87280 `candidate_should_be_dropped_in_favor_of` was using `TypeFoldable::is_global()` that was enough to discard the two `ParamCandidate`s. But #87280 changed it to use `TypeFoldable::is_known_global()` instead, which is pessimistic, so now the compiler drops the global impl instead (because `is_known_global` is not sure) and then can't decide between the two `ParamCandidate`s. Switching it to use `is_global` again solves the issue. Fixes #90195. --- compiler/rustc_trait_selection/src/traits/select/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1b26e38fe0e4d..8bd842a5e062c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1548,7 +1548,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. let is_global = - |cand: &ty::PolyTraitRef<'_>| cand.is_known_global() && !cand.has_late_bound_regions(); + |cand: &ty::PolyTraitRef<'tcx>| cand.is_global(self.infcx.tcx) && !cand.has_late_bound_regions(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // and `DiscriminantKindCandidate` to anything else. From 6f942a2f4a0487a7428a9aba8f018eb45cdb924e Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Thu, 28 Oct 2021 13:23:49 +0000 Subject: [PATCH 05/17] Reformat the changed line to make tidy happy --- compiler/rustc_trait_selection/src/traits/select/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8bd842a5e062c..60676ad3f4f60 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1547,8 +1547,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Check if a bound would previously have been removed when normalizing // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. - let is_global = - |cand: &ty::PolyTraitRef<'tcx>| cand.is_global(self.infcx.tcx) && !cand.has_late_bound_regions(); + let is_global = |cand: &ty::PolyTraitRef<'tcx>| { + cand.is_global(self.infcx.tcx) && !cand.has_late_bound_regions() + }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // and `DiscriminantKindCandidate` to anything else. From 9a0a622a042d0ed04ad349d8bb778e40dd417a16 Mon Sep 17 00:00:00 2001 From: Ilya Yanok Date: Thu, 28 Oct 2021 14:25:46 +0000 Subject: [PATCH 06/17] Add test cases --- src/test/ui/traits/issue-90195-2.rs | 20 ++++++++++++++++++++ src/test/ui/traits/issue-90195.rs | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/test/ui/traits/issue-90195-2.rs create mode 100644 src/test/ui/traits/issue-90195.rs diff --git a/src/test/ui/traits/issue-90195-2.rs b/src/test/ui/traits/issue-90195-2.rs new file mode 100644 index 0000000000000..b739dc46e4e8b --- /dev/null +++ b/src/test/ui/traits/issue-90195-2.rs @@ -0,0 +1,20 @@ +// check-pass +pub trait Archive { + type Archived; +} + +impl Archive for Option { + type Archived = (); +} +pub type Archived = ::Archived; + +pub trait Deserialize {} + +const ARRAY_SIZE: usize = 32; +impl<__D> Deserialize<__D> for () +where + Option<[u8; ARRAY_SIZE]>: Archive, + Archived>: Deserialize<__D>, +{ +} +fn main() {} diff --git a/src/test/ui/traits/issue-90195.rs b/src/test/ui/traits/issue-90195.rs new file mode 100644 index 0000000000000..543c9f197e1bb --- /dev/null +++ b/src/test/ui/traits/issue-90195.rs @@ -0,0 +1,21 @@ +// check-pass +pub trait Archive { + type Archived; +} + +impl Archive for Option { + type Archived = (); +} +pub type Archived = ::Archived; + +pub trait Deserialize {} + +const ARRAY_SIZE: usize = 32; +impl<__D> Deserialize<__D> for () +where + Option<[u8; ARRAY_SIZE]>: Archive, + Option<[u8; ARRAY_SIZE]>: Archive, + Archived>: Deserialize<__D>, +{ +} +fn main() {} From d8426ea6369e88b4de12eed1ba8ecd6be5dc6559 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 28 Oct 2021 21:48:21 +0100 Subject: [PATCH 07/17] Remove `ModData` from rustc_metadata This avoids having to decode 2 `Lazy`s when decoding a modules exports. --- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 4 +--- compiler/rustc_metadata/src/rmeta/mod.rs | 7 +------ compiler/rustc_resolve/src/build_reduced_graph.rs | 3 +-- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1e2b4aaef805f..5e90aec003e9b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1198,8 +1198,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - if let EntryKind::Mod(data) = kind { - for exp in data.decode((self, sess)).reexports.decode((self, sess)) { + if let EntryKind::Mod(exports) = kind { + for exp in exports.decode((self, sess)) { match exp.res { Res::Def(DefKind::Macro(..), _) => {} _ if macros_only => continue, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 08d9db381cf06..0dbef66ac37d7 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1086,9 +1086,7 @@ impl EncodeContext<'a, 'tcx> { Lazy::empty() }; - let data = ModData { reexports }; - - record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); + record!(self.tables.kind[def_id] <- EntryKind::Mod(reexports)); if self.is_proc_macro { record!(self.tables.children[def_id] <- &[]); // Encode this here because we don't do it in encode_def_ids. diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 40c497044ded1..4e09d23169aca 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -346,7 +346,7 @@ enum EntryKind { Union(Lazy, ReprOptions), Fn(Lazy), ForeignFn(Lazy), - Mod(Lazy), + Mod(Lazy<[Export]>), MacroDef(Lazy), ProcMacro(MacroKind), Closure, @@ -364,11 +364,6 @@ enum EntryKind { #[derive(Encodable, Decodable)] struct RenderedConst(String); -#[derive(MetadataEncodable, MetadataDecodable)] -struct ModData { - reexports: Lazy<[Export]>, -} - #[derive(MetadataEncodable, MetadataDecodable)] struct FnData { asyncness: hir::IsAsync, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index c215de5560ad4..33af9884cbb66 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -145,12 +145,11 @@ impl<'a> Resolver<'a> { } else { def_key.disambiguated_data.data.get_opt_name().expect("module without name") }; - let expn_id = self.cstore().module_expansion_untracked(def_id, &self.session); Some(self.new_module( parent, ModuleKind::Def(def_kind, def_id, name), - expn_id, + self.cstore().module_expansion_untracked(def_id, &self.session), self.cstore().get_span_untracked(def_id, &self.session), // FIXME: Account for `#[no_implicit_prelude]` attributes. parent.map_or(false, |module| module.no_implicit_prelude), From a76a2d4ef986ec69e4ffb81c35dfb8dc91effee9 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Thu, 28 Oct 2021 21:48:39 +0100 Subject: [PATCH 08/17] Add comments to hygiene tests --- src/test/ui/hygiene/cross-crate-define-and-use.rs | 5 +++++ src/test/ui/hygiene/cross-crate-fields.rs | 3 +++ src/test/ui/hygiene/cross-crate-glob-hygiene.rs | 5 +++++ src/test/ui/hygiene/cross-crate-glob-hygiene.stderr | 2 +- src/test/ui/hygiene/cross-crate-methods.rs | 4 ++++ src/test/ui/hygiene/cross-crate-name-collision.rs | 4 ++++ src/test/ui/hygiene/cross-crate-name-hiding-2.rs | 3 +++ src/test/ui/hygiene/cross-crate-name-hiding-2.stderr | 2 +- src/test/ui/hygiene/cross-crate-name-hiding.rs | 3 +++ src/test/ui/hygiene/cross-crate-name-hiding.stderr | 2 +- src/test/ui/hygiene/cross-crate-redefine.rs | 3 +++ src/test/ui/hygiene/cross-crate-redefine.stderr | 2 +- src/test/ui/hygiene/cross-crate-variants.rs | 3 +++ 13 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/test/ui/hygiene/cross-crate-define-and-use.rs b/src/test/ui/hygiene/cross-crate-define-and-use.rs index 9bb8d804940cd..94f1adff62642 100644 --- a/src/test/ui/hygiene/cross-crate-define-and-use.rs +++ b/src/test/ui/hygiene/cross-crate-define-and-use.rs @@ -1,3 +1,8 @@ +// Check that a marco from another crate can define an item in one expansion +// and use it from another, without it being visible to everyone. +// This requires that the definition of `my_struct` preserves the hygiene +// information for the tokens in its definition. + // check-pass // aux-build:use_by_macro.rs diff --git a/src/test/ui/hygiene/cross-crate-fields.rs b/src/test/ui/hygiene/cross-crate-fields.rs index 96ed412a6258a..1bcd64573ac6e 100644 --- a/src/test/ui/hygiene/cross-crate-fields.rs +++ b/src/test/ui/hygiene/cross-crate-fields.rs @@ -1,3 +1,6 @@ +// Test that fields on a struct defined in another crate are resolved correctly +// their names differ only in `SyntaxContext`. + // run-pass // aux-build:fields.rs diff --git a/src/test/ui/hygiene/cross-crate-glob-hygiene.rs b/src/test/ui/hygiene/cross-crate-glob-hygiene.rs index ebd632d2f66d8..de5576682a6bd 100644 --- a/src/test/ui/hygiene/cross-crate-glob-hygiene.rs +++ b/src/test/ui/hygiene/cross-crate-glob-hygiene.rs @@ -1,3 +1,8 @@ +// Check that globs cannot import hygienic identifiers from a macro expansion +// in another crate. `my_struct` is a `macro_rules` macro, so the struct it +// defines is only not imported because `my_struct` is defined by a macros 2.0 +// macro. + // aux-build:use_by_macro.rs extern crate use_by_macro; diff --git a/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr b/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr index 8f8ed8307b9e2..7369e77d0709e 100644 --- a/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr +++ b/src/test/ui/hygiene/cross-crate-glob-hygiene.stderr @@ -1,5 +1,5 @@ error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope - --> $DIR/cross-crate-glob-hygiene.rs:16:13 + --> $DIR/cross-crate-glob-hygiene.rs:21:13 | LL | let x = my_struct!(create); | ^^^^^^^^^^^^^^^^^^ not found in this scope diff --git a/src/test/ui/hygiene/cross-crate-methods.rs b/src/test/ui/hygiene/cross-crate-methods.rs index f27ad893a1e44..0e6f57c33f64a 100644 --- a/src/test/ui/hygiene/cross-crate-methods.rs +++ b/src/test/ui/hygiene/cross-crate-methods.rs @@ -1,3 +1,7 @@ +// Test that methods defined in another crate are resolved correctly their +// names differ only in `SyntaxContext`. This also checks that any name +// resolution done when monomorphizing is correct. + // run-pass // aux-build:methods.rs diff --git a/src/test/ui/hygiene/cross-crate-name-collision.rs b/src/test/ui/hygiene/cross-crate-name-collision.rs index 75742960b7e3c..8f118782f2319 100644 --- a/src/test/ui/hygiene/cross-crate-name-collision.rs +++ b/src/test/ui/hygiene/cross-crate-name-collision.rs @@ -1,3 +1,7 @@ +// Check that two items defined in another crate that have identifiers that +// only differ by `SyntaxContext` do not cause name collisions when imported +// in another crate. + // check-pass // aux-build:needs_hygiene.rs diff --git a/src/test/ui/hygiene/cross-crate-name-hiding-2.rs b/src/test/ui/hygiene/cross-crate-name-hiding-2.rs index 8d416b0210422..3eacd775c9e53 100644 --- a/src/test/ui/hygiene/cross-crate-name-hiding-2.rs +++ b/src/test/ui/hygiene/cross-crate-name-hiding-2.rs @@ -1,3 +1,6 @@ +// Check that an identifier from a 2.0 macro in another crate cannot be +// resolved with an identifier that's not from a macro expansion. + // aux-build:use_by_macro.rs extern crate use_by_macro; diff --git a/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr b/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr index 1a7e4900b8f33..46314cdd5ab4e 100644 --- a/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr +++ b/src/test/ui/hygiene/cross-crate-name-hiding-2.stderr @@ -1,5 +1,5 @@ error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope - --> $DIR/cross-crate-name-hiding-2.rs:10:13 + --> $DIR/cross-crate-name-hiding-2.rs:13:13 | LL | let x = MyStruct {}; | ^^^^^^^^ not found in this scope diff --git a/src/test/ui/hygiene/cross-crate-name-hiding.rs b/src/test/ui/hygiene/cross-crate-name-hiding.rs index 87d011dfe8731..dd76ecc5762f5 100644 --- a/src/test/ui/hygiene/cross-crate-name-hiding.rs +++ b/src/test/ui/hygiene/cross-crate-name-hiding.rs @@ -1,3 +1,6 @@ +// Check that an item defined by a 2.0 macro in another crate cannot be used in +// another crate. + // aux-build:pub_hygiene.rs extern crate pub_hygiene; diff --git a/src/test/ui/hygiene/cross-crate-name-hiding.stderr b/src/test/ui/hygiene/cross-crate-name-hiding.stderr index 149d7eec4ef4c..f8840c8f85a33 100644 --- a/src/test/ui/hygiene/cross-crate-name-hiding.stderr +++ b/src/test/ui/hygiene/cross-crate-name-hiding.stderr @@ -1,5 +1,5 @@ error[E0422]: cannot find struct, variant or union type `MyStruct` in this scope - --> $DIR/cross-crate-name-hiding.rs:8:13 + --> $DIR/cross-crate-name-hiding.rs:11:13 | LL | let x = MyStruct {}; | ^^^^^^^^ not found in this scope diff --git a/src/test/ui/hygiene/cross-crate-redefine.rs b/src/test/ui/hygiene/cross-crate-redefine.rs index c79a6be5133d6..3cb06b4bad873 100644 --- a/src/test/ui/hygiene/cross-crate-redefine.rs +++ b/src/test/ui/hygiene/cross-crate-redefine.rs @@ -1,3 +1,6 @@ +// Check that items with identical `SyntaxContext` conflict even when that +// context involves a mark from another crate. + // aux-build:use_by_macro.rs extern crate use_by_macro; diff --git a/src/test/ui/hygiene/cross-crate-redefine.stderr b/src/test/ui/hygiene/cross-crate-redefine.stderr index 6d0c95117ddfb..4f1419de42677 100644 --- a/src/test/ui/hygiene/cross-crate-redefine.stderr +++ b/src/test/ui/hygiene/cross-crate-redefine.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `MyStruct` is defined multiple times - --> $DIR/cross-crate-redefine.rs:7:1 + --> $DIR/cross-crate-redefine.rs:10:1 | LL | my_struct!(define); | ^^^^^^^^^^^^^^^^^^ `MyStruct` redefined here diff --git a/src/test/ui/hygiene/cross-crate-variants.rs b/src/test/ui/hygiene/cross-crate-variants.rs index 6c5671f324958..efc73a21f16f3 100644 --- a/src/test/ui/hygiene/cross-crate-variants.rs +++ b/src/test/ui/hygiene/cross-crate-variants.rs @@ -1,3 +1,6 @@ +// Test that variants of an enum defined in another crate are resolved +// correctly when their names differ only in `SyntaxContext`. + // run-pass // aux-build:variants.rs From 8a473ca3469340741a7108d52dd488c799f70fad Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 22 Oct 2021 21:45:06 +0200 Subject: [PATCH 09/17] Recursively document Deref --- src/librustdoc/html/render/context.rs | 9 ++- src/librustdoc/html/render/mod.rs | 82 ++++++++++++++++---- src/librustdoc/passes/collect_trait_impls.rs | 36 ++++++++- 3 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 0e29cc85f9e75..db718fbe67389 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use std::sync::mpsc::{channel, Receiver}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::edition::Edition; @@ -54,6 +54,9 @@ crate struct Context<'tcx> { /// real location of an item. This is used to allow external links to /// publicly reused items to redirect to the right location. pub(super) render_redirect_pages: bool, + /// Tracks section IDs for `Deref` targets so they match in both the main + /// body and the sidebar. + pub(super) deref_id_map: Rc>>, /// The map used to ensure all generated 'id=' attributes are unique. pub(super) id_map: RefCell, /// Shared mutable state. @@ -70,7 +73,7 @@ crate struct Context<'tcx> { // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 104); +rustc_data_structures::static_assert_size!(Context<'_>, 112); /// Shared mutable state used in [`Context`] and elsewhere. crate struct SharedContext<'tcx> { @@ -513,6 +516,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { dst, render_redirect_pages: false, id_map: RefCell::new(id_map), + deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), shared: Rc::new(scx), include_sources, }; @@ -536,6 +540,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { current: self.current.clone(), dst: self.dst.clone(), render_redirect_pages: self.render_redirect_pages, + deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), id_map: RefCell::new(IdMap::new()), shared: Rc::clone(&self.shared), include_sources: self.include_sources, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 07dea624d7c52..b84cd718d4c83 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1053,6 +1053,19 @@ fn render_assoc_items( containing_item: &clean::Item, it: DefId, what: AssocItemRender<'_>, +) { + let mut derefs = FxHashSet::default(); + derefs.insert(it); + render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs) +} + +fn render_assoc_items_inner( + w: &mut Buffer, + cx: &Context<'_>, + containing_item: &clean::Item, + it: DefId, + what: AssocItemRender<'_>, + derefs: &mut FxHashSet, ) { info!("Documenting associated items of {:?}", containing_item.name); let cache = cx.cache(); @@ -1072,12 +1085,18 @@ fn render_assoc_items( RenderMode::Normal } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { + let id = + cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); + if let Some(def_id) = type_.def_id(cx.cache()) { + cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); + } write!( w, - "

\ + "

\ Methods from {trait_}<Target = {type_}>\ - \ + \

", + id = id, trait_ = trait_.print(cx), type_ = type_.print(cx), ); @@ -1104,17 +1123,22 @@ fn render_assoc_items( ); } } - if let AssocItemRender::DerefFor { .. } = what { - return; - } + if !traits.is_empty() { let deref_impl = traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait()); if let Some(impl_) = deref_impl { let has_deref_mut = traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait()); - render_deref_methods(w, cx, impl_, containing_item, has_deref_mut); + render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, derefs); } + + // If we were already one level into rendering deref methods, we don't want to render + // anything after recursing into any further deref methods above. + if let AssocItemRender::DerefFor { .. } = what { + return; + } + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = traits.iter().partition(|t| t.inner_impl().synthetic); let (blanket_impl, concrete): (Vec<&&Impl>, _) = @@ -1166,6 +1190,7 @@ fn render_deref_methods( impl_: &Impl, container_item: &clean::Item, deref_mut: bool, + derefs: &mut FxHashSet, ) { let cache = cx.cache(); let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); @@ -1187,16 +1212,16 @@ fn render_deref_methods( if let Some(did) = target.def_id(cache) { if let Some(type_did) = impl_.inner_impl().for_.def_id(cache) { // `impl Deref for S` - if did == type_did { + if did == type_did || !derefs.insert(did) { // Avoid infinite cycles return; } } - render_assoc_items(w, cx, container_item, did, what); + render_assoc_items_inner(w, cx, container_item, did, what, derefs); } else { if let Some(prim) = target.primitive_type() { if let Some(&did) = cache.primitive_locations.get(&prim) { - render_assoc_items(w, cx, container_item, did, what); + render_assoc_items_inner(w, cx, container_item, did, what, derefs); } } } @@ -1986,7 +2011,9 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { if let Some(impl_) = v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait()) { - sidebar_deref_methods(cx, out, impl_, v); + let mut derefs = FxHashSet::default(); + derefs.insert(did); + sidebar_deref_methods(cx, out, impl_, v, &mut derefs); } let format_impls = |impls: Vec<&Impl>| { @@ -2060,7 +2087,13 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { } } -fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[Impl]) { +fn sidebar_deref_methods( + cx: &Context<'_>, + out: &mut Buffer, + impl_: &Impl, + v: &[Impl], + derefs: &mut FxHashSet, +) { let c = cx.cache(); debug!("found Deref: {:?}", impl_); @@ -2077,7 +2110,7 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[ if let Some(did) = target.def_id(c) { if let Some(type_did) = impl_.inner_impl().for_.def_id(c) { // `impl Deref for S` - if did == type_did { + if did == type_did || !derefs.insert(did) { // Avoid infinite cycles return; } @@ -2101,9 +2134,17 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[ }) .collect::>(); if !ret.is_empty() { + let map; + let id = if let Some(target_def_id) = real_target.def_id(c) { + map = cx.deref_id_map.borrow(); + map.get(&target_def_id).expect("Deref section without derived id") + } else { + "deref-methods" + }; write!( out, - "

Methods from {}<Target={}>

", + "

Methods from {}<Target={}>

", + id, Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))), Escape(&format!("{:#}", real_target.print(cx))), ); @@ -2116,6 +2157,21 @@ fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &[ out.push_str(""); } } + + // Recurse into any further impls that might exist for `target` + if let Some(target_did) = target.def_id_no_primitives() { + if let Some(target_impls) = c.impls.get(&target_did) { + if let Some(target_deref_impl) = target_impls.iter().find(|i| { + i.inner_impl() + .trait_ + .as_ref() + .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait()) + .unwrap_or(false) + }) { + sidebar_deref_methods(cx, out, target_deref_impl, target_impls, derefs); + } + } + } } } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 91a0cb413eb28..d559dd4effe2c 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -3,7 +3,8 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::def_id::DefId; use rustc_middle::ty::DefIdTree; use rustc_span::symbol::sym; @@ -53,6 +54,29 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { } let mut cleaner = BadImplStripper { prims, items: crate_items }; + let mut type_did_to_deref_target: FxHashMap = FxHashMap::default(); + + // Follow all `Deref` targets of included items and recursively add them as valid + fn add_deref_target( + map: &FxHashMap, + cleaner: &mut BadImplStripper, + type_did: DefId, + ) { + if let Some(target) = map.get(&type_did) { + debug!("add_deref_target: type {:?}, target {:?}", type_did, target); + if let Some(target_prim) = target.primitive_type() { + cleaner.prims.insert(target_prim); + } else if let Some(target_did) = target.def_id() { + // `impl Deref for S` + if target_did == type_did { + // Avoid infinite cycles + return; + } + cleaner.items.insert(target_did.into()); + add_deref_target(map, cleaner, target_did); + } + } + } // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in &new_items { @@ -73,6 +97,16 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { } else if let Some(did) = target.def_id(&cx.cache) { cleaner.items.insert(did.into()); } + if let Some(for_did) = for_.def_id() { + if type_did_to_deref_target.insert(for_did, target).is_none() { + // Since only the `DefId` portion of the `Type` instances is known to be same for both the + // `Deref` target type and the impl for type positions, this map of types is keyed by + // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly. + if cleaner.keep_impl_with_def_id(for_did.into()) { + add_deref_target(&type_did_to_deref_target, &mut cleaner, for_did); + } + } + } } } } From 0c38f31bf23506ad33bbd922ea6095f1010712df Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 22 Oct 2021 22:39:33 +0200 Subject: [PATCH 10/17] Add tests for recursive deref --- src/librustdoc/passes/collect_trait_impls.rs | 11 ++- src/test/rustdoc-ui/recursive-deref-ice.rs | 19 +++++ src/test/rustdoc/deref-recursive-pathbuf.rs | 24 ++++++ src/test/rustdoc/deref-recursive.rs | 40 ++++++++++ src/test/rustdoc/deref-typedef.rs | 4 +- src/test/rustdoc/recursive-deref-sidebar.rs | 2 +- src/test/rustdoc/recursive-deref.rs | 77 +++++++++++++++++++- 7 files changed, 169 insertions(+), 8 deletions(-) create mode 100644 src/test/rustdoc-ui/recursive-deref-ice.rs create mode 100644 src/test/rustdoc/deref-recursive-pathbuf.rs create mode 100644 src/test/rustdoc/deref-recursive.rs diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index d559dd4effe2c..b306eb98bb55e 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -81,8 +81,8 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { // scan through included items ahead of time to splice in Deref targets to the "valid" sets for it in &new_items { if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind { - if cleaner.keep_impl(for_) - && trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait() + if trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait() + && cleaner.keep_impl(for_) { let target = items .iter() @@ -221,8 +221,11 @@ impl BadImplStripper { true } else if let Some(prim) = ty.primitive_type() { self.prims.contains(&prim) - } else if let Some(did) = ty.def_id_no_primitives() { - self.keep_impl_with_def_id(did.into()) + } else if ty.def_id_no_primitives().is_some() { + // We want to keep *ALL* deref implementations in case some of them are used in + // the current crate. + // FIXME: Try to filter the one actually used... + true } else { false } diff --git a/src/test/rustdoc-ui/recursive-deref-ice.rs b/src/test/rustdoc-ui/recursive-deref-ice.rs new file mode 100644 index 0000000000000..c44fd27f40305 --- /dev/null +++ b/src/test/rustdoc-ui/recursive-deref-ice.rs @@ -0,0 +1,19 @@ +// check-pass + +// ICE found in https://github.com/rust-lang/rust/issues/83123 + +pub struct Attribute; + +pub struct Map<'hir> {} +impl<'hir> Map<'hir> { + pub fn attrs(&self) -> &'hir [Attribute] { &[] } +} + +pub struct List(T); + +impl std::ops::Deref for List { + type Target = [T]; + fn deref(&self) -> &[T] { + &[] + } +} diff --git a/src/test/rustdoc/deref-recursive-pathbuf.rs b/src/test/rustdoc/deref-recursive-pathbuf.rs new file mode 100644 index 0000000000000..ac23eced38671 --- /dev/null +++ b/src/test/rustdoc/deref-recursive-pathbuf.rs @@ -0,0 +1,24 @@ +// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing +// levels and across multiple crates. + +// @has 'foo/struct.Foo.html' +// @has '-' '//*[@id="deref-methods-PathBuf"]' 'Methods from Deref' +// @has '-' '//*[@class="impl-items"]//*[@id="method.as_path"]' 'pub fn as_path(&self)' +// @has '-' '//*[@id="deref-methods-Path"]' 'Methods from Deref' +// @has '-' '//*[@class="impl-items"]//*[@id="method.exists"]' 'pub fn exists(&self)' +// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-PathBuf"]' 'Methods from Deref' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.as_path"]' 'as_path' +// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Path"]' 'Methods from Deref' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.exists"]' 'exists' + +#![crate_name = "foo"] + +use std::ops::Deref; +use std::path::PathBuf; + +pub struct Foo(PathBuf); + +impl Deref for Foo { + type Target = PathBuf; + fn deref(&self) -> &PathBuf { &self.0 } +} diff --git a/src/test/rustdoc/deref-recursive.rs b/src/test/rustdoc/deref-recursive.rs new file mode 100644 index 0000000000000..ac43b10ec85f5 --- /dev/null +++ b/src/test/rustdoc/deref-recursive.rs @@ -0,0 +1,40 @@ +// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing +// levels if needed. + +// @has 'foo/struct.Foo.html' +// @has '-' '//*[@id="deref-methods-Bar"]' 'Methods from Deref' +// @has '-' '//*[@class="impl-items"]//*[@id="method.bar"]' 'pub fn bar(&self)' +// @has '-' '//*[@id="deref-methods-Baz"]' 'Methods from Deref' +// @has '-' '//*[@class="impl-items"]//*[@id="method.baz"]' 'pub fn baz(&self)' +// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Bar"]' 'Methods from Deref' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.bar"]' 'bar' +// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Baz"]' 'Methods from Deref' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.baz"]' 'baz' + +#![crate_name = "foo"] + +use std::ops::Deref; + +pub struct Foo(Bar); +pub struct Bar(Baz); +pub struct Baz; + +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Bar { &self.0 } +} + +impl Deref for Bar { + type Target = Baz; + fn deref(&self) -> &Baz { &self.0 } +} + +impl Bar { + /// This appears under `Foo` methods + pub fn bar(&self) {} +} + +impl Baz { + /// This should also appear in `Foo` methods when recursing + pub fn baz(&self) {} +} diff --git a/src/test/rustdoc/deref-typedef.rs b/src/test/rustdoc/deref-typedef.rs index d42ff384b29b8..ad7a96c5dad1f 100644 --- a/src/test/rustdoc/deref-typedef.rs +++ b/src/test/rustdoc/deref-typedef.rs @@ -1,12 +1,12 @@ #![crate_name = "foo"] // @has 'foo/struct.Bar.html' -// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref' +// @has '-' '//*[@id="deref-methods-FooJ"]' 'Methods from Deref' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_a"]' 'pub fn foo_a(&self)' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_b"]' 'pub fn foo_b(&self)' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_c"]' 'pub fn foo_c(&self)' // @has '-' '//*[@class="impl-items"]//*[@id="method.foo_j"]' 'pub fn foo_j(&self)' -// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods"]' 'Methods from Deref' +// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-FooJ"]' 'Methods from Deref' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_a"]' 'foo_a' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_b"]' 'foo_b' // @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_c"]' 'foo_c' diff --git a/src/test/rustdoc/recursive-deref-sidebar.rs b/src/test/rustdoc/recursive-deref-sidebar.rs index fcb636ade8f7a..65a7debc2538d 100644 --- a/src/test/rustdoc/recursive-deref-sidebar.rs +++ b/src/test/rustdoc/recursive-deref-sidebar.rs @@ -15,7 +15,7 @@ impl Deref for A { fn deref(&self) -> &B { todo!() } } -// @!has recursive_deref_sidebar/struct.A.html '//div[@class="sidebar-links"]' 'foo_c' +// @has recursive_deref_sidebar/struct.A.html '//div[@class="sidebar-links"]' 'foo_c' impl Deref for B { type Target = C; fn deref(&self) -> &C { todo!() } diff --git a/src/test/rustdoc/recursive-deref.rs b/src/test/rustdoc/recursive-deref.rs index 3d17bce472154..18634e1b3606d 100644 --- a/src/test/rustdoc/recursive-deref.rs +++ b/src/test/rustdoc/recursive-deref.rs @@ -1,7 +1,9 @@ use std::ops::Deref; +// Cyclic deref with the parent (which is not the top parent). pub struct A; pub struct B; +pub struct C; // @has recursive_deref/struct.A.html '//h3[@class="code-header in-band"]' 'impl Deref for A' impl Deref for A { @@ -14,7 +16,80 @@ impl Deref for A { // @has recursive_deref/struct.B.html '//h3[@class="code-header in-band"]' 'impl Deref for B' impl Deref for B { - type Target = A; + type Target = C; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @has recursive_deref/struct.C.html '//h3[@class="code-header in-band"]' 'impl Deref for C' +impl Deref for C { + type Target = B; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// Cyclic deref with the grand-parent (which is not the top parent). +pub struct D; +pub struct E; +pub struct F; +pub struct G; + +// @has recursive_deref/struct.D.html '//h3[@class="code-header in-band"]' 'impl Deref for D' +impl Deref for D { + type Target = E; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @has recursive_deref/struct.E.html '//h3[@class="code-header in-band"]' 'impl Deref for E' +impl Deref for E { + type Target = F; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @has recursive_deref/struct.F.html '//h3[@class="code-header in-band"]' 'impl Deref for F' +impl Deref for F { + type Target = G; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @has recursive_deref/struct.G.html '//h3[@class="code-header in-band"]' 'impl Deref for G' +impl Deref for G { + type Target = E; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// Cyclic deref with top parent. +pub struct H; +pub struct I; + +// @has recursive_deref/struct.H.html '//h3[@class="code-header in-band"]' 'impl Deref for H' +impl Deref for H { + type Target = I; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @has recursive_deref/struct.I.html '//h3[@class="code-header in-band"]' 'impl Deref for I' +impl Deref for I { + type Target = H; fn deref(&self) -> &Self::Target { panic!() From 3398877858b28862854097bd33a4b9bbadcd79af Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Oct 2021 22:18:33 +0200 Subject: [PATCH 11/17] Remove the Rc wrapping of deref_id_map --- src/librustdoc/html/render/context.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index db718fbe67389..f7bfd7add2920 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -56,7 +56,7 @@ crate struct Context<'tcx> { pub(super) render_redirect_pages: bool, /// Tracks section IDs for `Deref` targets so they match in both the main /// body and the sidebar. - pub(super) deref_id_map: Rc>>, + pub(super) deref_id_map: RefCell>, /// The map used to ensure all generated 'id=' attributes are unique. pub(super) id_map: RefCell, /// Shared mutable state. @@ -73,7 +73,7 @@ crate struct Context<'tcx> { // `Context` is cloned a lot, so we don't want the size to grow unexpectedly. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 112); +rustc_data_structures::static_assert_size!(Context<'_>, 144); /// Shared mutable state used in [`Context`] and elsewhere. crate struct SharedContext<'tcx> { @@ -516,7 +516,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { dst, render_redirect_pages: false, id_map: RefCell::new(id_map), - deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), + deref_id_map: RefCell::new(FxHashMap::default()), shared: Rc::new(scx), include_sources, }; @@ -540,7 +540,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { current: self.current.clone(), dst: self.dst.clone(), render_redirect_pages: self.render_redirect_pages, - deref_id_map: Rc::new(RefCell::new(FxHashMap::default())), + deref_id_map: RefCell::new(FxHashMap::default()), id_map: RefCell::new(IdMap::new()), shared: Rc::clone(&self.shared), include_sources: self.include_sources, From dd68d207a5713850f757b7355e175021062db059 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Oct 2021 22:57:02 +0200 Subject: [PATCH 12/17] Don't display "Methods from Deref<...>" if no method is display (the ones which don't have `self` argument) --- src/librustdoc/html/render/mod.rs | 12 +++++++++--- src/test/rustdoc/recursive-deref.rs | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b84cd718d4c83..ceabc76c2e91e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1075,9 +1075,10 @@ fn render_assoc_items_inner( }; let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { + let mut tmp_buf = Buffer::empty_from(w); let render_mode = match what { AssocItemRender::All => { - w.write_str( + tmp_buf.write_str( "

\ Implementations\

", @@ -1091,7 +1092,7 @@ fn render_assoc_items_inner( cx.deref_id_map.borrow_mut().insert(def_id, id.clone()); } write!( - w, + tmp_buf, "

\ Methods from {trait_}<Target = {type_}>\ \ @@ -1103,9 +1104,10 @@ fn render_assoc_items_inner( RenderMode::ForDeref { mut_: deref_mut_ } } }; + let mut impls_buf = Buffer::empty_from(w); for i in &non_trait { render_impl( - w, + &mut impls_buf, cx, i, containing_item, @@ -1122,6 +1124,10 @@ fn render_assoc_items_inner( }, ); } + if !impls_buf.is_empty() { + w.push_buffer(tmp_buf); + w.push_buffer(impls_buf); + } } if !traits.is_empty() { diff --git a/src/test/rustdoc/recursive-deref.rs b/src/test/rustdoc/recursive-deref.rs index 18634e1b3606d..9833599e12300 100644 --- a/src/test/rustdoc/recursive-deref.rs +++ b/src/test/rustdoc/recursive-deref.rs @@ -5,7 +5,12 @@ pub struct A; pub struct B; pub struct C; +impl C { + pub fn c(&self) {} +} + // @has recursive_deref/struct.A.html '//h3[@class="code-header in-band"]' 'impl Deref for A' +// @has '-' '//*[@class="impl-items"]//*[@id="method.c"]' 'pub fn c(&self)' impl Deref for A { type Target = B; @@ -15,6 +20,7 @@ impl Deref for A { } // @has recursive_deref/struct.B.html '//h3[@class="code-header in-band"]' 'impl Deref for B' +// @has '-' '//*[@class="impl-items"]//*[@id="method.c"]' 'pub fn c(&self)' impl Deref for B { type Target = C; @@ -38,7 +44,13 @@ pub struct E; pub struct F; pub struct G; +impl G { + // There is no "self" parameter so it shouldn't be listed! + pub fn g() {} +} + // @has recursive_deref/struct.D.html '//h3[@class="code-header in-band"]' 'impl Deref for D' +// @!has '-' '//*[@id="deref-methods-G"]' impl Deref for D { type Target = E; @@ -48,6 +60,7 @@ impl Deref for D { } // @has recursive_deref/struct.E.html '//h3[@class="code-header in-band"]' 'impl Deref for E' +// @!has '-' '//*[@id="deref-methods-G"]' impl Deref for E { type Target = F; @@ -57,6 +70,7 @@ impl Deref for E { } // @has recursive_deref/struct.F.html '//h3[@class="code-header in-band"]' 'impl Deref for F' +// @!has '-' '//*[@id="deref-methods-G"]' impl Deref for F { type Target = G; @@ -78,7 +92,13 @@ impl Deref for G { pub struct H; pub struct I; +impl I { + // There is no "self" parameter so it shouldn't be listed! + pub fn i() {} +} + // @has recursive_deref/struct.H.html '//h3[@class="code-header in-band"]' 'impl Deref for H' +// @!has '-' '//*[@id="deref-methods-I"]' impl Deref for H { type Target = I; From f09b67a696aac41bb2a8f5146b4017ffd141e232 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Oct 2021 23:26:29 +0200 Subject: [PATCH 13/17] Fix panic when documenting libproc-macro --- src/librustdoc/passes/collect_trait_impls.rs | 23 +++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index b306eb98bb55e..9ccc4e5b89f76 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -66,7 +66,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { debug!("add_deref_target: type {:?}, target {:?}", type_did, target); if let Some(target_prim) = target.primitive_type() { cleaner.prims.insert(target_prim); - } else if let Some(target_did) = target.def_id() { + } else if let Some(target_did) = target.def_id_no_primitives() { // `impl Deref for S` if target_did == type_did { // Avoid infinite cycles @@ -82,7 +82,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { for it in &new_items { if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind { if trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait() - && cleaner.keep_impl(for_) + && cleaner.keep_impl(for_, true) { let target = items .iter() @@ -97,7 +97,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { } else if let Some(did) = target.def_id(&cx.cache) { cleaner.items.insert(did.into()); } - if let Some(for_did) = for_.def_id() { + if let Some(for_did) = for_.def_id_no_primitives() { if type_did_to_deref_target.insert(for_did, target).is_none() { // Since only the `DefId` portion of the `Type` instances is known to be same for both the // `Deref` target type and the impl for type positions, this map of types is keyed by @@ -113,10 +113,10 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate { new_items.retain(|it| { if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind { - cleaner.keep_impl(for_) - || trait_ - .as_ref() - .map_or(false, |t| cleaner.keep_impl_with_def_id(t.def_id().into())) + cleaner.keep_impl( + for_, + trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait(), + ) || trait_.as_ref().map_or(false, |t| cleaner.keep_impl_with_def_id(t.def_id().into())) || blanket_impl.is_some() } else { true @@ -215,17 +215,14 @@ struct BadImplStripper { } impl BadImplStripper { - fn keep_impl(&self, ty: &Type) -> bool { + fn keep_impl(&self, ty: &Type, is_deref: bool) -> bool { if let Generic(_) = ty { // keep impls made on generics true } else if let Some(prim) = ty.primitive_type() { self.prims.contains(&prim) - } else if ty.def_id_no_primitives().is_some() { - // We want to keep *ALL* deref implementations in case some of them are used in - // the current crate. - // FIXME: Try to filter the one actually used... - true + } else if let Some(did) = ty.def_id_no_primitives() { + is_deref || self.keep_impl_with_def_id(did.into()) } else { false } From e2151497bfb65945102619e019adb677b2f3ed31 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Thu, 28 Oct 2021 00:05:38 +0300 Subject: [PATCH 14/17] Skip suggestions for the AsRef trait --- .../rustc_typeck/src/check/method/suggest.rs | 1 + src/test/ui/typeck/issue-89806.rs | 3 +++ src/test/ui/typeck/issue-89806.stderr | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 src/test/ui/typeck/issue-89806.rs create mode 100644 src/test/ui/typeck/issue-89806.stderr diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 28b19981c2d40..934b83bd700e1 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -1251,6 +1251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.lang_items().deref_trait(), self.tcx.lang_items().deref_mut_trait(), self.tcx.lang_items().drop_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), ]; // Try alternative arbitrary self types that could fulfill this call. // FIXME: probe for all types that *could* be arbitrary self-types, not diff --git a/src/test/ui/typeck/issue-89806.rs b/src/test/ui/typeck/issue-89806.rs new file mode 100644 index 0000000000000..69cec08652ae9 --- /dev/null +++ b/src/test/ui/typeck/issue-89806.rs @@ -0,0 +1,3 @@ +fn main() { + 0u8.as_ref(); //~ ERROR no method named `as_ref` found for type `u8` in the current scope +} diff --git a/src/test/ui/typeck/issue-89806.stderr b/src/test/ui/typeck/issue-89806.stderr new file mode 100644 index 0000000000000..9a54fac526159 --- /dev/null +++ b/src/test/ui/typeck/issue-89806.stderr @@ -0,0 +1,26 @@ +error[E0599]: no method named `as_ref` found for type `u8` in the current scope + --> $DIR/issue-89806.rs:2:9 + | +LL | 0u8.as_ref(); + | ^^^^^^ method not found in `u8` + | + ::: $SRC_DIR/core/src/pin.rs:LL:COL + | +LL | pub fn as_ref(&self) -> Pin<&P::Target> { + | ------ + | | + | the method is available for `Pin<&mut u8>` here + | the method is available for `Pin<&u8>` here + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Pin::new(&mut 0u8).as_ref(); + | +++++++++++++ + +help: consider wrapping the receiver expression with the appropriate type + | +LL | Pin::new(&0u8).as_ref(); + | ++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. From cad2d21cb669b21b6a31edc0c60b2d0748f3a4f2 Mon Sep 17 00:00:00 2001 From: Yuval Dolev Date: Fri, 29 Oct 2021 15:43:33 +0300 Subject: [PATCH 15/17] Explicitly skipping suggestions for 'Pin' as it does not implement the 'AsRef' trait --- .../rustc_typeck/src/check/method/suggest.rs | 8 ++++++-- src/test/ui/typeck/issue-89806.stderr | 17 ----------------- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 934b83bd700e1..8007b9f23776a 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -15,7 +15,7 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::lev_distance; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{source_map, FileName, MultiSpan, Span}; +use rustc_span::{source_map, FileName, MultiSpan, Span, Symbol}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation}; @@ -1301,7 +1301,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We don't want to suggest a container type when the missing // method is `.clone()` or `.deref()` otherwise we'd suggest // `Arc::new(foo).clone()`, which is far from what the user wants. - let skip = skippable.contains(&did); + // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not + // implement the `AsRef` trait. + let skip = skippable.contains(&did) + || (("Pin::new" == *pre) + && (Symbol::intern("as_ref") == item_name.name)); // Make sure the method is defined for the *actual* receiver: we don't // want to treat `Box` as a receiver if it only works because of // an autoderef to `&self` diff --git a/src/test/ui/typeck/issue-89806.stderr b/src/test/ui/typeck/issue-89806.stderr index 9a54fac526159..c36b4967ee996 100644 --- a/src/test/ui/typeck/issue-89806.stderr +++ b/src/test/ui/typeck/issue-89806.stderr @@ -3,23 +3,6 @@ error[E0599]: no method named `as_ref` found for type `u8` in the current scope | LL | 0u8.as_ref(); | ^^^^^^ method not found in `u8` - | - ::: $SRC_DIR/core/src/pin.rs:LL:COL - | -LL | pub fn as_ref(&self) -> Pin<&P::Target> { - | ------ - | | - | the method is available for `Pin<&mut u8>` here - | the method is available for `Pin<&u8>` here - | -help: consider wrapping the receiver expression with the appropriate type - | -LL | Pin::new(&mut 0u8).as_ref(); - | +++++++++++++ + -help: consider wrapping the receiver expression with the appropriate type - | -LL | Pin::new(&0u8).as_ref(); - | ++++++++++ + error: aborting due to previous error From 78b604569b4381ef47b7c0897be5c318b8f88618 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 30 Oct 2021 16:30:14 +0200 Subject: [PATCH 16/17] Document tests a bit more --- src/test/rustdoc/deref-recursive-pathbuf.rs | 1 + src/test/rustdoc/deref-recursive.rs | 1 + src/test/rustdoc/recursive-deref.rs | 3 +++ 3 files changed, 5 insertions(+) diff --git a/src/test/rustdoc/deref-recursive-pathbuf.rs b/src/test/rustdoc/deref-recursive-pathbuf.rs index ac23eced38671..9ab338ca9b1d1 100644 --- a/src/test/rustdoc/deref-recursive-pathbuf.rs +++ b/src/test/rustdoc/deref-recursive-pathbuf.rs @@ -1,5 +1,6 @@ // #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing // levels and across multiple crates. +// For `Deref` on non-foreign types, look at `deref-recursive.rs`. // @has 'foo/struct.Foo.html' // @has '-' '//*[@id="deref-methods-PathBuf"]' 'Methods from Deref' diff --git a/src/test/rustdoc/deref-recursive.rs b/src/test/rustdoc/deref-recursive.rs index ac43b10ec85f5..c07e048b0651c 100644 --- a/src/test/rustdoc/deref-recursive.rs +++ b/src/test/rustdoc/deref-recursive.rs @@ -1,5 +1,6 @@ // #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing // levels if needed. +// For `Deref` on foreign types, look at `deref-recursive-pathbuf.rs`. // @has 'foo/struct.Foo.html' // @has '-' '//*[@id="deref-methods-Bar"]' 'Methods from Deref' diff --git a/src/test/rustdoc/recursive-deref.rs b/src/test/rustdoc/recursive-deref.rs index 9833599e12300..a7504fbccfb50 100644 --- a/src/test/rustdoc/recursive-deref.rs +++ b/src/test/rustdoc/recursive-deref.rs @@ -50,6 +50,7 @@ impl G { } // @has recursive_deref/struct.D.html '//h3[@class="code-header in-band"]' 'impl Deref for D' +// We also check that `G::g` method isn't rendered because there is no `self` argument. // @!has '-' '//*[@id="deref-methods-G"]' impl Deref for D { type Target = E; @@ -60,6 +61,7 @@ impl Deref for D { } // @has recursive_deref/struct.E.html '//h3[@class="code-header in-band"]' 'impl Deref for E' +// We also check that `G::g` method isn't rendered because there is no `self` argument. // @!has '-' '//*[@id="deref-methods-G"]' impl Deref for E { type Target = F; @@ -70,6 +72,7 @@ impl Deref for E { } // @has recursive_deref/struct.F.html '//h3[@class="code-header in-band"]' 'impl Deref for F' +// We also check that `G::g` method isn't rendered because there is no `self` argument. // @!has '-' '//*[@id="deref-methods-G"]' impl Deref for F { type Target = G; From a65c98fefb78cddee955b87214732b0de30a769f Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 21 Oct 2021 23:09:11 -0700 Subject: [PATCH 17/17] Remove underlines from non-top docblocks. We still had a number of places where underlined section headings would show up, like under Implementations. --- src/librustdoc/html/static/css/rustdoc.css | 11 +++- .../{header-size.goml => headings.goml} | 58 ++++++++++++++++++- 2 files changed, 65 insertions(+), 4 deletions(-) rename src/test/rustdoc-gui/{header-size.goml => headings.goml} (51%) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index b98dae85ba053..93cbc0debb945 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -134,7 +134,7 @@ h1, h2, h3, h4 { margin: 20px 0 15px 0; padding-bottom: 6px; } -h5, h6 { +.docblock h3, .docblock h4, h5, h6 { margin: 15px 0 5px 0; } h1.fqn { @@ -149,7 +149,14 @@ h1.fqn { h1.fqn > .in-band > a:hover { text-decoration: underline; } -h2, h3, h4 { +/* The only headings that get underlines are: + Markdown-generated headings within the top-doc + Rustdoc-generated h2 section headings (e.g. "Implementations", "Required Methods", etc) + Underlines elsewhere in the documentation break up visual flow and tend to invert + section hierarchies. */ +h2, +.top-doc h3, +.top-doc h4 { border-bottom: 1px solid; } h3.code-header { diff --git a/src/test/rustdoc-gui/header-size.goml b/src/test/rustdoc-gui/headings.goml similarity index 51% rename from src/test/rustdoc-gui/header-size.goml rename to src/test/rustdoc-gui/headings.goml index fa5d9c9291628..35d772170f6f9 100644 --- a/src/test/rustdoc-gui/header-size.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -1,6 +1,8 @@ -// This test check that headers (a) have the correct heading level, and (b) are the right size. +// This test check that headers (a) have the correct heading level, (b) are the right size, +// and (c) have the correct underlining (or absence of underlining). // The sizes may change as design changes, but try to make sure a lower header is never bigger than -// its parent headers. +// its parent headers. Also make sure lower headers don't have underlines when their parents lack +// an underline. // Most of these sizes are set in CSS in `em` units, so here's a conversion chart based on our // default 16px font size: // 24px 1.5em @@ -13,87 +15,139 @@ goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html assert-css: ("h1.fqn", {"font-size": "24px"}) +assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) +assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) +assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"}) +assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#fields", {"font-size": "22.4px"}) +assert-css: ("h2#fields", {"border-bottom-width": "1px"}) assert-css: ("h3#title-for-field", {"font-size": "20.8px"}) +assert-css: ("h3#title-for-field", {"border-bottom-width": "0px"}) assert-css: ("h4#sub-heading-for-field", {"font-size": "16px"}) +assert-css: ("h4#sub-heading-for-field", {"border-bottom-width": "0px"}) assert-css: ("h2#implementations", {"font-size": "22.4px"}) +assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"}) +assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"}) +assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"}) assert-css: ("h4#title-for-struct-impl-doc", {"font-size": "16px"}) +assert-css: ("h4#title-for-struct-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#sub-heading-for-struct-impl-doc", {"font-size": "16px"}) +assert-css: ("h5#sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#title-for-struct-impl-item-doc", {"font-size": "16px"}) +assert-css: ("h5#title-for-struct-impl-item-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"}) goto: file://|DOC_PATH|/test_docs/enum.HeavilyDocumentedEnum.html assert-css: ("h1.fqn", {"font-size": "24px"}) +assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) +assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) +assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"}) +assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#variants", {"font-size": "22.4px"}) +assert-css: ("h2#variants", {"border-bottom-width": "1px"}) assert-css: ("h3#none-prose-title", {"font-size": "20.8px"}) +assert-css: ("h3#none-prose-title", {"border-bottom-width": "0px"}) assert-css: ("h4#none-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h4#none-prose-sub-heading", {"border-bottom-width": "0px"}) assert-css: ("h3#wrapped-prose-title", {"font-size": "20.8px"}) +assert-css: ("h3#wrapped-prose-title", {"border-bottom-width": "0px"}) assert-css: ("h4#wrapped-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h4#wrapped-prose-sub-heading", {"border-bottom-width": "0px"}) assert-css: ("h4#wrapped0-prose-title", {"font-size": "16px"}) +assert-css: ("h4#wrapped0-prose-title", {"border-bottom-width": "0px"}) assert-css: ("h5#wrapped0-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h5#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"}) assert-css: ("h4#structy-prose-title", {"font-size": "16px"}) +assert-css: ("h4#structy-prose-title", {"border-bottom-width": "0px"}) assert-css: ("h5#structy-prose-sub-heading", {"font-size": "16px"}) +assert-css: ("h5#structy-prose-sub-heading", {"border-bottom-width": "0px"}) assert-css: ("h2#implementations", {"font-size": "22.4px"}) +assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"}) +assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"}) +assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"}) assert-css: ("h4#title-for-enum-impl-doc", {"font-size": "16px"}) +assert-css: ("h4#title-for-enum-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#sub-heading-for-enum-impl-doc", {"font-size": "16px"}) +assert-css: ("h5#sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#title-for-enum-impl-item-doc", {"font-size": "16px"}) +assert-css: ("h5#title-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"}) goto: file://|DOC_PATH|/test_docs/union.HeavilyDocumentedUnion.html assert-css: ("h1.fqn", {"font-size": "24px"}) +assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) +assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) +assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#fields", {"font-size": "22.4px"}) +assert-css: ("h2#fields", {"border-bottom-width": "1px"}) assert-css: ("h3#title-for-union-variant", {"font-size": "20.8px"}) +assert-css: ("h3#title-for-union-variant", {"border-bottom-width": "0px"}) assert-css: ("h4#sub-heading-for-union-variant", {"font-size": "16px"}) +assert-css: ("h4#sub-heading-for-union-variant", {"border-bottom-width": "0px"}) assert-css: ("h2#implementations", {"font-size": "22.4px"}) +assert-css: ("h2#implementations", {"border-bottom-width": "1px"}) assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"}) +assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"}) assert-css: ("h4#title-for-union-impl-doc", {"font-size": "16px"}) +assert-css: ("h4#title-for-union-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#sub-heading-for-union-impl-doc", {"font-size": "16px"}) +assert-css: ("h5#sub-heading-for-union-impl-doc", {"border-bottom-width": "0px"}) assert-css: ("h5#title-for-union-impl-item-doc", {"font-size": "16px"}) +assert-css: ("h5#title-for-union-impl-item-doc", {"border-bottom-width": "0px"}) assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"font-size": "15.2px"}) +assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": "0px"}) goto: file://|DOC_PATH|/test_docs/macro.heavily_documented_macro.html assert-css: ("h1.fqn", {"font-size": "24px"}) +assert-css: ("h1.fqn", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) +assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"}) +assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})