diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index ca9daa49aa2d2..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, @@ -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..0dbef66ac37d7 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1086,11 +1086,11 @@ impl EncodeContext<'a, 'tcx> { Lazy::empty() }; - let data = ModData { reexports, expansion: tcx.expn_that_defined(local_def_id) }; - - 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. + 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..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,12 +364,6 @@ enum EntryKind { #[derive(Encodable, Decodable)] struct RenderedConst(String); -#[derive(MetadataEncodable, MetadataDecodable)] -struct ModData { - reexports: Lazy<[Export]>, - expansion: ExpnId, -} - #[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 2a562a06cb3cd..33af9884cbb66 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -145,17 +145,11 @@ 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() - }; 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), diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ccfab263bd48e..163acebcceacf 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -842,9 +842,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` diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 1b26e38fe0e4d..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<'_>| cand.is_known_global() && !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. diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 28b19981c2d40..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}; @@ -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 @@ -1300,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/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 1ce6a5c00be74..826e7782db1e9 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: RefCell>, /// 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<'_>, 144); /// 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: 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: 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 e2ecf20fe848b..f78129050d7ec 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1054,6 +1054,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(); @@ -1063,9 +1076,10 @@ fn render_assoc_items( }; 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\

", @@ -1073,21 +1087,28 @@ 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, - "

\ + tmp_buf, + "

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

", + id = id, trait_ = trait_.print(cx), type_ = type_.print(cx), ); 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, @@ -1104,18 +1125,27 @@ fn render_assoc_items( }, ); } + if !impls_buf.is_empty() { + w.push_buffer(tmp_buf); + w.push_buffer(impls_buf); + } } - 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>, _) = @@ -1167,6 +1197,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(); @@ -1188,16 +1219,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); } } } @@ -1987,7 +2018,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>| { @@ -2061,7 +2094,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_); @@ -2078,7 +2117,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; } @@ -2102,9 +2141,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))), ); @@ -2117,6 +2164,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/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/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index 1beb3f2f4abbd..7a4198198fa69 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; @@ -51,12 +52,35 @@ 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_no_primitives() { + // `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 { 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_, true) { let target = items .iter() @@ -71,16 +95,26 @@ 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_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 + // `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); + } + } + } } } } 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 @@ -179,14 +213,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 let Some(did) = ty.def_id_no_primitives() { - self.keep_impl_with_def_id(did.into()) + is_deref || self.keep_impl_with_def_id(did.into()) } else { false } 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"}) 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..9ab338ca9b1d1 --- /dev/null +++ b/src/test/rustdoc/deref-recursive-pathbuf.rs @@ -0,0 +1,25 @@ +// #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' +// @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..c07e048b0651c --- /dev/null +++ b/src/test/rustdoc/deref-recursive.rs @@ -0,0 +1,41 @@ +// #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' +// @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..a7504fbccfb50 100644 --- a/src/test/rustdoc/recursive-deref.rs +++ b/src/test/rustdoc/recursive-deref.rs @@ -1,9 +1,16 @@ use std::ops::Deref; +// Cyclic deref with the parent (which is not the top parent). 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; @@ -13,8 +20,99 @@ 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 = 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; + +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' +// 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; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @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; + + fn deref(&self) -> &Self::Target { + panic!() + } +} + +// @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; + + 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; + +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; + + 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!() 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..94f1adff62642 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-define-and-use.rs @@ -0,0 +1,19 @@ +// 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 + +#![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..1bcd64573ac6e --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-fields.rs @@ -0,0 +1,24 @@ +// 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 + +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..de5576682a6bd --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-glob-hygiene.rs @@ -0,0 +1,23 @@ +// 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; + +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..7369e77d0709e --- /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:21: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..0e6f57c33f64a --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-methods.rs @@ -0,0 +1,33 @@ +// 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 + +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-name-collision.rs b/src/test/ui/hygiene/cross-crate-name-collision.rs new file mode 100644 index 0000000000000..8f118782f2319 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-collision.rs @@ -0,0 +1,12 @@ +// 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 + +extern crate needs_hygiene; + +use needs_hygiene::*; + +fn main() {} 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..3eacd775c9e53 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding-2.rs @@ -0,0 +1,15 @@ +// 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; + +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..46314cdd5ab4e --- /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:13: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..dd76ecc5762f5 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-name-hiding.rs @@ -0,0 +1,13 @@ +// 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; + +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..f8840c8f85a33 --- /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:11: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..3cb06b4bad873 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-redefine.rs @@ -0,0 +1,14 @@ +// 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; + +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..4f1419de42677 --- /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:10: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..efc73a21f16f3 --- /dev/null +++ b/src/test/ui/hygiene/cross-crate-variants.rs @@ -0,0 +1,18 @@ +// 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 + +extern crate variants; + +use variants::*; + +fn main() { + check_variants(); + + test_variants!(); + test_variants2!(); + + assert_eq!(MyEnum::Variant as u8, 1); +} diff --git a/src/test/ui/hygiene/cross_crate_hygiene.rs b/src/test/ui/hygiene/cross_crate_hygiene.rs deleted file mode 100644 index 75742960b7e3c..0000000000000 --- a/src/test/ui/hygiene/cross_crate_hygiene.rs +++ /dev/null @@ -1,8 +0,0 @@ -// check-pass -// aux-build:needs_hygiene.rs - -extern crate needs_hygiene; - -use needs_hygiene::*; - -fn main() {} 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) 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() {} 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..c36b4967ee996 --- /dev/null +++ b/src/test/ui/typeck/issue-89806.stderr @@ -0,0 +1,9 @@ +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` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`.