From 688f4471fd553c83ae3ff0306956d89b7d7c2d28 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 14 Jul 2020 15:53:40 +0200 Subject: [PATCH 01/54] Stabilize future readiness fns --- library/core/src/future/mod.rs | 4 ++-- library/core/src/future/pending.rs | 20 +++++++++++++------- library/core/src/future/ready.rs | 13 ++++++++----- library/core/src/task/ready.rs | 2 -- library/std/src/future.rs | 2 +- library/std/src/lib.rs | 1 - 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 6d1ad9db74435..a69c4a0a6e91a 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -21,9 +21,9 @@ pub use self::future::Future; #[unstable(feature = "into_future", issue = "67644")] pub use into_future::IntoFuture; -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] pub use pending::{pending, Pending}; -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] pub use ready::{ready, Ready}; #[unstable(feature = "future_poll_fn", issue = "72302")] diff --git a/library/core/src/future/pending.rs b/library/core/src/future/pending.rs index 74887b68aa0fa..c1a4e0cda0320 100644 --- a/library/core/src/future/pending.rs +++ b/library/core/src/future/pending.rs @@ -1,3 +1,4 @@ +use crate::fmt::{self, Debug}; use crate::future::Future; use crate::marker; use crate::pin::Pin; @@ -10,8 +11,7 @@ use crate::task::{Context, Poll}; /// documentation for more. /// /// [`pending`]: fn.pending.html -#[unstable(feature = "future_readiness_fns", issue = "70921")] -#[derive(Debug)] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Pending { _data: marker::PhantomData, @@ -23,7 +23,6 @@ pub struct Pending { /// # Examples /// /// ```no_run -/// #![feature(future_readiness_fns)] /// use core::future; /// /// # async fn run() { @@ -32,12 +31,12 @@ pub struct Pending { /// unreachable!(); /// # } /// ``` -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] pub fn pending() -> Pending { Pending { _data: marker::PhantomData } } -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] impl Future for Pending { type Output = T; @@ -46,10 +45,17 @@ impl Future for Pending { } } -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] impl Unpin for Pending {} -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] +impl Debug for Pending { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Pending").finish() + } +} + +#[stable(feature = "future_readiness_fns", since = "1.47.0")] impl Clone for Pending { fn clone(&self) -> Self { pending() diff --git a/library/core/src/future/ready.rs b/library/core/src/future/ready.rs index 31b39d7fb6cd5..ddae6cfed4bdb 100644 --- a/library/core/src/future/ready.rs +++ b/library/core/src/future/ready.rs @@ -8,15 +8,15 @@ use crate::task::{Context, Poll}; /// documentation for more. /// /// [`ready`]: fn.ready.html -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Ready(Option); -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] impl Unpin for Ready {} -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] impl Future for Ready { type Output = T; @@ -28,10 +28,13 @@ impl Future for Ready { /// Creates a future that is immediately ready with a value. /// +/// Futures created through this function are functionally similar to those +/// created through `async {}`. The main difference is that futures created +/// through this function are named and implement `Unpin`. +/// /// # Examples /// /// ``` -/// #![feature(future_readiness_fns)] /// use core::future; /// /// # async fn run() { @@ -39,7 +42,7 @@ impl Future for Ready { /// assert_eq!(a.await, 1); /// # } /// ``` -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] pub fn ready(t: T) -> Ready { Ready(Some(t)) } diff --git a/library/core/src/task/ready.rs b/library/core/src/task/ready.rs index d4e733eb2bcf5..e221aaf3fd6d6 100644 --- a/library/core/src/task/ready.rs +++ b/library/core/src/task/ready.rs @@ -5,7 +5,6 @@ /// # Examples /// /// ``` -/// #![feature(future_readiness_fns)] /// #![feature(ready_macro)] /// /// use core::task::{ready, Context, Poll}; @@ -27,7 +26,6 @@ /// The `ready!` call expands to: /// /// ``` -/// # #![feature(future_readiness_fns)] /// # #![feature(ready_macro)] /// # /// # use core::task::{Context, Poll}; diff --git a/library/std/src/future.rs b/library/std/src/future.rs index 89dd9fb9b2cd5..5ff74e557547c 100644 --- a/library/std/src/future.rs +++ b/library/std/src/future.rs @@ -9,7 +9,7 @@ pub use core::future::Future; pub use core::future::{from_generator, get_context, ResumeTy}; #[doc(inline)] -#[unstable(feature = "future_readiness_fns", issue = "70921")] +#[stable(feature = "future_readiness_fns", since = "1.47.0")] pub use core::future::{pending, ready, Pending, Ready}; #[doc(inline)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f0487e0dff148..c820df5d7cc77 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -268,7 +268,6 @@ #![feature(external_doc)] #![feature(fn_traits)] #![feature(format_args_nl)] -#![feature(future_readiness_fns)] #![feature(gen_future)] #![feature(generator_trait)] #![feature(global_asm)] From 3797f29aadb51ed038e8b9eaf1b2098cfa26d547 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 11:41:18 -0400 Subject: [PATCH 02/54] [WIP] give better errors for broken intra doc links --- compiler/rustc_hir/src/def.rs | 9 + compiler/rustc_hir/src/lib.rs | 1 + .../passes/collect_intra_doc_links.rs | 361 ++++++++++++++---- .../deny-intra-link-resolution-failure.stderr | 3 +- .../rustdoc-ui/intra-doc-alias-ice.stderr | 5 +- src/test/rustdoc-ui/intra-link-errors.rs | 59 +++ src/test/rustdoc-ui/intra-link-errors.stderr | 68 ++++ .../intra-link-span-ice-55723.stderr | 3 +- .../intra-links-warning-crlf.stderr | 12 +- .../rustdoc-ui/intra-links-warning.stderr | 49 ++- 10 files changed, 476 insertions(+), 94 deletions(-) create mode 100644 src/test/rustdoc-ui/intra-link-errors.rs create mode 100644 src/test/rustdoc-ui/intra-link-errors.stderr diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 0d61dc037c65a..b019e518d0c54 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -6,6 +6,7 @@ use rustc_ast::NodeId; use rustc_macros::HashStable_Generic; use rustc_span::hygiene::MacroKind; +use std::array::IntoIter; use std::fmt::Debug; /// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct. @@ -291,6 +292,14 @@ impl PerNS { pub fn map U>(self, mut f: F) -> PerNS { PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) } } + + pub fn into_iter(self) -> IntoIter { + IntoIter::new([self.value_ns, self.type_ns, self.macro_ns]) + } + + pub fn iter(&self) -> IntoIter<&T, 3> { + IntoIter::new([&self.value_ns, &self.type_ns, &self.macro_ns]) + } } impl ::std::ops::Index for PerNS { diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index c69a9b063aeca..9d931b3a9e1e5 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -2,6 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +#![feature(array_value_iter)] #![feature(crate_visibility_modifier)] #![feature(const_fn)] // For the unsizing cast on `&[]` #![feature(const_panic)] diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5d10e2e149b32..f6f01028ee2e2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -17,7 +17,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; -use smallvec::SmallVec; +use smallvec::{smallvec, SmallVec}; use std::cell::Cell; use std::ops::Range; @@ -47,10 +47,53 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } enum ErrorKind { - ResolutionFailure, + Resolve(ResolutionFailure), AnchorFailure(AnchorFailure), } +#[derive(Debug)] +enum ResolutionFailure { + /// This resolved, but with the wrong namespace. + /// `Namespace` is the expected namespace (as opposed to the actual). + WrongNamespace(Res, Namespace), + /// `String` is the base name of the path (not necessarily the whole link) + NotInScope(String), + /// this is a primitive type without an impls (no associated methods) + /// when will this actually happen? + /// the `Res` is the primitive it resolved to + NoPrimitiveImpl(Res, String), + /// `[u8::not_found]` + /// the `Res` is the primitive it resolved to + NoPrimitiveAssocItem { res: Res, prim_name: String, assoc_item: String }, + /// `[S::not_found]` + /// the `String` is the associated item that wasn't found + NoAssocItem(Res, String), + /// should not ever happen + NoParentItem, + /// the root of this path resolved, but it was not an enum. + NotAnEnum(Res), + /// this could be an enum variant, but the last path fragment wasn't resolved. + /// the `String` is the variant that didn't exist + NotAVariant(Res, String), + /// used to communicate that this should be ignored, but shouldn't be reported to the user + Dummy, +} + +impl ResolutionFailure { + fn res(&self) -> Option { + use ResolutionFailure::*; + match self { + NoPrimitiveAssocItem { res, .. } + | NoAssocItem(res, _) + | NoPrimitiveImpl(res, _) + | NotAnEnum(res) + | NotAVariant(res, _) + | WrongNamespace(res, _) => Some(*res), + NotInScope(_) | NoParentItem | Dummy => None, + } + } +} + enum AnchorFailure { MultipleAnchors, Primitive, @@ -85,10 +128,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let cx = self.cx; let mut split = path_str.rsplitn(3, "::"); - let variant_field_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; + let variant_field_name = split + .next() + .map(|f| Symbol::intern(f)) + .expect("fold_item should ensure link is non-empty"); let variant_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; + // we're not sure this is a variant at all, so use the full string + split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.to_string())))?; + // TODO: this looks very wrong, why are we requiring 3 fields? let path = split .next() .map(|f| { @@ -99,14 +146,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::ResolutionFailure)?; + // TODO: is this right? + .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(variant_name.to_string())))?; let (_, ty_res) = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; + .map_err(|_| ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string())))?; if let Res::Err = ty_res { - return Err(ErrorKind::ResolutionFailure); + return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string()))); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -118,7 +166,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .flat_map(|imp| cx.tcx.associated_items(*imp).in_definition_order()) .any(|item| item.ident.name == variant_name) { - return Err(ErrorKind::ResolutionFailure); + // This is just to let `fold_item` know that this shouldn't be considered; + // it's a bug for the error to make it to the user + return Err(ErrorKind::Resolve(ResolutionFailure::Dummy)); } match cx.tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { @@ -131,18 +181,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { )), )) } else { - Err(ErrorKind::ResolutionFailure) + Err(ErrorKind::Resolve(ResolutionFailure::NotAVariant( + ty_res, + variant_field_name.to_string(), + ))) } } - _ => Err(ErrorKind::ResolutionFailure), + _ => unreachable!(), } } - _ => Err(ErrorKind::ResolutionFailure), + _ => Err(ErrorKind::Resolve(ResolutionFailure::NotAnEnum(ty_res))), } } /// Resolves a string as a macro. - fn macro_resolve(&self, path_str: &str, parent_id: Option) -> Option { + fn macro_resolve( + &self, + path_str: &str, + parent_id: Option, + ) -> Result { let cx = self.cx; let path = ast::Path::from_ident(Ident::from_str(path_str)); cx.enter_resolver(|resolver| { @@ -154,11 +211,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { false, ) { if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Some(res.map_id(|_| panic!("unexpected id"))); + return Ok(res.map_id(|_| panic!("unexpected id"))); } } if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { - return Some(res.map_id(|_| panic!("unexpected id"))); + return Ok(res.map_id(|_| panic!("unexpected id"))); } if let Some(module_id) = parent_id { debug!("resolving {} as a macro in the module {:?}", path_str, module_id); @@ -168,13 +225,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // don't resolve builtins like `#[derive]` if let Res::Def(..) = res { let res = res.map_id(|_| panic!("unexpected node_id")); - return Some(res); + return Ok(res); } } } else { debug!("attempting to resolve item without parent module: {}", path_str); + return Err(ResolutionFailure::NoParentItem); } - None + return Err(ResolutionFailure::NotInScope(path_str.to_string())); }) } /// Resolves a string as a path within a particular namespace. Also returns an optional @@ -196,8 +254,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }); debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); let result = match result { - Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure), - _ => result.map_err(|_| ErrorKind::ResolutionFailure), + Ok((_, Res::Err)) => Err(()), + _ => result.map_err(|_| ()), }; if let Ok((_, res)) = result { @@ -226,7 +284,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; if value != (ns == ValueNS) { - return Err(ErrorKind::ResolutionFailure); + return Err(ErrorKind::Resolve(ResolutionFailure::WrongNamespace(res, ns))); } } else if let Some((path, prim)) = is_primitive(path_str, ns) { if extra_fragment.is_some() { @@ -237,9 +295,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Try looking for methods and associated items. let mut split = path_str.rsplitn(2, "::"); - let item_name = - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::ResolutionFailure)?; - let path = split + // this can be an `unwrap()` because we ensure the link is never empty + let item_name = Symbol::intern(split.next().unwrap()); + let path_root = split .next() .map(|f| { if f == "self" || f == "Self" { @@ -249,10 +307,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::ResolutionFailure)?; - - if let Some((path, prim)) = is_primitive(&path, TypeNS) { - for &impl_ in primitive_impl(cx, &path).ok_or(ErrorKind::ResolutionFailure)? { + // If there's no `::`, it's not an associated item. + // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. + .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string())))?; + + if let Some((path, prim)) = is_primitive(&path_root, ns) { + let impls = primitive_impl(cx, &path).ok_or_else(|| { + ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root)) + })?; + for &impl_ in impls { let link = cx .tcx .associated_items(impl_) @@ -272,19 +335,25 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return Ok(link); } } - return Err(ErrorKind::ResolutionFailure); + return Err(ErrorKind::Resolve(ResolutionFailure::NoPrimitiveAssocItem { + res: prim, + prim_name: path.to_string(), + assoc_item: item_name.to_string(), + })); } let (_, ty_res) = cx .enter_resolver(|resolver| { - resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) + resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; + .map_err(|_| { + ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone())) + })?; if let Res::Err = ty_res { return if ns == Namespace::ValueNS { self.variant_field(path_str, current_item, module_id) } else { - Err(ErrorKind::ResolutionFailure) + Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root))) }; } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); @@ -380,7 +449,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } else { // We already know this isn't in ValueNS, so no need to check variant_field - return Err(ErrorKind::ResolutionFailure); + return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( + ty_res, + item_name.to_string(), + ))); } } Res::Def(DefKind::Trait, did) => cx @@ -419,12 +491,16 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { if ns == Namespace::ValueNS { self.variant_field(path_str, current_item, module_id) } else { - Err(ErrorKind::ResolutionFailure) + Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( + ty_res, + item_name.to_string(), + ))) } }) } else { debug!("attempting to resolve item without parent module: {}", path_str); - Err(ErrorKind::ResolutionFailure) + // TODO: maybe this should just be an ICE? + Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)) } } } @@ -562,10 +638,10 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx /// Check for resolve collisions between a trait and its derive /// /// These are common and we should just resolve to the trait in that case -fn is_derive_trait_collision(ns: &PerNS>) -> bool { +fn is_derive_trait_collision(ns: &PerNS>) -> bool { if let PerNS { - type_ns: Some((Res::Def(DefKind::Trait, _), _)), - macro_ns: Some((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), + type_ns: Ok((Res::Def(DefKind::Trait, _), _)), + macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), .. } = *ns { @@ -764,8 +840,15 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) { Ok(res) => res, - Err(ErrorKind::ResolutionFailure) => { - resolution_failure(cx, &item, path_str, &dox, link_range); + Err(ErrorKind::Resolve(kind)) => { + resolution_failure( + cx, + &item, + path_str, + &dox, + link_range, + smallvec![kind], + ); // This could just be a normal link or a broken link // we could potentially check if something is // "intra-doc-link-like" and warn in that case. @@ -792,13 +875,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { ) { Ok(res) => { debug!("got res in TypeNS: {:?}", res); - Some(res) + Ok(res) } Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - Err(ErrorKind::ResolutionFailure) => None, + Err(ErrorKind::Resolve(kind)) => Err(kind), }, value_ns: match self.resolve( path_str, @@ -807,48 +890,62 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { base_node, &extra_fragment, ) { - Ok(res) => Some(res), + Ok(res) => Ok(res), Err(ErrorKind::AnchorFailure(msg)) => { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - Err(ErrorKind::ResolutionFailure) => None, + Err(ErrorKind::Resolve(kind)) => Err(kind), } .and_then(|(res, fragment)| { // Constructors are picked up in the type namespace. match res { - Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => None, + Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => { + Err(ResolutionFailure::WrongNamespace(res, TypeNS)) + } _ => match (fragment, extra_fragment) { (Some(fragment), Some(_)) => { // Shouldn't happen but who knows? - Some((res, Some(fragment))) - } - (fragment, None) | (None, fragment) => { - Some((res, fragment)) + Ok((res, Some(fragment))) } + (fragment, None) | (None, fragment) => Ok((res, fragment)), }, } }), }; - if candidates.is_empty() { - resolution_failure(cx, &item, path_str, &dox, link_range); + let mut candidates_iter = + candidates.iter().filter_map(|res| res.as_ref().ok()); + let len = candidates_iter.clone().count(); + + if len == 0 { + drop(candidates_iter); + resolution_failure( + cx, + &item, + path_str, + &dox, + link_range, + candidates.into_iter().filter_map(|res| res.err()).collect(), + ); // this could just be a normal link continue; } - let len = candidates.clone().present_items().count(); - if len == 1 { - candidates.present_items().next().unwrap() + candidates_iter.next().unwrap().clone() } else if len == 2 && is_derive_trait_collision(&candidates) { + drop(candidates_iter); candidates.type_ns.unwrap() } else { + drop(candidates_iter); if is_derive_trait_collision(&candidates) { - candidates.macro_ns = None; + candidates.macro_ns = + Err(ResolutionFailure::NotInScope(path_str.to_string())); } + // If we're reporting an ambiguity, don't mention the namespaces that failed let candidates = - candidates.map(|candidate| candidate.map(|(res, _)| res)); + candidates.map(|candidate| candidate.ok().map(|(res, _)| res)); ambiguity_error( cx, &item, @@ -861,11 +958,44 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } Some(MacroNS) => { - if let Some(res) = self.macro_resolve(path_str, base_node) { - (res, extra_fragment) - } else { - resolution_failure(cx, &item, path_str, &dox, link_range); - continue; + match self.macro_resolve(path_str, base_node) { + Ok(res) => (res, extra_fragment), + Err(mut kind) => { + // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. + for &ns in &[TypeNS, ValueNS] { + match self.resolve( + path_str, + ns, + ¤t_item, + base_node, + &extra_fragment, + ) { + Ok(res) => { + kind = ResolutionFailure::WrongNamespace(res.0, MacroNS) + } + // This will show up in the other namespace, no need to handle it here + Err(ErrorKind::Resolve( + ResolutionFailure::WrongNamespace(..), + )) => {} + Err(ErrorKind::AnchorFailure(_)) => {} + Err(ErrorKind::Resolve(inner_kind)) => { + if let Some(res) = inner_kind.res() { + kind = + ResolutionFailure::WrongNamespace(res, MacroNS); + } + } + } + } + resolution_failure( + cx, + &item, + path_str, + &dox, + link_range, + smallvec![kind], + ); + continue; + } } } } @@ -907,7 +1037,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| { // The resolved item did not match the disambiguator; give a better error than 'not found' let msg = format!("incompatible link kind for `{}`", path_str); - report_diagnostic(cx, &msg, &item, &dox, link_range.clone(), |diag, sp| { + report_diagnostic(cx, &msg, &item, &dox, &link_range, |diag, sp| { let note = format!( "this link resolved to {} {}, which is not {} {}", resolved.article(), @@ -1161,7 +1291,7 @@ fn report_diagnostic( msg: &str, item: &Item, dox: &str, - link_range: Option>, + link_range: &Option>, decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option), ) { let hir_id = match cx.as_local_hir_id(item.def_id) { @@ -1218,19 +1348,107 @@ fn resolution_failure( path_str: &str, dox: &str, link_range: Option>, + kinds: SmallVec<[ResolutionFailure; 3]>, ) { report_diagnostic( cx, &format!("unresolved link to `{}`", path_str), item, dox, - link_range, + &link_range, |diag, sp| { - if let Some(sp) = sp { - diag.span_label(sp, "unresolved link"); + let in_scope = kinds.iter().any(|kind| kind.res().is_some()); + let mut reported_not_in_scope = false; + let item = |res: Res| { + if let Some(id) = res.opt_def_id() { + (format!("the {} `{}`", res.descr(), cx.tcx.item_name(id).to_string()), ",") + } else { + (format!("{} {}", res.article(), res.descr()), "") + } + }; + for failure in kinds { + match failure { + // already handled above + ResolutionFailure::NotInScope(base) => { + if in_scope || reported_not_in_scope { + continue; + } + reported_not_in_scope = true; + diag.note(&format!("no item named `{}` is in scope", base)); + diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); + } + ResolutionFailure::Dummy => continue, + ResolutionFailure::WrongNamespace(res, expected_ns) => { + let (item, comma) = item(res); + let note = format!( + "this link resolves to {}{} which is not in the {} namespace", + item, + comma, + expected_ns.descr() + ); + diag.note(¬e); + + if let Res::Def(kind, _) = res { + let disambiguator = Disambiguator::Kind(kind); + suggest_disambiguator( + disambiguator, + diag, + path_str, + dox, + sp, + &link_range, + ) + } + } + ResolutionFailure::NoParentItem => { + panic!("all intra doc links should have a parent item") + } + ResolutionFailure::NoPrimitiveImpl(res, _) => { + let (item, comma) = item(res); + let note = format!( + "this link partially resolves to {}{} which does not have any associated items", + item, comma, + ); + diag.note(¬e); + } + ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => { + let note = format!( + "the builtin type `{}` does not have an associated item named `{}`", + prim_name, assoc_item + ); + diag.note(¬e); + } + ResolutionFailure::NoAssocItem(res, assoc_item) => { + let (item, _) = item(res); + diag.note(&format!("this link partially resolves to {}", item)); + // FIXME: when are items neither a primitive nor a Def? + if let Res::Def(_, def_id) = res { + let name = cx.tcx.item_name(def_id); + let note = format!( + "`{}` has no field, variant, or associated item named `{}`", + name, assoc_item + ); + diag.note(¬e); + } + } + // TODO: is there ever a case where this happens? + ResolutionFailure::NotAnEnum(res) => { + let (item, comma) = item(res); + let note = + format!("this link resolves to {}{} which is not an enum", item, comma); + diag.note(¬e); + diag.note("if this were an enum, it might have a variant which resolved"); + } + ResolutionFailure::NotAVariant(res, variant) => { + let note = format!( + "this link partially resolves to {}, but there is no variant named {}", + item(res).0, + variant + ); + diag.note(¬e); + } + } } - - diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); }, ); } @@ -1269,7 +1487,7 @@ fn anchor_failure( } }; - report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| { + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "contains invalid anchor"); } @@ -1308,7 +1526,7 @@ fn ambiguity_error( } } - report_diagnostic(cx, &msg, item, dox, link_range.clone(), |diag, sp| { + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "ambiguous link"); } else { @@ -1356,7 +1574,7 @@ fn privacy_error( let msg = format!("public documentation for `{}` links to private item `{}`", item_name, path_str); - report_diagnostic(cx, &msg, item, dox, link_range, |diag, sp| { + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "this item is private"); } @@ -1384,7 +1602,8 @@ fn handle_variant( let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent } else { - return Err(ErrorKind::ResolutionFailure); + // TODO: this should just be an unwrap, there should never be `Variant`s without a parent + return Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)); }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index 7530e3ad0f551..4ae53e83613e0 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -2,13 +2,14 @@ error: unresolved link to `v2` --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] - | ^^ unresolved link + | ^^ | note: the lint level is defined here --> $DIR/deny-intra-link-resolution-failure.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ + = note: no item named `v2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index f1c07e31cd753..f5eb3a15abcc7 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -2,14 +2,15 @@ error: unresolved link to `TypeAlias::hoge` --> $DIR/intra-doc-alias-ice.rs:5:30 | LL | /// [broken cross-reference](TypeAlias::hoge) - | ^^^^^^^^^^^^^^^ unresolved link + | ^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/intra-doc-alias-ice.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: this link partially resolves to the type alias `TypeAlias`, + = note: `TypeAlias` has no field, variant, or associated item named `hoge` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs new file mode 100644 index 0000000000000..7a53a6f079334 --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -0,0 +1,59 @@ +#![deny(broken_intra_doc_links)] +//~^ NOTE lint level is defined + +//! [std::io::oops] +//! [std::io::oops::not::here] + +// FIXME: this should say that it was skipped (maybe an allowed by default lint?) +/// [] + +// FIXME: this could say which path was the first to not be found (in this case, `path`) +/// [path::to::nonexistent::module] +//~^ ERROR unresolved link +//~| NOTE no item named `path::to::nonexistent` is in scope +//~| HELP to escape + +// TODO: why does this say `f` and not `f::A`?? +/// [f::A] +//~^ ERROR unresolved link +//~| NOTE no item named `f` is in scope +//~| HELP to escape + +/// [S::A] +//~^ ERROR unresolved link +//~| NOTE this link partially resolves +//~| NOTE `S` has no field + +/// [S::fmt] +//~^ ERROR unresolved link +//~| NOTE this link partially resolves +//~| NOTE `S` has no field + +/// [E::D] +//~^ ERROR unresolved link +//~| NOTE this link partially resolves +//~| NOTE `E` has no field + +/// [u8::not_found] +//~^ ERROR unresolved link +//~| NOTE the builtin type `u8` does not have an associated item named `not_found` + +/// [S!] +//~^ ERROR unresolved link +//~| HELP to link to the unit struct, use its disambiguator +//~| NOTE this link resolves to the unit struct `S` +pub fn f() {} +#[derive(Debug)] +pub struct S; + +pub enum E { A, B, C } + +/// [type@S::h] +impl S { + pub fn h() {} +} + +/// [type@T::g] +pub trait T { + fn g() {} +} diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr new file mode 100644 index 0000000000000..249b27cd87894 --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -0,0 +1,68 @@ +error: unresolved link to `path::to::nonexistent::module` + --> $DIR/intra-link-errors.rs:8:6 + | +LL | /// [path::to::nonexistent::module] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/intra-link-errors.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: no item named `path::to::nonexistent` is in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `f::A` + --> $DIR/intra-link-errors.rs:14:6 + | +LL | /// [f::A] + | ^^^^ + | + = note: no item named `f` is in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `S::A` + --> $DIR/intra-link-errors.rs:19:6 + | +LL | /// [S::A] + | ^^^^ + | + = note: this link partially resolves to the struct `S`, + = note: `S` has no field, variant, or associated item named `A` + +error: unresolved link to `S::fmt` + --> $DIR/intra-link-errors.rs:24:6 + | +LL | /// [S::fmt] + | ^^^^^^ + | + = note: this link partially resolves to the struct `S`, + = note: `S` has no field, variant, or associated item named `fmt` + +error: unresolved link to `E::D` + --> $DIR/intra-link-errors.rs:29:6 + | +LL | /// [E::D] + | ^^^^ + | + = note: this link partially resolves to the enum `E`, + = note: `E` has no field, variant, or associated item named `D` + +error: unresolved link to `u8::not_found` + --> $DIR/intra-link-errors.rs:34:6 + | +LL | /// [u8::not_found] + | ^^^^^^^^^^^^^ + | + = note: the builtin type `u8` does not have an associated item named `not_found` + +error: unresolved link to `S` + --> $DIR/intra-link-errors.rs:38:6 + | +LL | /// [S!] + | ^^ help: to link to the unit struct, use its disambiguator: `value@S` + | + = note: this link resolves to the unit struct `S`, which is not in the value namespace + +error: aborting due to 7 previous errors + diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index 6b0ff8f116295..47b6a08baf37d 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr @@ -2,13 +2,14 @@ error: unresolved link to `i` --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) - | ^ unresolved link + | ^ | note: the lint level is defined here --> $DIR/intra-link-span-ice-55723.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ + = note: no item named `i` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index 1e3a26fadfa9f..1da27b786180f 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -2,33 +2,37 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] - | ^^^^^ unresolved link + | ^^^^^ | = note: `#[warn(broken_intra_doc_links)]` on by default + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error1` --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `error1` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error2` --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `error2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. - | ^^^^^ unresolved link + | ^^^^^ | + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: 4 warnings emitted diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 53f2476295ebb..f728d3919e670 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -2,73 +2,82 @@ warning: unresolved link to `Foo::baz` --> $DIR/intra-links-warning.rs:3:23 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ unresolved link + | ^^^^^^^^ | = note: `#[warn(broken_intra_doc_links)]` on by default - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: this link partially resolves to the struct `Foo`, + = note: `Foo` has no field, variant, or associated item named `baz` warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ unresolved link + | ^^^^^^^^ | + = note: no item named `Bar` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ unresolved link + | ^^^^^^^^^^ | + = note: no item named `Uniooon` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `Qux` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ unresolved link + | ^^^^^^^^^^ | + = note: no item named `Uniooon` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `Qux` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ unresolved link + | ^^^^^ | + = note: no item named `Qux:Y` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ unresolved link + | ^^^^^ | + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ unresolved link + | ^^^^^ | + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -81,6 +90,7 @@ LL | #[doc = "single line [error]"] single line [error] ^^^^^ + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -93,6 +103,7 @@ LL | #[doc = "single line with \"escaping\" [error]"] single line with "escaping" [error] ^^^^^ + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -107,46 +118,52 @@ LL | | /// [error] [error] ^^^^^ + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error1` --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `error1` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ unresolved link + | ^^^^^^ | + = note: no item named `error2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ unresolved link + | ^^^^ | + = note: no item named `BarA` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ unresolved link + | ^^^^ | + = note: no item named `BarB` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ unresolved link + | ^^^^ | + = note: no item named `BarC` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarD` @@ -159,6 +176,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] bar [BarD] bar ^^^^ + = note: no item named `BarD` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` @@ -174,6 +192,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); bar [BarF] bar ^^^^ + = note: no item named `BarF` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) From f45e7b586259b795babf49231cd7de4a4534a2e7 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 21:15:32 -0400 Subject: [PATCH 03/54] Update .stderr --- .../rustdoc-ui/intra-doc-alias-ice.stderr | 2 +- src/test/rustdoc-ui/intra-link-errors.stderr | 68 +++++++++++++++---- .../rustdoc-ui/intra-links-warning.stderr | 2 +- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index f5eb3a15abcc7..cc1343eb7b95e 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: this link partially resolves to the type alias `TypeAlias`, + = note: this link partially resolves to the type alias `TypeAlias` = note: `TypeAlias` has no field, variant, or associated item named `hoge` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 249b27cd87894..baf4ab7a9bc46 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -1,19 +1,39 @@ -error: unresolved link to `path::to::nonexistent::module` - --> $DIR/intra-link-errors.rs:8:6 +error: unresolved link to `std::io::oops` + --> $DIR/intra-link-errors.rs:4:6 | -LL | /// [path::to::nonexistent::module] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | //! [std::io::oops] + | ^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/intra-link-errors.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this link resolves to the crate `std`, which is not an enum + = note: if this were an enum, it might have a variant which resolved + = note: this link partially resolves to the module `io` + = note: `io` has no field, variant, or associated item named `oops` + +error: unresolved link to `std::io::oops::not::here` + --> $DIR/intra-link-errors.rs:5:6 + | +LL | //! [std::io::oops::not::here] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: no item named `std::io::oops::not` is in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + +error: unresolved link to `path::to::nonexistent::module` + --> $DIR/intra-link-errors.rs:11:6 + | +LL | /// [path::to::nonexistent::module] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | = note: no item named `path::to::nonexistent` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:14:6 + --> $DIR/intra-link-errors.rs:17:6 | LL | /// [f::A] | ^^^^ @@ -22,34 +42,34 @@ LL | /// [f::A] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:19:6 + --> $DIR/intra-link-errors.rs:22:6 | LL | /// [S::A] | ^^^^ | - = note: this link partially resolves to the struct `S`, + = note: this link partially resolves to the struct `S` = note: `S` has no field, variant, or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:24:6 + --> $DIR/intra-link-errors.rs:27:6 | LL | /// [S::fmt] | ^^^^^^ | - = note: this link partially resolves to the struct `S`, + = note: this link partially resolves to the struct `S` = note: `S` has no field, variant, or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:29:6 + --> $DIR/intra-link-errors.rs:32:6 | LL | /// [E::D] | ^^^^ | - = note: this link partially resolves to the enum `E`, + = note: this link partially resolves to the enum `E` = note: `E` has no field, variant, or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:34:6 + --> $DIR/intra-link-errors.rs:37:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -57,12 +77,30 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:38:6 + --> $DIR/intra-link-errors.rs:41:6 | LL | /// [S!] | ^^ help: to link to the unit struct, use its disambiguator: `value@S` | - = note: this link resolves to the unit struct `S`, which is not in the value namespace + = note: this link resolves to the unit struct `S`, which is not in the macro namespace + +error: unresolved link to `T::g` + --> $DIR/intra-link-errors.rs:56:6 + | +LL | /// [type@T::g] + | ^^^^^^^^^ + | + = note: this link partially resolves to the trait `T` + = note: `T` has no field, variant, or associated item named `g` + +error: unresolved link to `S::h` + --> $DIR/intra-link-errors.rs:51:6 + | +LL | /// [type@S::h] + | ^^^^^^^^^ + | + = note: this link partially resolves to the struct `S` + = note: `S` has no field, variant, or associated item named `h` -error: aborting due to 7 previous errors +error: aborting due to 11 previous errors diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index f728d3919e670..3737cad1cb187 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -5,7 +5,7 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... | ^^^^^^^^ | = note: `#[warn(broken_intra_doc_links)]` on by default - = note: this link partially resolves to the struct `Foo`, + = note: this link partially resolves to the struct `Foo` = note: `Foo` has no field, variant, or associated item named `baz` warning: unresolved link to `Bar::foo` From 4ace4e75201d5bfc187bd40cf918cd2df6009419 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 21:29:14 -0400 Subject: [PATCH 04/54] Use fewer `.to_string()`s --- .../passes/collect_intra_doc_links.rs | 81 ++++++++++--------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index f6f01028ee2e2..9bf30c71892aa 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -19,6 +19,7 @@ use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; use smallvec::{smallvec, SmallVec}; +use std::borrow::Cow; use std::cell::Cell; use std::ops::Range; @@ -46,40 +47,40 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } } -enum ErrorKind { - Resolve(ResolutionFailure), +enum ErrorKind<'a> { + Resolve(ResolutionFailure<'a>), AnchorFailure(AnchorFailure), } #[derive(Debug)] -enum ResolutionFailure { +enum ResolutionFailure<'a> { /// This resolved, but with the wrong namespace. /// `Namespace` is the expected namespace (as opposed to the actual). WrongNamespace(Res, Namespace), /// `String` is the base name of the path (not necessarily the whole link) - NotInScope(String), + NotInScope(Cow<'a, str>), /// this is a primitive type without an impls (no associated methods) /// when will this actually happen? /// the `Res` is the primitive it resolved to NoPrimitiveImpl(Res, String), /// `[u8::not_found]` /// the `Res` is the primitive it resolved to - NoPrimitiveAssocItem { res: Res, prim_name: String, assoc_item: String }, + NoPrimitiveAssocItem { res: Res, prim_name: &'a str, assoc_item: Symbol }, /// `[S::not_found]` /// the `String` is the associated item that wasn't found - NoAssocItem(Res, String), + NoAssocItem(Res, Symbol), /// should not ever happen NoParentItem, /// the root of this path resolved, but it was not an enum. NotAnEnum(Res), /// this could be an enum variant, but the last path fragment wasn't resolved. /// the `String` is the variant that didn't exist - NotAVariant(Res, String), + NotAVariant(Res, Symbol), /// used to communicate that this should be ignored, but shouldn't be reported to the user Dummy, } -impl ResolutionFailure { +impl ResolutionFailure<'a> { fn res(&self) -> Option { use ResolutionFailure::*; match self { @@ -121,10 +122,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { fn variant_field( &self, - path_str: &str, + path_str: &'path str, current_item: &Option, module_id: DefId, - ) -> Result<(Res, Option), ErrorKind> { + ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; let mut split = path_str.rsplitn(3, "::"); @@ -134,7 +135,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .expect("fold_item should ensure link is non-empty"); let variant_name = // we're not sure this is a variant at all, so use the full string - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.to_string())))?; + split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.into())))?; // TODO: this looks very wrong, why are we requiring 3 fields? let path = split .next() @@ -147,14 +148,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { f.to_owned() }) // TODO: is this right? - .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(variant_name.to_string())))?; + .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( + variant_name.to_string().into(), + )))?; let (_, ty_res) = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) - .map_err(|_| ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string())))?; + .map_err(|_| { + ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string().into())) + })?; if let Res::Err = ty_res { - return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string()))); + return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string().into()))); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -183,7 +188,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } else { Err(ErrorKind::Resolve(ResolutionFailure::NotAVariant( ty_res, - variant_field_name.to_string(), + variant_field_name, ))) } } @@ -197,9 +202,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// Resolves a string as a macro. fn macro_resolve( &self, - path_str: &str, + path_str: &'a str, parent_id: Option, - ) -> Result { + ) -> Result> { let cx = self.cx; let path = ast::Path::from_ident(Ident::from_str(path_str)); cx.enter_resolver(|resolver| { @@ -232,19 +237,19 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { debug!("attempting to resolve item without parent module: {}", path_str); return Err(ResolutionFailure::NoParentItem); } - return Err(ResolutionFailure::NotInScope(path_str.to_string())); + return Err(ResolutionFailure::NotInScope(path_str.into())); }) } /// Resolves a string as a path within a particular namespace. Also returns an optional /// URL fragment in the case of variants and methods. - fn resolve( + fn resolve<'path>( &self, - path_str: &str, + path_str: &'path str, ns: Namespace, current_item: &Option, parent_id: Option, extra_fragment: &Option, - ) -> Result<(Res, Option), ErrorKind> { + ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; // In case we're in a module, try to resolve the relative path. @@ -309,11 +314,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) // If there's no `::`, it's not an associated item. // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. - .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string())))?; + .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( + item_name.to_string().into(), + )))?; if let Some((path, prim)) = is_primitive(&path_root, ns) { let impls = primitive_impl(cx, &path).ok_or_else(|| { - ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root)) + ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root.into())) })?; for &impl_ in impls { let link = cx @@ -337,8 +344,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } return Err(ErrorKind::Resolve(ResolutionFailure::NoPrimitiveAssocItem { res: prim, - prim_name: path.to_string(), - assoc_item: item_name.to_string(), + prim_name: path, + assoc_item: item_name, })); } @@ -347,13 +354,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) .map_err(|_| { - ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone())) + ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone().into())) })?; if let Res::Err = ty_res { return if ns == Namespace::ValueNS { self.variant_field(path_str, current_item, module_id) } else { - Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root))) + Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.into()))) }; } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); @@ -450,8 +457,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } else { // We already know this isn't in ValueNS, so no need to check variant_field return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( - ty_res, - item_name.to_string(), + ty_res, item_name, ))); } } @@ -491,10 +497,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { if ns == Namespace::ValueNS { self.variant_field(path_str, current_item, module_id) } else { - Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( - ty_res, - item_name.to_string(), - ))) + Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem(ty_res, item_name))) } }) } else { @@ -638,7 +641,7 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx /// Check for resolve collisions between a trait and its derive /// /// These are common and we should just resolve to the trait in that case -fn is_derive_trait_collision(ns: &PerNS>) -> bool { +fn is_derive_trait_collision(ns: &PerNS>>) -> bool { if let PerNS { type_ns: Ok((Res::Def(DefKind::Trait, _), _)), macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), @@ -941,7 +944,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { drop(candidates_iter); if is_derive_trait_collision(&candidates) { candidates.macro_ns = - Err(ResolutionFailure::NotInScope(path_str.to_string())); + Err(ResolutionFailure::NotInScope(path_str.into())); } // If we're reporting an ambiguity, don't mention the namespaces that failed let candidates = @@ -1348,7 +1351,7 @@ fn resolution_failure( path_str: &str, dox: &str, link_range: Option>, - kinds: SmallVec<[ResolutionFailure; 3]>, + kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { report_diagnostic( cx, @@ -1494,10 +1497,10 @@ fn anchor_failure( }); } -fn ambiguity_error( +fn ambiguity_error<'a>( cx: &DocContext<'_>, item: &Item, - path_str: &str, + path_str: &'a str, dox: &str, link_range: Option>, candidates: Vec, @@ -1593,7 +1596,7 @@ fn handle_variant( cx: &DocContext<'_>, res: Res, extra_fragment: &Option, -) -> Result<(Res, Option), ErrorKind> { +) -> Result<(Res, Option), ErrorKind<'static>> { use rustc_middle::ty::DefIdTree; if extra_fragment.is_some() { From fcb21999a7989792c9405a89e9c788ac143e0574 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 22:31:44 -0400 Subject: [PATCH 05/54] Report if the thing exists in another namespace --- .../passes/collect_intra_doc_links.rs | 65 ++++++++++++------- src/test/rustdoc-ui/intra-link-errors.rs | 1 + 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9bf30c71892aa..5013257736719 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -81,6 +81,7 @@ enum ResolutionFailure<'a> { } impl ResolutionFailure<'a> { + // A partial or full resolution fn res(&self) -> Option { use ResolutionFailure::*; match self { @@ -93,6 +94,14 @@ impl ResolutionFailure<'a> { NotInScope(_) | NoParentItem | Dummy => None, } } + + // This resolved fully (not just partially) but is erroneous for some other reason + fn full_res(&self) -> Option { + match self { + Self::WrongNamespace(res, _) => Some(*res), + _ => None, + } + } } enum AnchorFailure { @@ -128,6 +137,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; + debug!("looking for enum variant {}", path_str); let mut split = path_str.rsplitn(3, "::"); let variant_field_name = split .next() @@ -260,7 +270,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); let result = match result { Ok((_, Res::Err)) => Err(()), - _ => result.map_err(|_| ()), + x => x, }; if let Ok((_, res)) = result { @@ -419,6 +429,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Ok((ty_res, Some(format!("{}.{}", out, item_name)))) }) } else if ns == Namespace::ValueNS { + debug!("looking for variants or fields named {} for {:?}", item_name, did); match cx.tcx.type_of(did).kind() { ty::Adt(def, _) => { let field = if def.is_enum() { @@ -838,12 +849,36 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } + // used for reporting better errors + let check_full_res = |this: &mut Self, ns| { + match this.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) { + Ok(res) => { + debug!( + "check_full_res: saw res for {} in {:?} ns: {:?}", + path_str, ns, res.0 + ); + Some(res.0) + } + Err(ErrorKind::Resolve(kind)) => kind.full_res(), + // TODO: add `Res` to AnchorFailure + Err(ErrorKind::AnchorFailure(_)) => None, + } + }; + match disambiguator.map(Disambiguator::ns) { Some(ns @ (ValueNS | TypeNS)) => { match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) { Ok(res) => res, - Err(ErrorKind::Resolve(kind)) => { + Err(ErrorKind::Resolve(mut kind)) => { + // We only looked in one namespace. Try to give a better error if possible. + // TODO: handle MacroNS too + if kind.full_res().is_none() { + let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; + if let Some(res) = check_full_res(self, other_ns) { + kind = ResolutionFailure::WrongNamespace(res, other_ns); + } + } resolution_failure( cx, &item, @@ -965,30 +1000,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Ok(res) => (res, extra_fragment), Err(mut kind) => { // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. + //if kind.res().is_none() { for &ns in &[TypeNS, ValueNS] { - match self.resolve( - path_str, - ns, - ¤t_item, - base_node, - &extra_fragment, - ) { - Ok(res) => { - kind = ResolutionFailure::WrongNamespace(res.0, MacroNS) - } - // This will show up in the other namespace, no need to handle it here - Err(ErrorKind::Resolve( - ResolutionFailure::WrongNamespace(..), - )) => {} - Err(ErrorKind::AnchorFailure(_)) => {} - Err(ErrorKind::Resolve(inner_kind)) => { - if let Some(res) = inner_kind.res() { - kind = - ResolutionFailure::WrongNamespace(res, MacroNS); - } - } + if let Some(res) = check_full_res(self, ns) { + kind = ResolutionFailure::WrongNamespace(res, MacroNS); + break; } } + //} resolution_failure( cx, &item, diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 7a53a6f079334..99d080fb324e3 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -54,6 +54,7 @@ impl S { } /// [type@T::g] +/// [T::h!] pub trait T { fn g() {} } From 42bed035001b9a0bd6282326a243a926ea3d0424 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 23:05:47 -0400 Subject: [PATCH 06/54] Pass on the DefId so rustdoc can name it in suggestions Look at this beauty: ```rust error: unresolved link to `S::h` --> intra-link-errors.rs:51:6 | 51 | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` | = note: this link resolves to the associated function `h`, which is not in the type namespace ``` --- .../passes/collect_intra_doc_links.rs | 52 ++++++++++--------- src/test/rustdoc-ui/intra-link-errors.rs | 7 +++ src/test/rustdoc-ui/intra-link-errors.stderr | 27 ++++++---- 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5013257736719..287f5fcf805ef 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -121,7 +121,7 @@ struct LinkCollector<'a, 'tcx> { /// This is used to store the kind of associated items, /// because `clean` and the disambiguator code expect them to be different. /// See the code for associated items on inherent impls for details. - kind_side_channel: Cell>, + kind_side_channel: Cell>, } impl<'a, 'tcx> LinkCollector<'a, 'tcx> { @@ -381,7 +381,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ) => { debug!("looking for associated item named {} for item {:?}", item_name, did); // Checks if item_name belongs to `impl SomeItem` - let kind = cx + let assoc_item = cx .tcx .inherent_impls(did) .iter() @@ -393,7 +393,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { imp, ) }) - .map(|item| item.kind) + .map(|item| (item.kind, item.def_id)) // There should only ever be one associated item that matches from any inherent impl .next() // Check if item_name belongs to `impl SomeTrait for SomeItem` @@ -409,7 +409,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { kind }); - if let Some(kind) = kind { + if let Some((kind, id)) = assoc_item { let out = match kind { ty::AssocKind::Fn => "method", ty::AssocKind::Const => "associatedconstant", @@ -425,7 +425,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // HACK(jynelson): `clean` expects the type, not the associated item. // but the disambiguator logic expects the associated item. // Store the kind in a side channel so that only the disambiguator logic looks at it. - self.kind_side_channel.set(Some(kind.as_def_kind())); + self.kind_side_channel.set(Some((kind.as_def_kind(), id))); Ok((ty_res, Some(format!("{}.{}", out, item_name)))) }) } else if ns == Namespace::ValueNS { @@ -525,7 +525,7 @@ fn resolve_associated_trait_item( item_name: Symbol, ns: Namespace, cx: &DocContext<'_>, -) -> Option { +) -> Option<(ty::AssocKind, DefId)> { let ty = cx.tcx.type_of(did); // First consider automatic impls: `impl From for T` let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did); @@ -553,7 +553,7 @@ fn resolve_associated_trait_item( // but provided methods come directly from `tcx`. // Fortunately, we don't need the whole method, we just need to know // what kind of associated item it is. - Some((assoc.def_id, kind)) + Some((kind, assoc.def_id)) }); let assoc = items.next(); debug_assert_eq!(items.count(), 0); @@ -575,7 +575,7 @@ fn resolve_associated_trait_item( ns, trait_, ) - .map(|assoc| (assoc.def_id, assoc.kind)) + .map(|assoc| (assoc.kind, assoc.def_id)) } } _ => panic!("get_impls returned something that wasn't an impl"), @@ -592,12 +592,12 @@ fn resolve_associated_trait_item( cx.tcx .associated_items(trait_) .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_) - .map(|assoc| (assoc.def_id, assoc.kind)) + .map(|assoc| (assoc.kind, assoc.def_id)) })); } // FIXME: warn about ambiguity debug!("the candidates were {:?}", candidates); - candidates.pop().map(|(_, kind)| kind) + candidates.pop() } /// Given a type, return all traits in scope in `module` implemented by that type. @@ -851,18 +851,21 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { // used for reporting better errors let check_full_res = |this: &mut Self, ns| { - match this.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) { - Ok(res) => { - debug!( - "check_full_res: saw res for {} in {:?} ns: {:?}", - path_str, ns, res.0 - ); - Some(res.0) - } - Err(ErrorKind::Resolve(kind)) => kind.full_res(), - // TODO: add `Res` to AnchorFailure - Err(ErrorKind::AnchorFailure(_)) => None, - } + let res = + match this.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) + { + Ok(res) => { + debug!( + "check_full_res: saw res for {} in {:?} ns: {:?}", + path_str, ns, res.0 + ); + Some(res.0) + } + Err(ErrorKind::Resolve(kind)) => kind.full_res(), + // TODO: add `Res` to AnchorFailure + Err(ErrorKind::AnchorFailure(_)) => None, + }; + this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) }; match disambiguator.map(Disambiguator::ns) { @@ -876,7 +879,8 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { if kind.full_res().is_none() { let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; if let Some(res) = check_full_res(self, other_ns) { - kind = ResolutionFailure::WrongNamespace(res, other_ns); + // recall that this stores the _expected_ namespace + kind = ResolutionFailure::WrongNamespace(res, ns); } } resolution_failure( @@ -1092,7 +1096,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { // Disallow e.g. linking to enums with `struct@` if let Res::Def(kind, _) = res { debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator); - match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) { + match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) { | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const))) // NOTE: this allows 'method' to mean both normal functions and associated functions // This can't cause ambiguity because both are in the same namespace. diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 99d080fb324e3..8c42a38ff4eb0 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -49,11 +49,18 @@ pub struct S; pub enum E { A, B, C } /// [type@S::h] +//~^ ERROR unresolved link +//~| HELP to link to the associated function +//~| NOTE not in the type namespace impl S { pub fn h() {} } /// [type@T::g] +//~^ ERROR unresolved link +//~| HELP to link to the associated function +//~| NOTE not in the type namespace + /// [T::h!] pub trait T { fn g() {} diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index baf4ab7a9bc46..bb9db68e0d5e1 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -80,27 +80,34 @@ error: unresolved link to `S` --> $DIR/intra-link-errors.rs:41:6 | LL | /// [S!] - | ^^ help: to link to the unit struct, use its disambiguator: `value@S` + | ^^ help: to link to the struct, use its disambiguator: `struct@S` | - = note: this link resolves to the unit struct `S`, which is not in the macro namespace + = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:56:6 + --> $DIR/intra-link-errors.rs:59:6 | LL | /// [type@T::g] - | ^^^^^^^^^ + | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()` | - = note: this link partially resolves to the trait `T` - = note: `T` has no field, variant, or associated item named `g` + = note: this link resolves to the associated function `g`, which is not in the type namespace + +error: unresolved link to `T::h` + --> $DIR/intra-link-errors.rs:64:6 + | +LL | /// [T::h!] + | ^^^^^ + | + = note: no item named `T::h` is in scope + = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::h` --> $DIR/intra-link-errors.rs:51:6 | LL | /// [type@S::h] - | ^^^^^^^^^ + | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` | - = note: this link partially resolves to the struct `S` - = note: `S` has no field, variant, or associated item named `h` + = note: this link resolves to the associated function `h`, which is not in the type namespace -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From f4e6ebd11aaea9fc412d78371fd3060468625056 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 20 Aug 2020 23:14:41 -0400 Subject: [PATCH 07/54] Fix tests and improve error message if `::` isn't found --- .../passes/collect_intra_doc_links.rs | 13 +++-- .../rustdoc-ui/intra-doc-alias-ice.stderr | 2 +- src/test/rustdoc-ui/intra-link-errors.rs | 16 +++--- src/test/rustdoc-ui/intra-link-errors.stderr | 54 ++++++------------- .../rustdoc-ui/intra-links-warning.stderr | 2 +- 5 files changed, 33 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 287f5fcf805ef..ea3ec1b6685cc 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -324,9 +324,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) // If there's no `::`, it's not an associated item. // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. - .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( - item_name.to_string().into(), - )))?; + .ok_or_else(|| { + debug!("found no `::`, assumming {} was correctly not in scope", item_name); + ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string().into())) + })?; if let Some((path, prim)) = is_primitive(&path_root, ns) { let impls = primitive_impl(cx, &path).ok_or_else(|| { @@ -361,6 +362,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let (_, ty_res) = cx .enter_resolver(|resolver| { + // only types can have associated items resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) .map_err(|_| { @@ -1450,10 +1452,7 @@ fn resolution_failure( // FIXME: when are items neither a primitive nor a Def? if let Res::Def(_, def_id) = res { let name = cx.tcx.item_name(def_id); - let note = format!( - "`{}` has no field, variant, or associated item named `{}`", - name, assoc_item - ); + let note = format!("no `{}` in `{}`", assoc_item, name,); diag.note(¬e); } } diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index cc1343eb7b95e..58abe4406a85d 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: this link partially resolves to the type alias `TypeAlias` - = note: `TypeAlias` has no field, variant, or associated item named `hoge` + = note: no `hoge` in `TypeAlias` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 8c42a38ff4eb0..79ce57c22676f 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -1,9 +1,6 @@ #![deny(broken_intra_doc_links)] //~^ NOTE lint level is defined -//! [std::io::oops] -//! [std::io::oops::not::here] - // FIXME: this should say that it was skipped (maybe an allowed by default lint?) /// [] @@ -22,17 +19,17 @@ /// [S::A] //~^ ERROR unresolved link //~| NOTE this link partially resolves -//~| NOTE `S` has no field +//~| NOTE no `A` in `S` /// [S::fmt] //~^ ERROR unresolved link //~| NOTE this link partially resolves -//~| NOTE `S` has no field +//~| NOTE no `fmt` in `S` /// [E::D] //~^ ERROR unresolved link //~| NOTE this link partially resolves -//~| NOTE `E` has no field +//~| NOTE no `D` in `E` /// [u8::not_found] //~^ ERROR unresolved link @@ -40,8 +37,8 @@ /// [S!] //~^ ERROR unresolved link -//~| HELP to link to the unit struct, use its disambiguator -//~| NOTE this link resolves to the unit struct `S` +//~| HELP to link to the struct, use its disambiguator +//~| NOTE this link resolves to the struct `S` pub fn f() {} #[derive(Debug)] pub struct S; @@ -62,6 +59,9 @@ impl S { //~| NOTE not in the type namespace /// [T::h!] +//~^ ERROR unresolved link +//~| NOTE no item named `T::h` +//~| HELP to escape pub trait T { fn g() {} } diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index bb9db68e0d5e1..4044c7fde66f2 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -1,39 +1,19 @@ -error: unresolved link to `std::io::oops` - --> $DIR/intra-link-errors.rs:4:6 +error: unresolved link to `path::to::nonexistent::module` + --> $DIR/intra-link-errors.rs:8:6 | -LL | //! [std::io::oops] - | ^^^^^^^^^^^^^ +LL | /// [path::to::nonexistent::module] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/intra-link-errors.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: this link resolves to the crate `std`, which is not an enum - = note: if this were an enum, it might have a variant which resolved - = note: this link partially resolves to the module `io` - = note: `io` has no field, variant, or associated item named `oops` - -error: unresolved link to `std::io::oops::not::here` - --> $DIR/intra-link-errors.rs:5:6 - | -LL | //! [std::io::oops::not::here] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: no item named `std::io::oops::not` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` - -error: unresolved link to `path::to::nonexistent::module` - --> $DIR/intra-link-errors.rs:11:6 - | -LL | /// [path::to::nonexistent::module] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | = note: no item named `path::to::nonexistent` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:17:6 + --> $DIR/intra-link-errors.rs:14:6 | LL | /// [f::A] | ^^^^ @@ -42,34 +22,34 @@ LL | /// [f::A] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:22:6 + --> $DIR/intra-link-errors.rs:19:6 | LL | /// [S::A] | ^^^^ | = note: this link partially resolves to the struct `S` - = note: `S` has no field, variant, or associated item named `A` + = note: no `A` in `S` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:27:6 + --> $DIR/intra-link-errors.rs:24:6 | LL | /// [S::fmt] | ^^^^^^ | = note: this link partially resolves to the struct `S` - = note: `S` has no field, variant, or associated item named `fmt` + = note: no `fmt` in `S` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:32:6 + --> $DIR/intra-link-errors.rs:29:6 | LL | /// [E::D] | ^^^^ | = note: this link partially resolves to the enum `E` - = note: `E` has no field, variant, or associated item named `D` + = note: no `D` in `E` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:37:6 + --> $DIR/intra-link-errors.rs:34:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -77,7 +57,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:41:6 + --> $DIR/intra-link-errors.rs:38:6 | LL | /// [S!] | ^^ help: to link to the struct, use its disambiguator: `struct@S` @@ -85,7 +65,7 @@ LL | /// [S!] = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:59:6 + --> $DIR/intra-link-errors.rs:56:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()` @@ -93,7 +73,7 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:64:6 + --> $DIR/intra-link-errors.rs:61:6 | LL | /// [T::h!] | ^^^^^ @@ -102,12 +82,12 @@ LL | /// [T::h!] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:51:6 + --> $DIR/intra-link-errors.rs:48:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` | = note: this link resolves to the associated function `h`, which is not in the type namespace -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 3737cad1cb187..b51c8e89600b0 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -6,7 +6,7 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... | = note: `#[warn(broken_intra_doc_links)]` on by default = note: this link partially resolves to the struct `Foo` - = note: `Foo` has no field, variant, or associated item named `baz` + = note: no `baz` in `Foo` warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 From 002d3a922c06c11a45e001d2737951a00e40687a Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 00:23:23 -0400 Subject: [PATCH 08/54] Don't give misleading errors for `f::A`, where f is in the value namespace --- .../passes/collect_intra_doc_links.rs | 62 ++++++++++++++----- src/test/rustdoc-ui/intra-link-errors.rs | 6 +- src/test/rustdoc-ui/intra-link-errors.stderr | 6 +- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ea3ec1b6685cc..ed411bf2b8dab 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -57,6 +57,9 @@ enum ResolutionFailure<'a> { /// This resolved, but with the wrong namespace. /// `Namespace` is the expected namespace (as opposed to the actual). WrongNamespace(Res, Namespace), + /// This has a partial resolution, but is not in the TypeNS and so cannot + /// have associated items or fields. + CannotHaveAssociatedItems(Res, Namespace), /// `String` is the base name of the path (not necessarily the whole link) NotInScope(Cow<'a, str>), /// this is a primitive type without an impls (no associated methods) @@ -90,7 +93,8 @@ impl ResolutionFailure<'a> { | NoPrimitiveImpl(res, _) | NotAnEnum(res) | NotAVariant(res, _) - | WrongNamespace(res, _) => Some(*res), + | WrongNamespace(res, _) + | CannotHaveAssociatedItems(res, _) => Some(*res), NotInScope(_) | NoParentItem | Dummy => None, } } @@ -360,21 +364,39 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { })); } - let (_, ty_res) = cx + let ty_res = cx .enter_resolver(|resolver| { // only types can have associated items resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) - .map_err(|_| { - ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone().into())) - })?; - if let Res::Err = ty_res { - return if ns == Namespace::ValueNS { - self.variant_field(path_str, current_item, module_id) - } else { - Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.into()))) - }; - } + .map(|(_, res)| res); + let ty_res = match ty_res { + Err(()) | Ok(Res::Err) => { + return if ns == Namespace::ValueNS { + self.variant_field(path_str, current_item, module_id) + } else { + // See if it only broke because of the namespace. + let kind = cx.enter_resolver(|resolver| { + for &ns in &[MacroNS, ValueNS] { + match resolver + .resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id) + { + Ok((_, Res::Err)) | Err(()) => {} + Ok((_, res)) => { + let res = res.map_id(|_| panic!("unexpected node_id")); + return ResolutionFailure::CannotHaveAssociatedItems( + res, ns, + ); + } + } + } + ResolutionFailure::NotInScope(path_root.into()) + }); + Err(ErrorKind::Resolve(kind)) + }; + } + Ok(res) => res, + }; let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); let res = match ty_res { Res::Def( @@ -1006,14 +1028,12 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Ok(res) => (res, extra_fragment), Err(mut kind) => { // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. - //if kind.res().is_none() { for &ns in &[TypeNS, ValueNS] { if let Some(res) = check_full_res(self, ns) { kind = ResolutionFailure::WrongNamespace(res, MacroNS); break; } } - //} resolution_failure( cx, &item, @@ -1456,6 +1476,20 @@ fn resolution_failure( diag.note(¬e); } } + ResolutionFailure::CannotHaveAssociatedItems(res, _) => { + let (item, _) = item(res); + diag.note(&format!("this link partially resolves to {}", item)); + if let Res::Def(kind, def_id) = res { + let name = cx.tcx.item_name(def_id); + let note = format!( + "`{}` is {} {}, not a module or type, and cannot have associated items", + name, + kind.article(), + kind.descr(def_id) + ); + diag.note(¬e); + } + } // TODO: is there ever a case where this happens? ResolutionFailure::NotAnEnum(res) => { let (item, comma) = item(res); diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 79ce57c22676f..1a2f24cef2423 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -7,14 +7,14 @@ // FIXME: this could say which path was the first to not be found (in this case, `path`) /// [path::to::nonexistent::module] //~^ ERROR unresolved link -//~| NOTE no item named `path::to::nonexistent` is in scope +//~| NOTE no item named `path::to` is in scope //~| HELP to escape // TODO: why does this say `f` and not `f::A`?? /// [f::A] //~^ ERROR unresolved link -//~| NOTE no item named `f` is in scope -//~| HELP to escape +//~| NOTE this link partially resolves +//~| NOTE `f` is a function, not a module /// [S::A] //~^ ERROR unresolved link diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 4044c7fde66f2..0244d2bfde8d9 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: no item named `path::to::nonexistent` is in scope + = note: no item named `path::to` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `f::A` @@ -18,8 +18,8 @@ error: unresolved link to `f::A` LL | /// [f::A] | ^^^^ | - = note: no item named `f` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: this link partially resolves to the function `f` + = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` --> $DIR/intra-link-errors.rs:19:6 From 7b8d0befd67ad2472052e3f84cf310b7866b829f Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 12:31:04 -0400 Subject: [PATCH 09/54] Remove some TODOs --- src/librustdoc/passes/collect_intra_doc_links.rs | 2 -- src/test/rustdoc-ui/intra-link-errors.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index ed411bf2b8dab..0d07f8f0328b4 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -150,7 +150,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let variant_name = // we're not sure this is a variant at all, so use the full string split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.into())))?; - // TODO: this looks very wrong, why are we requiring 3 fields? let path = split .next() .map(|f| { @@ -161,7 +160,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - // TODO: is this right? .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( variant_name.to_string().into(), )))?; diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 1a2f24cef2423..5bfbdcc495f29 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -10,7 +10,6 @@ //~| NOTE no item named `path::to` is in scope //~| HELP to escape -// TODO: why does this say `f` and not `f::A`?? /// [f::A] //~^ ERROR unresolved link //~| NOTE this link partially resolves From e2d69f2eb115b0b6a433977ae7c5a73c249a4f98 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 13:35:50 -0400 Subject: [PATCH 10/54] Make errors more concise and helpful Before: ``` = note: this link partially resolves to the struct `S` = note: no `fmt` in `S` ``` After: ``` = note: the struct `S` has no field or associated item named `fmt` ``` --- .../passes/collect_intra_doc_links.rs | 95 ++++++++++++------- .../rustdoc-ui/assoc-item-not-in-scope.stderr | 4 +- .../rustdoc-ui/intra-doc-alias-ice.stderr | 3 +- src/test/rustdoc-ui/intra-link-errors.rs | 10 +- src/test/rustdoc-ui/intra-link-errors.stderr | 28 +++--- .../rustdoc-ui/intra-links-warning.stderr | 3 +- src/test/rustdoc-ui/lint-group.stderr | 3 +- 7 files changed, 81 insertions(+), 65 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0d07f8f0328b4..c69850f9d9743 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1406,11 +1406,18 @@ fn resolution_failure( let in_scope = kinds.iter().any(|kind| kind.res().is_some()); let mut reported_not_in_scope = false; let item = |res: Res| { - if let Some(id) = res.opt_def_id() { - (format!("the {} `{}`", res.descr(), cx.tcx.item_name(id).to_string()), ",") - } else { - (format!("{} {}", res.article(), res.descr()), "") - } + format!("the {} `{}`", res.descr(), cx.tcx.item_name(res.def_id()).to_string()) + }; + let assoc_item_not_allowed = |res: Res, diag: &mut DiagnosticBuilder<'_>| { + let def_id = res.def_id(); + let name = cx.tcx.item_name(def_id); + let note = format!( + "`{}` is {} {}, not a module or type, and cannot have associated items", + name, + res.article(), + res.descr() + ); + diag.note(¬e); }; for failure in kinds { match failure { @@ -1425,11 +1432,9 @@ fn resolution_failure( } ResolutionFailure::Dummy => continue, ResolutionFailure::WrongNamespace(res, expected_ns) => { - let (item, comma) = item(res); let note = format!( - "this link resolves to {}{} which is not in the {} namespace", - item, - comma, + "this link resolves to {}, which is not in the {} namespace", + item(res), expected_ns.descr() ); diag.note(¬e); @@ -1450,10 +1455,9 @@ fn resolution_failure( panic!("all intra doc links should have a parent item") } ResolutionFailure::NoPrimitiveImpl(res, _) => { - let (item, comma) = item(res); let note = format!( - "this link partially resolves to {}{} which does not have any associated items", - item, comma, + "this link partially resolves to {}, which does not have any associated items", + item(res), ); diag.note(¬e); } @@ -1465,41 +1469,62 @@ fn resolution_failure( diag.note(¬e); } ResolutionFailure::NoAssocItem(res, assoc_item) => { - let (item, _) = item(res); - diag.note(&format!("this link partially resolves to {}", item)); - // FIXME: when are items neither a primitive nor a Def? - if let Res::Def(_, def_id) = res { - let name = cx.tcx.item_name(def_id); - let note = format!("no `{}` in `{}`", assoc_item, name,); - diag.note(¬e); - } + use DefKind::*; + + let (kind, def_id) = match res { + Res::Def(kind, def_id) => (kind, def_id), + _ => unreachable!( + "primitives are covered above and other `Res` variants aren't possible at module scope" + ), + }; + let name = cx.tcx.item_name(def_id); + let path_description = match kind { + Mod | ForeignMod => "inner item", + Struct => "field or associated item", + Enum | Union => "variant or associated item", + Variant + | Field + | Closure + | Generator + | AssocTy + | AssocConst + | AssocFn + | Fn + | Macro(_) + | Const + | ConstParam + | ExternCrate + | Use + | LifetimeParam + | Ctor(_, _) + | AnonConst => return assoc_item_not_allowed(res, diag), + Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam + | Static => "associated item", + Impl | GlobalAsm => unreachable!("not a path"), + }; + let note = format!( + "the {} `{}` has no {} named `{}`", + res.descr(), + name, + path_description, + assoc_item + ); + diag.note(¬e); } ResolutionFailure::CannotHaveAssociatedItems(res, _) => { - let (item, _) = item(res); - diag.note(&format!("this link partially resolves to {}", item)); - if let Res::Def(kind, def_id) = res { - let name = cx.tcx.item_name(def_id); - let note = format!( - "`{}` is {} {}, not a module or type, and cannot have associated items", - name, - kind.article(), - kind.descr(def_id) - ); - diag.note(¬e); - } + assoc_item_not_allowed(res, diag) } // TODO: is there ever a case where this happens? ResolutionFailure::NotAnEnum(res) => { - let (item, comma) = item(res); let note = - format!("this link resolves to {}{} which is not an enum", item, comma); + format!("this link resolves to {}, which is not an enum", item(res)); diag.note(¬e); diag.note("if this were an enum, it might have a variant which resolved"); } ResolutionFailure::NotAVariant(res, variant) => { let note = format!( "this link partially resolves to {}, but there is no variant named {}", - item(res).0, + item(res), variant ); diag.note(¬e); diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr index 8827c9351a62d..8acfcca2139e3 100644 --- a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr +++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr @@ -2,14 +2,14 @@ error: unresolved link to `S::fmt` --> $DIR/assoc-item-not-in-scope.rs:4:14 | LL | /// Link to [`S::fmt`] - | ^^^^^^^^ unresolved link + | ^^^^^^^^ | note: the lint level is defined here --> $DIR/assoc-item-not-in-scope.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + = note: the struct `S` has no field or associated item named `fmt` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index 58abe4406a85d..5379a91883753 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -9,8 +9,7 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: this link partially resolves to the type alias `TypeAlias` - = note: no `hoge` in `TypeAlias` + = note: the type alias `TypeAlias` has no associated item named `hoge` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 5bfbdcc495f29..ecf2b05e2818b 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -12,23 +12,19 @@ /// [f::A] //~^ ERROR unresolved link -//~| NOTE this link partially resolves //~| NOTE `f` is a function, not a module /// [S::A] //~^ ERROR unresolved link -//~| NOTE this link partially resolves -//~| NOTE no `A` in `S` +//~| NOTE struct `S` has no field or associated item /// [S::fmt] //~^ ERROR unresolved link -//~| NOTE this link partially resolves -//~| NOTE no `fmt` in `S` +//~| NOTE struct `S` has no field or associated item /// [E::D] //~^ ERROR unresolved link -//~| NOTE this link partially resolves -//~| NOTE no `D` in `E` +//~| NOTE enum `E` has no variant or associated item /// [u8::not_found] //~^ ERROR unresolved link diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 0244d2bfde8d9..ba091ff854ce6 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -13,43 +13,39 @@ LL | #![deny(broken_intra_doc_links)] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:14:6 + --> $DIR/intra-link-errors.rs:13:6 | LL | /// [f::A] | ^^^^ | - = note: this link partially resolves to the function `f` = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:19:6 + --> $DIR/intra-link-errors.rs:17:6 | LL | /// [S::A] | ^^^^ | - = note: this link partially resolves to the struct `S` - = note: no `A` in `S` + = note: the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:24:6 + --> $DIR/intra-link-errors.rs:21:6 | LL | /// [S::fmt] | ^^^^^^ | - = note: this link partially resolves to the struct `S` - = note: no `fmt` in `S` + = note: the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:29:6 + --> $DIR/intra-link-errors.rs:25:6 | LL | /// [E::D] | ^^^^ | - = note: this link partially resolves to the enum `E` - = note: no `D` in `E` + = note: the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:34:6 + --> $DIR/intra-link-errors.rs:29:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -57,7 +53,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:38:6 + --> $DIR/intra-link-errors.rs:33:6 | LL | /// [S!] | ^^ help: to link to the struct, use its disambiguator: `struct@S` @@ -65,7 +61,7 @@ LL | /// [S!] = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:56:6 + --> $DIR/intra-link-errors.rs:51:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()` @@ -73,7 +69,7 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:61:6 + --> $DIR/intra-link-errors.rs:56:6 | LL | /// [T::h!] | ^^^^^ @@ -82,7 +78,7 @@ LL | /// [T::h!] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:48:6 + --> $DIR/intra-link-errors.rs:43:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index b51c8e89600b0..988a65c7a591c 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -5,8 +5,7 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... | ^^^^^^^^ | = note: `#[warn(broken_intra_doc_links)]` on by default - = note: this link partially resolves to the struct `Foo` - = note: no `baz` in `Foo` + = note: the struct `Foo` has no field or associated item named `baz` warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index 04296d2e44a7c..10fcebb27fe0c 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -32,7 +32,7 @@ error: unresolved link to `error` --> $DIR/lint-group.rs:9:29 | LL | /// what up, let's make an [error] - | ^^^^^ unresolved link + | ^^^^^ | note: the lint level is defined here --> $DIR/lint-group.rs:7:9 @@ -40,6 +40,7 @@ note: the lint level is defined here LL | #![deny(rustdoc)] | ^^^^^^^ = note: `#[deny(broken_intra_doc_links)]` implied by `#[deny(rustdoc)]` + = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to 3 previous errors From 19d100278d9609c389780e2692dddaeb45fba301 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 15:00:30 -0400 Subject: [PATCH 11/54] Fix failures to resolve primitives Previously, when looking for the associated items for primitives, rustdoc would look for primitives in the current namespace. But all primitives are in the type namespace. To fix this, rustdoc now always looks for primitives in the namespace when considering them as a stepping stone to the associated item. However, fixing that bug caused several duplicate errors because rustdoc now reports the same error in each namespace. To avoid this, rustdoc now ignores all duplicate errors when issuing them. --- .../passes/collect_intra_doc_links.rs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index c69850f9d9743..9a88c8eb42ad6 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -331,7 +331,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string().into())) })?; - if let Some((path, prim)) = is_primitive(&path_root, ns) { + if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { let impls = primitive_impl(cx, &path).ok_or_else(|| { ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root.into())) })?; @@ -355,6 +355,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return Ok(link); } } + debug!( + "returning primitive error for {}::{} in {} namespace", + path, + item_name, + ns.descr() + ); return Err(ErrorKind::Resolve(ResolutionFailure::NoPrimitiveAssocItem { res: prim, prim_name: path, @@ -1404,7 +1410,6 @@ fn resolution_failure( &link_range, |diag, sp| { let in_scope = kinds.iter().any(|kind| kind.res().is_some()); - let mut reported_not_in_scope = false; let item = |res: Res| { format!("the {} `{}`", res.descr(), cx.tcx.item_name(res.def_id()).to_string()) }; @@ -1419,14 +1424,19 @@ fn resolution_failure( ); diag.note(¬e); }; + // ignore duplicates + let mut variants_seen = SmallVec::<[_; 3]>::new(); for failure in kinds { + let variant = std::mem::discriminant(&failure); + if variants_seen.contains(&variant) { + continue; + } + variants_seen.push(variant); match failure { - // already handled above ResolutionFailure::NotInScope(base) => { - if in_scope || reported_not_in_scope { + if in_scope { continue; } - reported_not_in_scope = true; diag.note(&format!("no item named `{}` is in scope", base)); diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); } From ebc8cb470fd137214911fa317ff6b06c6683ad3c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 15:19:47 -0400 Subject: [PATCH 12/54] Turn NoParentItem from a panic into an ICE --- src/librustdoc/passes/collect_intra_doc_links.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 9a88c8eb42ad6..a859d1b2f5f59 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -541,7 +541,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } else { debug!("attempting to resolve item without parent module: {}", path_str); - // TODO: maybe this should just be an ICE? Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)) } } @@ -1462,7 +1461,8 @@ fn resolution_failure( } } ResolutionFailure::NoParentItem => { - panic!("all intra doc links should have a parent item") + diag.level = rustc_errors::Level::Bug; + diag.note("all intra doc links should have a parent item"); } ResolutionFailure::NoPrimitiveImpl(res, _) => { let note = format!( @@ -1694,7 +1694,6 @@ fn handle_variant( let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent } else { - // TODO: this should just be an unwrap, there should never be `Variant`s without a parent return Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)); }; let parent_def = Res::Def(DefKind::Enum, parent); From 6875220e1abea26c67885833c27365854cd7f73c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 16:12:39 -0400 Subject: [PATCH 13/54] Use rustc_resolve's descr() instead of rewriting it --- .../passes/collect_intra_doc_links.rs | 86 ++++++++----------- .../rustdoc-ui/intra-links-anchors.stderr | 6 +- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a859d1b2f5f59..1c709cead7017 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -110,12 +110,7 @@ impl ResolutionFailure<'a> { enum AnchorFailure { MultipleAnchors, - Primitive, - Variant, - AssocConstant, - AssocType, - Field, - Method, + RustdocAnchorConflict(Res), } struct LinkCollector<'a, 'tcx> { @@ -288,7 +283,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Not a trait item; just return what we found. Res::PrimTy(..) => { if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive)); + return Err(ErrorKind::AnchorFailure( + AnchorFailure::RustdocAnchorConflict(res), + )); } return Ok((res, Some(path_str.to_owned()))); } @@ -305,7 +302,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } else if let Some((path, prim)) = is_primitive(path_str, ns) { if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive)); + return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + prim, + ))); } return Ok((prim, Some(path.to_owned()))); } @@ -444,11 +443,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { ty::AssocKind::Type => "associatedtype", }; Some(if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if kind == ty::AssocKind::Fn { - AnchorFailure::Method - } else { - AnchorFailure::AssocConstant - })) + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + ty_res, + ))) } else { // HACK(jynelson): `clean` expects the type, not the associated item. // but the disambiguator logic expects the associated item. @@ -470,11 +467,17 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; field.map(|item| { if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if def.is_enum() { - AnchorFailure::Variant - } else { - AnchorFailure::Field - })) + let res = Res::Def( + if def.is_enum() { + DefKind::Variant + } else { + DefKind::Field + }, + item.did, + ); + Err(ErrorKind::AnchorFailure( + AnchorFailure::RustdocAnchorConflict(res), + )) } else { Ok(( ty_res, @@ -518,13 +521,9 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; if extra_fragment.is_some() { - Err(ErrorKind::AnchorFailure(if item.kind == ty::AssocKind::Const { - AnchorFailure::AssocConstant - } else if item.kind == ty::AssocKind::Type { - AnchorFailure::AssocType - } else { - AnchorFailure::Method - })) + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict( + ty_res, + ))) } else { let res = Res::Def(item.kind.as_def_kind(), item.def_id); Ok((res, Some(format!("{}.{}", kind, item_name)))) @@ -889,8 +888,10 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Some(res.0) } Err(ErrorKind::Resolve(kind)) => kind.full_res(), - // TODO: add `Res` to AnchorFailure - Err(ErrorKind::AnchorFailure(_)) => None, + Err(ErrorKind::AnchorFailure( + AnchorFailure::RustdocAnchorConflict(res), + )) => Some(res), + Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, }; this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) }; @@ -1070,7 +1071,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { path_str, &dox, link_range, - AnchorFailure::Primitive, + AnchorFailure::RustdocAnchorConflict(prim), ); continue; } @@ -1555,28 +1556,11 @@ fn anchor_failure( ) { let msg = match failure { AnchorFailure::MultipleAnchors => format!("`{}` contains multiple anchors", path_str), - AnchorFailure::Primitive - | AnchorFailure::Variant - | AnchorFailure::AssocConstant - | AnchorFailure::AssocType - | AnchorFailure::Field - | AnchorFailure::Method => { - let kind = match failure { - AnchorFailure::Primitive => "primitive type", - AnchorFailure::Variant => "enum variant", - AnchorFailure::AssocConstant => "associated constant", - AnchorFailure::AssocType => "associated type", - AnchorFailure::Field => "struct field", - AnchorFailure::Method => "method", - AnchorFailure::MultipleAnchors => unreachable!("should be handled already"), - }; - - format!( - "`{}` contains an anchor, but links to {kind}s are already anchored", - path_str, - kind = kind - ) - } + AnchorFailure::RustdocAnchorConflict(res) => format!( + "`{}` contains an anchor, but links to {kind}s are already anchored", + path_str, + kind = res.descr(), + ), }; report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { @@ -1689,7 +1673,7 @@ fn handle_variant( use rustc_middle::ty::DefIdTree; if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure(AnchorFailure::Variant)); + return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))); } let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent diff --git a/src/test/rustdoc-ui/intra-links-anchors.stderr b/src/test/rustdoc-ui/intra-links-anchors.stderr index e737b84320d94..1825a4ad1fa6b 100644 --- a/src/test/rustdoc-ui/intra-links-anchors.stderr +++ b/src/test/rustdoc-ui/intra-links-anchors.stderr @@ -1,4 +1,4 @@ -error: `Foo::f#hola` contains an anchor, but links to struct fields are already anchored +error: `Foo::f#hola` contains an anchor, but links to fields are already anchored --> $DIR/intra-links-anchors.rs:25:15 | LL | /// Or maybe [Foo::f#hola]. @@ -16,13 +16,13 @@ error: `hello#people#!` contains multiple anchors LL | /// Another anchor error: [hello#people#!]. | ^^^^^^^^^^^^^^ contains invalid anchor -error: `Enum::A#whatever` contains an anchor, but links to enum variants are already anchored +error: `Enum::A#whatever` contains an anchor, but links to variants are already anchored --> $DIR/intra-links-anchors.rs:37:28 | LL | /// Damn enum's variants: [Enum::A#whatever]. | ^^^^^^^^^^^^^^^^ contains invalid anchor -error: `u32#hello` contains an anchor, but links to primitive types are already anchored +error: `u32#hello` contains an anchor, but links to builtin types are already anchored --> $DIR/intra-links-anchors.rs:43:6 | LL | /// [u32#hello] From 418f6089e93d7f784643d55e6782055c9bb479d5 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 21 Aug 2020 16:37:31 -0400 Subject: [PATCH 14/54] Give a better error message when linking to a macro with the wrong disambiguator Before: ``` warning: unresolved link to `m` --> m.rs:1:6 | 1 | /// [value@m] | ^^^^^^^ | = note: `#[warn(broken_intra_doc_links)]` on by default = note: no item named `m` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` ``` After: ``` warning: unresolved link to `m` --> m.rs:1:6 | 1 | /// [value@m] | ^^^^^^^ help: to link to the macro, use its disambiguator: `m!` | = note: `#[warn(broken_intra_doc_links)]` on by default = note: this link resolves to the macro `m`, which is not in the value namespace ``` --- .../passes/collect_intra_doc_links.rs | 119 ++++++++++++------ src/test/rustdoc-ui/intra-link-errors.rs | 21 ++++ src/test/rustdoc-ui/intra-link-errors.stderr | 52 ++++++-- 3 files changed, 142 insertions(+), 50 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 1c709cead7017..7bd4b8ca854b5 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -74,8 +74,6 @@ enum ResolutionFailure<'a> { NoAssocItem(Res, Symbol), /// should not ever happen NoParentItem, - /// the root of this path resolved, but it was not an enum. - NotAnEnum(Res), /// this could be an enum variant, but the last path fragment wasn't resolved. /// the `String` is the variant that didn't exist NotAVariant(Res, Symbol), @@ -91,7 +89,6 @@ impl ResolutionFailure<'a> { NoPrimitiveAssocItem { res, .. } | NoAssocItem(res, _) | NoPrimitiveImpl(res, _) - | NotAnEnum(res) | NotAVariant(res, _) | WrongNamespace(res, _) | CannotHaveAssociatedItems(res, _) => Some(*res), @@ -133,6 +130,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { path_str: &'path str, current_item: &Option, module_id: DefId, + extra_fragment: &Option, ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; @@ -202,7 +200,28 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { _ => unreachable!(), } } - _ => Err(ErrorKind::Resolve(ResolutionFailure::NotAnEnum(ty_res))), + // `variant_field` looks at 3 different path segments in a row. + // But `NoAssocItem` assumes there are only 2. Check to see if there's + // an intermediate segment that resolves. + _ => { + let intermediate_path = format!("{}::{}", path, variant_name); + // NOTE: we have to be careful here, because we're already in `resolve`. + // We know this doesn't recurse forever because we use a shorter path each time. + // NOTE: this uses `TypeNS` because nothing else has a valid path segment after + let kind = if let Some(intermediate) = self.check_full_res( + TypeNS, + &intermediate_path, + Some(module_id), + current_item, + extra_fragment, + ) { + ResolutionFailure::NoAssocItem(intermediate, variant_field_name) + } else { + // Even with the shorter path, it didn't resolve, so say that. + ResolutionFailure::NoAssocItem(ty_res, variant_name) + }; + Err(ErrorKind::Resolve(kind)) + } } } @@ -376,7 +395,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let ty_res = match ty_res { Err(()) | Ok(Res::Err) => { return if ns == Namespace::ValueNS { - self.variant_field(path_str, current_item, module_id) + self.variant_field(path_str, current_item, module_id, extra_fragment) } else { // See if it only broke because of the namespace. let kind = cx.enter_resolver(|resolver| { @@ -533,7 +552,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; res.unwrap_or_else(|| { if ns == Namespace::ValueNS { - self.variant_field(path_str, current_item, module_id) + self.variant_field(path_str, current_item, module_id, extra_fragment) } else { Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem(ty_res, item_name))) } @@ -543,6 +562,41 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)) } } + + // used for reporting better errors + fn check_full_res( + &self, + ns: Namespace, + path_str: &str, + base_node: Option, + current_item: &Option, + extra_fragment: &Option, + ) -> Option { + let check_full_res_inner = |this: &Self, result: Result>| { + let res = match result { + Ok(res) => Some(res), + Err(ErrorKind::Resolve(kind)) => kind.full_res(), + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => { + Some(res) + } + Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, + }; + this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) + }; + // cannot be used for macro namespace + let check_full_res = |this: &Self, ns| { + let result = this.resolve(path_str, ns, current_item, base_node, extra_fragment); + check_full_res_inner(this, result.map(|(res, _)| res)) + }; + let check_full_res_macro = |this: &Self| { + let result = this.macro_resolve(path_str, base_node); + check_full_res_inner(this, result.map_err(ErrorKind::Resolve)) + }; + match ns { + Namespace::MacroNS => check_full_res_macro(self), + Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns), + } + } } fn resolve_associated_trait_item( @@ -652,7 +706,7 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); // Check if these are the same type. let impl_type = trait_ref.self_ty(); - debug!( + trace!( "comparing type {} with kind {:?} against type {:?}", impl_type, impl_type.kind(), @@ -875,27 +929,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } - // used for reporting better errors - let check_full_res = |this: &mut Self, ns| { - let res = - match this.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) - { - Ok(res) => { - debug!( - "check_full_res: saw res for {} in {:?} ns: {:?}", - path_str, ns, res.0 - ); - Some(res.0) - } - Err(ErrorKind::Resolve(kind)) => kind.full_res(), - Err(ErrorKind::AnchorFailure( - AnchorFailure::RustdocAnchorConflict(res), - )) => Some(res), - Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, - }; - this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) - }; - match disambiguator.map(Disambiguator::ns) { Some(ns @ (ValueNS | TypeNS)) => { match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) @@ -903,12 +936,19 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Ok(res) => res, Err(ErrorKind::Resolve(mut kind)) => { // We only looked in one namespace. Try to give a better error if possible. - // TODO: handle MacroNS too if kind.full_res().is_none() { let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; - if let Some(res) = check_full_res(self, other_ns) { - // recall that this stores the _expected_ namespace - kind = ResolutionFailure::WrongNamespace(res, ns); + for &ns in &[other_ns, MacroNS] { + if let Some(res) = self.check_full_res( + ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { + kind = ResolutionFailure::WrongNamespace(res, ns); + break; + } } } resolution_failure( @@ -1033,7 +1073,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { Err(mut kind) => { // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. for &ns in &[TypeNS, ValueNS] { - if let Some(res) = check_full_res(self, ns) { + if let Some(res) = self.check_full_res( + ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { kind = ResolutionFailure::WrongNamespace(res, MacroNS); break; } @@ -1525,13 +1571,6 @@ fn resolution_failure( ResolutionFailure::CannotHaveAssociatedItems(res, _) => { assoc_item_not_allowed(res, diag) } - // TODO: is there ever a case where this happens? - ResolutionFailure::NotAnEnum(res) => { - let note = - format!("this link resolves to {}, which is not an enum", item(res)); - diag.note(¬e); - diag.note("if this were an enum, it might have a variant which resolved"); - } ResolutionFailure::NotAVariant(res, variant) => { let note = format!( "this link partially resolves to {}, but there is no variant named {}", diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index ecf2b05e2818b..33c8d1b3c4965 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -10,6 +10,18 @@ //~| NOTE no item named `path::to` is in scope //~| HELP to escape +/// [std::io::not::here] +//~^ ERROR unresolved link +//~| NOTE the module `io` has no inner item + +/// [std::io::Error::x] +//~^ ERROR unresolved link +//~| NOTE the struct `Error` has no field + +/// [std::io::ErrorKind::x] +//~^ ERROR unresolved link +//~| NOTE the enum `ErrorKind` has no variant + /// [f::A] //~^ ERROR unresolved link //~| NOTE `f` is a function, not a module @@ -60,3 +72,12 @@ impl S { pub trait T { fn g() {} } + +/// [m()] +//~^ ERROR unresolved link +//~| HELP to link to the macro +//~| NOTE not in the value namespace +#[macro_export] +macro_rules! m { + () => {}; +} diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index ba091ff854ce6..0b9149cd8ea45 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -12,16 +12,40 @@ LL | #![deny(broken_intra_doc_links)] = note: no item named `path::to` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -error: unresolved link to `f::A` +error: unresolved link to `std::io::not::here` --> $DIR/intra-link-errors.rs:13:6 | +LL | /// [std::io::not::here] + | ^^^^^^^^^^^^^^^^^^ + | + = note: the module `io` has no inner item named `not` + +error: unresolved link to `std::io::Error::x` + --> $DIR/intra-link-errors.rs:17:6 + | +LL | /// [std::io::Error::x] + | ^^^^^^^^^^^^^^^^^ + | + = note: the struct `Error` has no field or associated item named `x` + +error: unresolved link to `std::io::ErrorKind::x` + --> $DIR/intra-link-errors.rs:21:6 + | +LL | /// [std::io::ErrorKind::x] + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: the enum `ErrorKind` has no variant or associated item named `x` + +error: unresolved link to `f::A` + --> $DIR/intra-link-errors.rs:25:6 + | LL | /// [f::A] | ^^^^ | = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:17:6 + --> $DIR/intra-link-errors.rs:29:6 | LL | /// [S::A] | ^^^^ @@ -29,7 +53,7 @@ LL | /// [S::A] = note: the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:21:6 + --> $DIR/intra-link-errors.rs:33:6 | LL | /// [S::fmt] | ^^^^^^ @@ -37,7 +61,7 @@ LL | /// [S::fmt] = note: the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:25:6 + --> $DIR/intra-link-errors.rs:37:6 | LL | /// [E::D] | ^^^^ @@ -45,7 +69,7 @@ LL | /// [E::D] = note: the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:29:6 + --> $DIR/intra-link-errors.rs:41:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -53,7 +77,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:33:6 + --> $DIR/intra-link-errors.rs:45:6 | LL | /// [S!] | ^^ help: to link to the struct, use its disambiguator: `struct@S` @@ -61,7 +85,7 @@ LL | /// [S!] = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:51:6 + --> $DIR/intra-link-errors.rs:63:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()` @@ -69,7 +93,7 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:56:6 + --> $DIR/intra-link-errors.rs:68:6 | LL | /// [T::h!] | ^^^^^ @@ -78,12 +102,20 @@ LL | /// [T::h!] = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:43:6 + --> $DIR/intra-link-errors.rs:55:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` | = note: this link resolves to the associated function `h`, which is not in the type namespace -error: aborting due to 10 previous errors +error: unresolved link to `m` + --> $DIR/intra-link-errors.rs:76:6 + | +LL | /// [m()] + | ^^^ help: to link to the macro, use its disambiguator: `m!` + | + = note: this link resolves to the macro `m`, which is not in the value namespace + +error: aborting due to 14 previous errors From 2ca6f11663709bcbc113d3dd223ae51442460e15 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 22 Aug 2020 00:50:26 -0400 Subject: [PATCH 15/54] Fix rebase conflicts --- src/librustdoc/passes/collect_intra_doc_links.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 7bd4b8ca854b5..43003c944918e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -938,9 +938,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { // We only looked in one namespace. Try to give a better error if possible. if kind.full_res().is_none() { let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; - for &ns in &[other_ns, MacroNS] { + for &new_ns in &[other_ns, MacroNS] { if let Some(res) = self.check_full_res( - ns, + new_ns, path_str, base_node, ¤t_item, From bb9d1576044020851ff2f528879a41e1ca0b7b5d Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 25 Aug 2020 17:39:50 -0400 Subject: [PATCH 16/54] Address my own review comments - Remove unneeded lifetime parameter - Comment why some code doesn't use `check_full_res` --- src/librustdoc/passes/collect_intra_doc_links.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 43003c944918e..b35ec2e73cb27 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -399,6 +399,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } else { // See if it only broke because of the namespace. let kind = cx.enter_resolver(|resolver| { + // NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it) for &ns in &[MacroNS, ValueNS] { match resolver .resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id) @@ -563,9 +564,15 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } - // used for reporting better errors + /// Used for reporting better errors. + /// + /// Returns whether the link resolved 'fully' in another namespace. + /// 'fully' here means that all parts of the link resolved, not just some path segments. + /// This returns the `Res` even if it was erroneous for some reason + /// (such as having invalid URL fragments or being in the wrong namespace). fn check_full_res( &self, + // TODO: is this parameter actually needed, since we return results for the wrong namespace? ns: Namespace, path_str: &str, base_node: Option, @@ -1609,10 +1616,10 @@ fn anchor_failure( }); } -fn ambiguity_error<'a>( +fn ambiguity_error( cx: &DocContext<'_>, item: &Item, - path_str: &'a str, + path_str: &str, dox: &str, link_range: Option>, candidates: Vec, From f2826d9e9bcba3c161812fca893f98b3dc010f19 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Tue, 25 Aug 2020 18:45:57 -0400 Subject: [PATCH 17/54] Show the first path segment which failed to resolve. Before, it would arbitrarily pick the third-to-last if the last three or more did not resolve. --- .../passes/collect_intra_doc_links.rs | 30 +++++++++++++++---- src/test/rustdoc-ui/intra-link-errors.rs | 5 ++-- src/test/rustdoc-ui/intra-link-errors.stderr | 14 +++++---- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index b35ec2e73cb27..098ac13ffe238 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -156,15 +156,35 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( variant_name.to_string().into(), )))?; - let (_, ty_res) = cx + let ty_res = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) - .map_err(|_| { - ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string().into())) - })?; + .map(|(_, res)| res) + .unwrap_or(Res::Err); + // This code only gets hit if three path segments in a row don't get resolved. + // It's a good time to check if _any_ parent of the path gets resolved. + // If so, report it and say the first which failed; if not, say the first path segment didn't resolve. if let Res::Err = ty_res { - return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path.to_string().into()))); + let mut current = path.as_str(); + while let Some(parent) = current.rsplitn(2, "::").nth(1) { + current = parent; + if let Some(res) = self.check_full_res( + TypeNS, + ¤t, + Some(module_id), + current_item, + extra_fragment, + ) { + return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( + res, + Symbol::intern(&path), + ))); + } + } + return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope( + current.to_string().into(), + ))); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 33c8d1b3c4965..d8d4ebeb60bdb 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -4,10 +4,9 @@ // FIXME: this should say that it was skipped (maybe an allowed by default lint?) /// [] -// FIXME: this could say which path was the first to not be found (in this case, `path`) /// [path::to::nonexistent::module] //~^ ERROR unresolved link -//~| NOTE no item named `path::to` is in scope +//~| NOTE no item named `path` is in scope //~| HELP to escape /// [std::io::not::here] @@ -44,7 +43,7 @@ /// [S!] //~^ ERROR unresolved link -//~| HELP to link to the struct, use its disambiguator +//~| HELP to link to the struct, prefix with the item kind //~| NOTE this link resolves to the struct `S` pub fn f() {} #[derive(Debug)] diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 0b9149cd8ea45..0275fd692feb4 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: no item named `path::to` is in scope + = note: no item named `path` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `std::io::not::here` @@ -80,15 +80,19 @@ error: unresolved link to `S` --> $DIR/intra-link-errors.rs:45:6 | LL | /// [S!] - | ^^ help: to link to the struct, use its disambiguator: `struct@S` + | ^^ | = note: this link resolves to the struct `S`, which is not in the macro namespace +help: to link to the struct, prefix with the item kind + | +LL | /// [struct@S] + | ^^^^^^^^ error: unresolved link to `T::g` --> $DIR/intra-link-errors.rs:63:6 | LL | /// [type@T::g] - | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `T::g()` + | ^^^^^^^^^ help: to link to the associated function, add parentheses: `T::g()` | = note: this link resolves to the associated function `g`, which is not in the type namespace @@ -105,7 +109,7 @@ error: unresolved link to `S::h` --> $DIR/intra-link-errors.rs:55:6 | LL | /// [type@S::h] - | ^^^^^^^^^ help: to link to the associated function, use its disambiguator: `S::h()` + | ^^^^^^^^^ help: to link to the associated function, add parentheses: `S::h()` | = note: this link resolves to the associated function `h`, which is not in the type namespace @@ -113,7 +117,7 @@ error: unresolved link to `m` --> $DIR/intra-link-errors.rs:76:6 | LL | /// [m()] - | ^^^ help: to link to the macro, use its disambiguator: `m!` + | ^^^ help: to link to the macro, add an exclamation mark: `m!` | = note: this link resolves to the macro `m`, which is not in the value namespace From d67eb1f1487c04cdd2564741828cba0784ed8501 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 28 Aug 2020 00:23:24 -0400 Subject: [PATCH 18/54] Don't suggest \[ \] if there's a :: in the path --- .../passes/collect_intra_doc_links.rs | 9 ++++-- src/test/rustdoc-ui/intra-link-errors.rs | 2 -- src/test/rustdoc-ui/intra-link-errors.stderr | 28 +++++++++---------- .../rustdoc-ui/intra-links-warning.stderr | 5 ---- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 098ac13ffe238..0ae322ee11ae3 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -592,7 +592,6 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { /// (such as having invalid URL fragments or being in the wrong namespace). fn check_full_res( &self, - // TODO: is this parameter actually needed, since we return results for the wrong namespace? ns: Namespace, path_str: &str, base_node: Option, @@ -1511,7 +1510,13 @@ fn resolution_failure( continue; } diag.note(&format!("no item named `{}` is in scope", base)); - diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); + // If the link has `::` in the path, assume it's meant to be an intra-doc link + if !path_str.contains("::") { + // Otherwise, the `[]` might be unrelated. + // FIXME(https://github.com/raphlinus/pulldown-cmark/issues/373): + // don't show this for autolinks (`<>`), `()` style links, or reference links + diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); + } } ResolutionFailure::Dummy => continue, ResolutionFailure::WrongNamespace(res, expected_ns) => { diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index d8d4ebeb60bdb..f66bb8a453305 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -7,7 +7,6 @@ /// [path::to::nonexistent::module] //~^ ERROR unresolved link //~| NOTE no item named `path` is in scope -//~| HELP to escape /// [std::io::not::here] //~^ ERROR unresolved link @@ -67,7 +66,6 @@ impl S { /// [T::h!] //~^ ERROR unresolved link //~| NOTE no item named `T::h` -//~| HELP to escape pub trait T { fn g() {} } diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 0275fd692feb4..4dac892b82051 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -10,10 +10,9 @@ note: the lint level is defined here LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: no item named `path` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `std::io::not::here` - --> $DIR/intra-link-errors.rs:13:6 + --> $DIR/intra-link-errors.rs:12:6 | LL | /// [std::io::not::here] | ^^^^^^^^^^^^^^^^^^ @@ -21,7 +20,7 @@ LL | /// [std::io::not::here] = note: the module `io` has no inner item named `not` error: unresolved link to `std::io::Error::x` - --> $DIR/intra-link-errors.rs:17:6 + --> $DIR/intra-link-errors.rs:16:6 | LL | /// [std::io::Error::x] | ^^^^^^^^^^^^^^^^^ @@ -29,7 +28,7 @@ LL | /// [std::io::Error::x] = note: the struct `Error` has no field or associated item named `x` error: unresolved link to `std::io::ErrorKind::x` - --> $DIR/intra-link-errors.rs:21:6 + --> $DIR/intra-link-errors.rs:20:6 | LL | /// [std::io::ErrorKind::x] | ^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +36,7 @@ LL | /// [std::io::ErrorKind::x] = note: the enum `ErrorKind` has no variant or associated item named `x` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:25:6 + --> $DIR/intra-link-errors.rs:24:6 | LL | /// [f::A] | ^^^^ @@ -45,7 +44,7 @@ LL | /// [f::A] = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:29:6 + --> $DIR/intra-link-errors.rs:28:6 | LL | /// [S::A] | ^^^^ @@ -53,7 +52,7 @@ LL | /// [S::A] = note: the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:33:6 + --> $DIR/intra-link-errors.rs:32:6 | LL | /// [S::fmt] | ^^^^^^ @@ -61,7 +60,7 @@ LL | /// [S::fmt] = note: the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:37:6 + --> $DIR/intra-link-errors.rs:36:6 | LL | /// [E::D] | ^^^^ @@ -69,7 +68,7 @@ LL | /// [E::D] = note: the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:41:6 + --> $DIR/intra-link-errors.rs:40:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -77,7 +76,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:45:6 + --> $DIR/intra-link-errors.rs:44:6 | LL | /// [S!] | ^^ @@ -89,7 +88,7 @@ LL | /// [struct@S] | ^^^^^^^^ error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:63:6 + --> $DIR/intra-link-errors.rs:62:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `T::g()` @@ -97,16 +96,15 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:68:6 + --> $DIR/intra-link-errors.rs:67:6 | LL | /// [T::h!] | ^^^^^ | = note: no item named `T::h` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:55:6 + --> $DIR/intra-link-errors.rs:54:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `S::h()` @@ -114,7 +112,7 @@ LL | /// [type@S::h] = note: this link resolves to the associated function `h`, which is not in the type namespace error: unresolved link to `m` - --> $DIR/intra-link-errors.rs:76:6 + --> $DIR/intra-link-errors.rs:74:6 | LL | /// [m()] | ^^^ help: to link to the macro, add an exclamation mark: `m!` diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 988a65c7a591c..25c6975d3a509 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -14,7 +14,6 @@ LL | //! Test with [Foo::baz], [Bar::foo], ... | ^^^^^^^^ | = note: no item named `Bar` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 @@ -23,7 +22,6 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^^^^^ | = note: no item named `Uniooon` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 @@ -32,7 +30,6 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^ | = note: no item named `Qux` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 @@ -41,7 +38,6 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^^^^^ | = note: no item named `Uniooon` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 @@ -50,7 +46,6 @@ LL | //! , [Uniooon::X] and [Qux::Z]. | ^^^^^^ | = note: no item named `Qux` is in scope - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 From ee683ef8532034a4bee01e9aa8fd92dbe38ac6f1 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 28 Aug 2020 00:53:36 -0400 Subject: [PATCH 19/54] Say 'prefix with `kind@`' instead of 'prefix with the item kind' This is both more specific and easier to read. --- .../passes/collect_intra_doc_links.rs | 57 +++++++++++++------ src/test/rustdoc-ui/intra-link-errors.rs | 2 +- src/test/rustdoc-ui/intra-link-errors.stderr | 6 +- .../rustdoc-ui/intra-link-prim-conflict.rs | 4 +- .../intra-link-prim-conflict.stderr | 20 ++----- .../rustdoc-ui/intra-links-ambiguity.stderr | 12 ++-- .../intra-links-disambiguator-mismatch.rs | 18 +++--- .../intra-links-disambiguator-mismatch.stderr | 54 +++--------------- 8 files changed, 73 insertions(+), 100 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0ae322ee11ae3..a19387ad992fa 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1328,21 +1328,16 @@ impl Disambiguator { } } - /// Return (description of the change, suggestion) - fn suggestion_for(self, path_str: &str) -> (&'static str, String) { - const PREFIX: &str = "prefix with the item kind"; - const FUNCTION: &str = "add parentheses"; - const MACRO: &str = "add an exclamation mark"; - + fn suggestion(self) -> Suggestion { let kind = match self { - Disambiguator::Primitive => return (PREFIX, format!("prim@{}", path_str)), + Disambiguator::Primitive => return Suggestion::Prefix("prim"), Disambiguator::Kind(kind) => kind, Disambiguator::Namespace(_) => panic!("display_for cannot be used on namespaces"), }; if kind == DefKind::Macro(MacroKind::Bang) { - return (MACRO, format!("{}!", path_str)); + return Suggestion::Macro; } else if kind == DefKind::Fn || kind == DefKind::AssocFn { - return (FUNCTION, format!("{}()", path_str)); + return Suggestion::Function; } let prefix = match kind { @@ -1367,8 +1362,7 @@ impl Disambiguator { }, }; - // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` - (PREFIX, format!("{}@{}", prefix, path_str)) + Suggestion::Prefix(prefix) } fn ns(self) -> Namespace { @@ -1400,6 +1394,31 @@ impl Disambiguator { } } +enum Suggestion { + Prefix(&'static str), + Function, + Macro, +} + +impl Suggestion { + fn descr(&self) -> Cow<'static, str> { + match self { + Self::Prefix(x) => format!("prefix with `{}@`", x).into(), + Self::Function => "add parentheses".into(), + Self::Macro => "add an exclamation mark".into(), + } + } + + fn as_help(&self, path_str: &str) -> String { + // FIXME: if this is an implied shortcut link, it's bad style to suggest `@` + match self { + Self::Prefix(prefix) => format!("{}@{}", prefix, path_str), + Self::Function => format!("{}()", path_str), + Self::Macro => format!("{}!", path_str), + } + } +} + /// Reports a diagnostic for an intra-doc link. /// /// If no link range is provided, or the source span of the link cannot be determined, the span of @@ -1695,18 +1714,20 @@ fn suggest_disambiguator( sp: Option, link_range: &Option>, ) { - let (action, mut suggestion) = disambiguator.suggestion_for(path_str); - let help = format!("to link to the {}, {}", disambiguator.descr(), action); + let suggestion = disambiguator.suggestion(); + let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr()); if let Some(sp) = sp { let link_range = link_range.as_ref().expect("must have a link range if we have a span"); - if dox.bytes().nth(link_range.start) == Some(b'`') { - suggestion = format!("`{}`", suggestion); - } + let msg = if dox.bytes().nth(link_range.start) == Some(b'`') { + format!("`{}`", suggestion.as_help(path_str)) + } else { + suggestion.as_help(path_str) + }; - diag.span_suggestion(sp, &help, suggestion, Applicability::MaybeIncorrect); + diag.span_suggestion(sp, &help, msg, Applicability::MaybeIncorrect); } else { - diag.help(&format!("{}: {}", help, suggestion)); + diag.help(&format!("{}: {}", help, suggestion.as_help(path_str))); } } diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index f66bb8a453305..919c46fe1dab2 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -42,7 +42,7 @@ /// [S!] //~^ ERROR unresolved link -//~| HELP to link to the struct, prefix with the item kind +//~| HELP to link to the struct, prefix with `struct@` //~| NOTE this link resolves to the struct `S` pub fn f() {} #[derive(Debug)] diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 4dac892b82051..7318193964f8d 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -79,13 +79,9 @@ error: unresolved link to `S` --> $DIR/intra-link-errors.rs:44:6 | LL | /// [S!] - | ^^ + | ^^ help: to link to the struct, prefix with `struct@`: `struct@S` | = note: this link resolves to the struct `S`, which is not in the macro namespace -help: to link to the struct, prefix with the item kind - | -LL | /// [struct@S] - | ^^^^^^^^ error: unresolved link to `T::g` --> $DIR/intra-link-errors.rs:62:6 diff --git a/src/test/rustdoc-ui/intra-link-prim-conflict.rs b/src/test/rustdoc-ui/intra-link-prim-conflict.rs index 548d3e2544a00..85738ceae8e61 100644 --- a/src/test/rustdoc-ui/intra-link-prim-conflict.rs +++ b/src/test/rustdoc-ui/intra-link-prim-conflict.rs @@ -18,13 +18,13 @@ /// [struct@char] //~^ ERROR incompatible link -//~| HELP prefix with the item kind +//~| HELP prefix with `mod@` //~| NOTE resolved to a module pub mod char {} pub mod inner { //! [struct@char] //~^ ERROR incompatible link - //~| HELP prefix with the item kind + //~| HELP prefix with `prim@` //~| NOTE resolved to a builtin type } diff --git a/src/test/rustdoc-ui/intra-link-prim-conflict.stderr b/src/test/rustdoc-ui/intra-link-prim-conflict.stderr index 53dccfbf1a2c4..43587a80021af 100644 --- a/src/test/rustdoc-ui/intra-link-prim-conflict.stderr +++ b/src/test/rustdoc-ui/intra-link-prim-conflict.stderr @@ -9,11 +9,11 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ -help: to link to the module, prefix with the item kind +help: to link to the module, prefix with `mod@` | LL | /// [mod@char] | ^^^^^^^^ -help: to link to the builtin type, prefix with the item kind +help: to link to the builtin type, prefix with `prim@` | LL | /// [prim@char] | ^^^^^^^^^ @@ -24,11 +24,11 @@ error: `char` is both a module and a builtin type LL | /// [type@char] | ^^^^^^^^^ ambiguous link | -help: to link to the module, prefix with the item kind +help: to link to the module, prefix with `mod@` | LL | /// [mod@char] | ^^^^^^^^ -help: to link to the builtin type, prefix with the item kind +help: to link to the builtin type, prefix with `prim@` | LL | /// [prim@char] | ^^^^^^^^^ @@ -37,25 +37,17 @@ error: incompatible link kind for `char` --> $DIR/intra-link-prim-conflict.rs:19:6 | LL | /// [struct@char] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: to link to the module, prefix with `mod@`: `mod@char` | = note: this link resolved to a module, which is not a struct -help: to link to the module, prefix with the item kind - | -LL | /// [mod@char] - | ^^^^^^^^ error: incompatible link kind for `char` --> $DIR/intra-link-prim-conflict.rs:26:10 | LL | //! [struct@char] - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: to link to the builtin type, prefix with `prim@`: `prim@char` | = note: this link resolved to a builtin type, which is not a struct -help: to link to the builtin type, prefix with the item kind - | -LL | //! [prim@char] - | ^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/src/test/rustdoc-ui/intra-links-ambiguity.stderr b/src/test/rustdoc-ui/intra-links-ambiguity.stderr index 7912c046f1c78..17891ca05efa1 100644 --- a/src/test/rustdoc-ui/intra-links-ambiguity.stderr +++ b/src/test/rustdoc-ui/intra-links-ambiguity.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ -help: to link to the struct, prefix with the item kind +help: to link to the struct, prefix with `struct@` | LL | /// [`struct@ambiguous`] is ambiguous. | ^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ error: `ambiguous` is both a struct and a function LL | /// [ambiguous] is ambiguous. | ^^^^^^^^^ ambiguous link | -help: to link to the struct, prefix with the item kind +help: to link to the struct, prefix with `struct@` | LL | /// [struct@ambiguous] is ambiguous. | ^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ error: `multi_conflict` is a struct, a function, and a macro LL | /// [`multi_conflict`] is a three-way conflict. | ^^^^^^^^^^^^^^^^ ambiguous link | -help: to link to the struct, prefix with the item kind +help: to link to the struct, prefix with `struct@` | LL | /// [`struct@multi_conflict`] is a three-way conflict. | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -58,11 +58,11 @@ error: `type_and_value` is both a module and a constant LL | /// Ambiguous [type_and_value]. | ^^^^^^^^^^^^^^ ambiguous link | -help: to link to the module, prefix with the item kind +help: to link to the module, prefix with `mod@` | LL | /// Ambiguous [mod@type_and_value]. | ^^^^^^^^^^^^^^^^^^ -help: to link to the constant, prefix with the item kind +help: to link to the constant, prefix with `const@` | LL | /// Ambiguous [const@type_and_value]. | ^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ error: `foo::bar` is both an enum and a function LL | /// Ambiguous non-implied shortcut link [`foo::bar`]. | ^^^^^^^^^^ ambiguous link | -help: to link to the enum, prefix with the item kind +help: to link to the enum, prefix with `enum@` | LL | /// Ambiguous non-implied shortcut link [`enum@foo::bar`]. | ^^^^^^^^^^^^^^^ diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs index 54e507adfe550..b9c8e033b1b21 100644 --- a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs +++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs @@ -14,27 +14,27 @@ trait T {} /// Link to [struct@S] //~^ ERROR incompatible link kind for `S` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `enum@` /// Link to [mod@S] //~^ ERROR incompatible link kind for `S` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `enum@` /// Link to [union@S] //~^ ERROR incompatible link kind for `S` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `enum@` /// Link to [trait@S] //~^ ERROR incompatible link kind for `S` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `enum@` /// Link to [struct@T] //~^ ERROR incompatible link kind for `T` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `trait@` /// Link to [derive@m] //~^ ERROR incompatible link kind for `m` @@ -44,22 +44,22 @@ trait T {} /// Link to [const@s] //~^ ERROR incompatible link kind for `s` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `static@` /// Link to [static@c] //~^ ERROR incompatible link kind for `c` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `const@` /// Link to [fn@c] //~^ ERROR incompatible link kind for `c` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `const@` /// Link to [c()] //~^ ERROR incompatible link kind for `c` //~| NOTE this link resolved -//~| HELP prefix with the item kind +//~| HELP prefix with `const@` /// Link to [const@f] //~^ ERROR incompatible link kind for `f` diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr index 27b94af0378c2..2e732baf6e01e 100644 --- a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr +++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr @@ -2,7 +2,7 @@ error: incompatible link kind for `S` --> $DIR/intra-links-disambiguator-mismatch.rs:14:14 | LL | /// Link to [struct@S] - | ^^^^^^^^ + | ^^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` | note: the lint level is defined here --> $DIR/intra-links-disambiguator-mismatch.rs:1:9 @@ -10,58 +10,38 @@ note: the lint level is defined here LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: this link resolved to an enum, which is not a struct -help: to link to the enum, prefix with the item kind - | -LL | /// Link to [enum@S] - | ^^^^^^ error: incompatible link kind for `S` --> $DIR/intra-links-disambiguator-mismatch.rs:19:14 | LL | /// Link to [mod@S] - | ^^^^^ + | ^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` | = note: this link resolved to an enum, which is not a module -help: to link to the enum, prefix with the item kind - | -LL | /// Link to [enum@S] - | ^^^^^^ error: incompatible link kind for `S` --> $DIR/intra-links-disambiguator-mismatch.rs:24:14 | LL | /// Link to [union@S] - | ^^^^^^^ + | ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` | = note: this link resolved to an enum, which is not a union -help: to link to the enum, prefix with the item kind - | -LL | /// Link to [enum@S] - | ^^^^^^ error: incompatible link kind for `S` --> $DIR/intra-links-disambiguator-mismatch.rs:29:14 | LL | /// Link to [trait@S] - | ^^^^^^^ + | ^^^^^^^ help: to link to the enum, prefix with `enum@`: `enum@S` | = note: this link resolved to an enum, which is not a trait -help: to link to the enum, prefix with the item kind - | -LL | /// Link to [enum@S] - | ^^^^^^ error: incompatible link kind for `T` --> $DIR/intra-links-disambiguator-mismatch.rs:34:14 | LL | /// Link to [struct@T] - | ^^^^^^^^ + | ^^^^^^^^ help: to link to the trait, prefix with `trait@`: `trait@T` | = note: this link resolved to a trait, which is not a struct -help: to link to the trait, prefix with the item kind - | -LL | /// Link to [trait@T] - | ^^^^^^^ error: incompatible link kind for `m` --> $DIR/intra-links-disambiguator-mismatch.rs:39:14 @@ -75,49 +55,33 @@ error: incompatible link kind for `s` --> $DIR/intra-links-disambiguator-mismatch.rs:44:14 | LL | /// Link to [const@s] - | ^^^^^^^ + | ^^^^^^^ help: to link to the static, prefix with `static@`: `static@s` | = note: this link resolved to a static, which is not a constant -help: to link to the static, prefix with the item kind - | -LL | /// Link to [static@s] - | ^^^^^^^^ error: incompatible link kind for `c` --> $DIR/intra-links-disambiguator-mismatch.rs:49:14 | LL | /// Link to [static@c] - | ^^^^^^^^ + | ^^^^^^^^ help: to link to the constant, prefix with `const@`: `const@c` | = note: this link resolved to a constant, which is not a static -help: to link to the constant, prefix with the item kind - | -LL | /// Link to [const@c] - | ^^^^^^^ error: incompatible link kind for `c` --> $DIR/intra-links-disambiguator-mismatch.rs:54:14 | LL | /// Link to [fn@c] - | ^^^^ + | ^^^^ help: to link to the constant, prefix with `const@`: `const@c` | = note: this link resolved to a constant, which is not a function -help: to link to the constant, prefix with the item kind - | -LL | /// Link to [const@c] - | ^^^^^^^ error: incompatible link kind for `c` --> $DIR/intra-links-disambiguator-mismatch.rs:59:14 | LL | /// Link to [c()] - | ^^^ + | ^^^ help: to link to the constant, prefix with `const@`: `const@c` | = note: this link resolved to a constant, which is not a function -help: to link to the constant, prefix with the item kind - | -LL | /// Link to [const@c] - | ^^^^^^^ error: incompatible link kind for `f` --> $DIR/intra-links-disambiguator-mismatch.rs:64:14 From efdc3facdfe931108146587b2f9d22b7dfd217c5 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 28 Aug 2020 01:31:33 -0400 Subject: [PATCH 20/54] Give a much better error message when an item has a macro disambiguator Previously, this called `check_full_res` for values and types, but not macros. This would result in not showing when there was a partial resolution for a parent of the item. This now calls `check_full_res`. Additionally, it checks if there was a disambiguator, and if so, says that specific kind wasn't found instead of saying generically 'associated item'. --- .../passes/collect_intra_doc_links.rs | 87 ++++++++++++------- src/test/rustdoc-ui/intra-link-errors.rs | 2 +- src/test/rustdoc-ui/intra-link-errors.stderr | 2 +- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index a19387ad992fa..df8ecf26a412e 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -262,11 +262,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { false, ) { if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Ok(res.map_id(|_| panic!("unexpected id"))); + return Some(Ok(res.map_id(|_| panic!("unexpected id")))); } } if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) { - return Ok(res.map_id(|_| panic!("unexpected id"))); + return Some(Ok(res.map_id(|_| panic!("unexpected id")))); } if let Some(module_id) = parent_id { debug!("resolving {} as a macro in the module {:?}", path_str, module_id); @@ -276,14 +276,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // don't resolve builtins like `#[derive]` if let Res::Def(..) = res { let res = res.map_id(|_| panic!("unexpected node_id")); - return Ok(res); + return Some(Ok(res)); } } } else { debug!("attempting to resolve item without parent module: {}", path_str); - return Err(ResolutionFailure::NoParentItem); + return Some(Err(ResolutionFailure::NoParentItem)); } - return Err(ResolutionFailure::NotInScope(path_str.into())); + None + }) + // This weird control flow is so we don't borrow the resolver more than once at a time + .unwrap_or_else(|| { + let mut split = path_str.rsplitn(2, "::"); + if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) { + if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) { + return Err(if matches!(res, Res::PrimTy(_)) { + ResolutionFailure::NoPrimitiveAssocItem { + res, + prim_name: parent, + assoc_item: Symbol::intern(base), + } + } else { + ResolutionFailure::NoAssocItem(res, Symbol::intern(base)) + }); + } + } + Err(ResolutionFailure::NotInScope(path_str.into())) }) } /// Resolves a string as a path within a particular namespace. Also returns an optional @@ -981,6 +999,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { cx, &item, path_str, + disambiguator, &dox, link_range, smallvec![kind], @@ -1060,6 +1079,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { cx, &item, path_str, + disambiguator, &dox, link_range, candidates.into_iter().filter_map(|res| res.err()).collect(), @@ -1114,6 +1134,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { cx, &item, path_str, + disambiguator, &dox, link_range, smallvec![kind], @@ -1489,6 +1510,7 @@ fn resolution_failure( cx: &DocContext<'_>, item: &Item, path_str: &str, + disambiguator: Option, dox: &str, link_range: Option>, kinds: SmallVec<[ResolutionFailure<'_>; 3]>, @@ -1581,34 +1603,39 @@ fn resolution_failure( let (kind, def_id) = match res { Res::Def(kind, def_id) => (kind, def_id), - _ => unreachable!( - "primitives are covered above and other `Res` variants aren't possible at module scope" + x => unreachable!( + "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}", + x, ), }; let name = cx.tcx.item_name(def_id); - let path_description = match kind { - Mod | ForeignMod => "inner item", - Struct => "field or associated item", - Enum | Union => "variant or associated item", - Variant - | Field - | Closure - | Generator - | AssocTy - | AssocConst - | AssocFn - | Fn - | Macro(_) - | Const - | ConstParam - | ExternCrate - | Use - | LifetimeParam - | Ctor(_, _) - | AnonConst => return assoc_item_not_allowed(res, diag), - Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam - | Static => "associated item", - Impl | GlobalAsm => unreachable!("not a path"), + let path_description = if let Some(disambiguator) = disambiguator { + disambiguator.descr() + } else { + match kind { + Mod | ForeignMod => "inner item", + Struct => "field or associated item", + Enum | Union => "variant or associated item", + Variant + | Field + | Closure + | Generator + | AssocTy + | AssocConst + | AssocFn + | Fn + | Macro(_) + | Const + | ConstParam + | ExternCrate + | Use + | LifetimeParam + | Ctor(_, _) + | AnonConst => return assoc_item_not_allowed(res, diag), + Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam + | Static => "associated item", + Impl | GlobalAsm => unreachable!("not a path"), + } }; let note = format!( "the {} `{}` has no {} named `{}`", diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 919c46fe1dab2..ae89f418984d2 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -65,7 +65,7 @@ impl S { /// [T::h!] //~^ ERROR unresolved link -//~| NOTE no item named `T::h` +//~| NOTE `T` has no macro named `h` pub trait T { fn g() {} } diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 7318193964f8d..68941d64c7a2c 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -97,7 +97,7 @@ error: unresolved link to `T::h` LL | /// [T::h!] | ^^^^^ | - = note: no item named `T::h` is in scope + = note: the trait `T` has no macro named `h` error: unresolved link to `S::h` --> $DIR/intra-link-errors.rs:54:6 From 8318a185f301de617c64376fbd3a2d556296c8bb Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Sat, 29 Aug 2020 09:59:57 -0400 Subject: [PATCH 21/54] x.py bless --- src/test/rustdoc-ui/intra-link-errors.stderr | 28 ++++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 68941d64c7a2c..73e16bef0aa9f 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -1,5 +1,5 @@ error: unresolved link to `path::to::nonexistent::module` - --> $DIR/intra-link-errors.rs:8:6 + --> $DIR/intra-link-errors.rs:7:6 | LL | /// [path::to::nonexistent::module] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(broken_intra_doc_links)] = note: no item named `path` is in scope error: unresolved link to `std::io::not::here` - --> $DIR/intra-link-errors.rs:12:6 + --> $DIR/intra-link-errors.rs:11:6 | LL | /// [std::io::not::here] | ^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | /// [std::io::not::here] = note: the module `io` has no inner item named `not` error: unresolved link to `std::io::Error::x` - --> $DIR/intra-link-errors.rs:16:6 + --> $DIR/intra-link-errors.rs:15:6 | LL | /// [std::io::Error::x] | ^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | /// [std::io::Error::x] = note: the struct `Error` has no field or associated item named `x` error: unresolved link to `std::io::ErrorKind::x` - --> $DIR/intra-link-errors.rs:20:6 + --> $DIR/intra-link-errors.rs:19:6 | LL | /// [std::io::ErrorKind::x] | ^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | /// [std::io::ErrorKind::x] = note: the enum `ErrorKind` has no variant or associated item named `x` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:24:6 + --> $DIR/intra-link-errors.rs:23:6 | LL | /// [f::A] | ^^^^ @@ -44,7 +44,7 @@ LL | /// [f::A] = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:28:6 + --> $DIR/intra-link-errors.rs:27:6 | LL | /// [S::A] | ^^^^ @@ -52,7 +52,7 @@ LL | /// [S::A] = note: the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:32:6 + --> $DIR/intra-link-errors.rs:31:6 | LL | /// [S::fmt] | ^^^^^^ @@ -60,7 +60,7 @@ LL | /// [S::fmt] = note: the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:36:6 + --> $DIR/intra-link-errors.rs:35:6 | LL | /// [E::D] | ^^^^ @@ -68,7 +68,7 @@ LL | /// [E::D] = note: the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:40:6 + --> $DIR/intra-link-errors.rs:39:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -76,7 +76,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:44:6 + --> $DIR/intra-link-errors.rs:43:6 | LL | /// [S!] | ^^ help: to link to the struct, prefix with `struct@`: `struct@S` @@ -84,7 +84,7 @@ LL | /// [S!] = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:62:6 + --> $DIR/intra-link-errors.rs:61:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `T::g()` @@ -92,7 +92,7 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:67:6 + --> $DIR/intra-link-errors.rs:66:6 | LL | /// [T::h!] | ^^^^^ @@ -100,7 +100,7 @@ LL | /// [T::h!] = note: the trait `T` has no macro named `h` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:54:6 + --> $DIR/intra-link-errors.rs:53:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `S::h()` @@ -108,7 +108,7 @@ LL | /// [type@S::h] = note: this link resolves to the associated function `h`, which is not in the type namespace error: unresolved link to `m` - --> $DIR/intra-link-errors.rs:74:6 + --> $DIR/intra-link-errors.rs:73:6 | LL | /// [m()] | ^^^ help: to link to the macro, add an exclamation mark: `m!` From cd72d9029ff5b2368e5c539f9b326a2eea855127 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 31 Aug 2020 23:28:38 -0400 Subject: [PATCH 22/54] Find the first segment that failed to resolve for _any_ namespace Moves this detection into `resolution_failure` to avoid doing unnecessary work and make the control flow a little easier to work with. --- .../passes/collect_intra_doc_links.rs | 109 ++++++++++-------- src/test/rustdoc-ui/intra-link-errors.rs | 8 ++ src/test/rustdoc-ui/intra-link-errors.stderr | 44 ++++--- 3 files changed, 102 insertions(+), 59 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index df8ecf26a412e..d8abf411de7c2 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -60,8 +60,8 @@ enum ResolutionFailure<'a> { /// This has a partial resolution, but is not in the TypeNS and so cannot /// have associated items or fields. CannotHaveAssociatedItems(Res, Namespace), - /// `String` is the base name of the path (not necessarily the whole link) - NotInScope(Cow<'a, str>), + /// `name` is the base name of the path (not necessarily the whole link) + NotInScope { module_id: DefId, name: Cow<'a, str> }, /// this is a primitive type without an impls (no associated methods) /// when will this actually happen? /// the `Res` is the primitive it resolved to @@ -92,7 +92,7 @@ impl ResolutionFailure<'a> { | NotAVariant(res, _) | WrongNamespace(res, _) | CannotHaveAssociatedItems(res, _) => Some(*res), - NotInScope(_) | NoParentItem | Dummy => None, + NotInScope { .. } | NoParentItem | Dummy => None, } } @@ -142,7 +142,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .expect("fold_item should ensure link is non-empty"); let variant_name = // we're not sure this is a variant at all, so use the full string - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_str.into())))?; + split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope{ + module_id, + name: path_str.into(), + }))?; let path = split .next() .map(|f| { @@ -153,38 +156,21 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope( - variant_name.to_string().into(), - )))?; + .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope { + module_id, + name: variant_name.to_string().into(), + }))?; let ty_res = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) }) .map(|(_, res)| res) .unwrap_or(Res::Err); - // This code only gets hit if three path segments in a row don't get resolved. - // It's a good time to check if _any_ parent of the path gets resolved. - // If so, report it and say the first which failed; if not, say the first path segment didn't resolve. if let Res::Err = ty_res { - let mut current = path.as_str(); - while let Some(parent) = current.rsplitn(2, "::").nth(1) { - current = parent; - if let Some(res) = self.check_full_res( - TypeNS, - ¤t, - Some(module_id), - current_item, - extra_fragment, - ) { - return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( - res, - Symbol::intern(&path), - ))); - } - } - return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope( - current.to_string().into(), - ))); + return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope { + module_id, + name: path.into(), + })); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -301,7 +287,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }); } } - Err(ResolutionFailure::NotInScope(path_str.into())) + Err(ResolutionFailure::NotInScope { + module_id: parent_id.expect("already saw `Some` when resolving as a macro"), + name: path_str.into(), + }) }) } /// Resolves a string as a path within a particular namespace. Also returns an optional @@ -384,7 +373,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. .ok_or_else(|| { debug!("found no `::`, assumming {} was correctly not in scope", item_name); - ErrorKind::Resolve(ResolutionFailure::NotInScope(item_name.to_string().into())) + ErrorKind::Resolve(ResolutionFailure::NotInScope { + module_id, + name: item_name.to_string().into(), + }) })?; if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { @@ -451,7 +443,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } } - ResolutionFailure::NotInScope(path_root.into()) + ResolutionFailure::NotInScope { module_id, name: path_root.into() } }); Err(ErrorKind::Resolve(kind)) }; @@ -996,7 +988,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } resolution_failure( - cx, + self, &item, path_str, disambiguator, @@ -1076,7 +1068,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { if len == 0 { drop(candidates_iter); resolution_failure( - cx, + self, &item, path_str, disambiguator, @@ -1096,8 +1088,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } else { drop(candidates_iter); if is_derive_trait_collision(&candidates) { - candidates.macro_ns = - Err(ResolutionFailure::NotInScope(path_str.into())); + candidates.macro_ns = Err(ResolutionFailure::Dummy); } // If we're reporting an ambiguity, don't mention the namespaces that failed let candidates = @@ -1131,7 +1122,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } } resolution_failure( - cx, + self, &item, path_str, disambiguator, @@ -1507,7 +1498,7 @@ fn report_diagnostic( } fn resolution_failure( - cx: &DocContext<'_>, + collector: &LinkCollector<'_, '_>, item: &Item, path_str: &str, disambiguator: Option, @@ -1516,7 +1507,7 @@ fn resolution_failure( kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { report_diagnostic( - cx, + collector.cx, &format!("unresolved link to `{}`", path_str), item, dox, @@ -1524,11 +1515,15 @@ fn resolution_failure( |diag, sp| { let in_scope = kinds.iter().any(|kind| kind.res().is_some()); let item = |res: Res| { - format!("the {} `{}`", res.descr(), cx.tcx.item_name(res.def_id()).to_string()) + format!( + "the {} `{}`", + res.descr(), + collector.cx.tcx.item_name(res.def_id()).to_string() + ) }; let assoc_item_not_allowed = |res: Res, diag: &mut DiagnosticBuilder<'_>| { let def_id = res.def_id(); - let name = cx.tcx.item_name(def_id); + let name = collector.cx.tcx.item_name(def_id); let note = format!( "`{}` is {} {}, not a module or type, and cannot have associated items", name, @@ -1539,18 +1534,42 @@ fn resolution_failure( }; // ignore duplicates let mut variants_seen = SmallVec::<[_; 3]>::new(); - for failure in kinds { + for mut failure in kinds { + // Check if _any_ parent of the path gets resolved. + // If so, report it and say the first which failed; if not, say the first path segment didn't resolve. + if let ResolutionFailure::NotInScope { module_id, name } = &mut failure { + let mut current = name.as_ref(); + loop { + current = match current.rsplitn(2, "::").nth(1) { + Some(p) => p, + None => { + *name = current.to_owned().into(); + break; + } + }; + if let Some(res) = collector.check_full_res( + TypeNS, + ¤t, + Some(*module_id), + &None, + &None, + ) { + failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current)); + break; + } + } + } let variant = std::mem::discriminant(&failure); if variants_seen.contains(&variant) { continue; } variants_seen.push(variant); match failure { - ResolutionFailure::NotInScope(base) => { + ResolutionFailure::NotInScope { name, .. } => { if in_scope { continue; } - diag.note(&format!("no item named `{}` is in scope", base)); + diag.note(&format!("no item named `{}` is in scope", name)); // If the link has `::` in the path, assume it's meant to be an intra-doc link if !path_str.contains("::") { // Otherwise, the `[]` might be unrelated. @@ -1608,7 +1627,7 @@ fn resolution_failure( x, ), }; - let name = cx.tcx.item_name(def_id); + let name = collector.cx.tcx.item_name(def_id); let path_description = if let Some(disambiguator) = disambiguator { disambiguator.descr() } else { diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index ae89f418984d2..477ad6464f887 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -8,6 +8,14 @@ //~^ ERROR unresolved link //~| NOTE no item named `path` is in scope +/// [path::to::nonexistent::macro!] +//~^ ERROR unresolved link +//~| NOTE no item named `path` is in scope + +/// [type@path::to::nonexistent::type] +//~^ ERROR unresolved link +//~| NOTE no item named `path` is in scope + /// [std::io::not::here] //~^ ERROR unresolved link //~| NOTE the module `io` has no inner item diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 73e16bef0aa9f..3b1a09f913e9f 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -11,16 +11,32 @@ LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ = note: no item named `path` is in scope -error: unresolved link to `std::io::not::here` +error: unresolved link to `path::to::nonexistent::macro` --> $DIR/intra-link-errors.rs:11:6 | +LL | /// [path::to::nonexistent::macro!] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: no item named `path` is in scope + +error: unresolved link to `path::to::nonexistent::type` + --> $DIR/intra-link-errors.rs:15:6 + | +LL | /// [type@path::to::nonexistent::type] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: no item named `path` is in scope + +error: unresolved link to `std::io::not::here` + --> $DIR/intra-link-errors.rs:19:6 + | LL | /// [std::io::not::here] | ^^^^^^^^^^^^^^^^^^ | = note: the module `io` has no inner item named `not` error: unresolved link to `std::io::Error::x` - --> $DIR/intra-link-errors.rs:15:6 + --> $DIR/intra-link-errors.rs:23:6 | LL | /// [std::io::Error::x] | ^^^^^^^^^^^^^^^^^ @@ -28,7 +44,7 @@ LL | /// [std::io::Error::x] = note: the struct `Error` has no field or associated item named `x` error: unresolved link to `std::io::ErrorKind::x` - --> $DIR/intra-link-errors.rs:19:6 + --> $DIR/intra-link-errors.rs:27:6 | LL | /// [std::io::ErrorKind::x] | ^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +52,7 @@ LL | /// [std::io::ErrorKind::x] = note: the enum `ErrorKind` has no variant or associated item named `x` error: unresolved link to `f::A` - --> $DIR/intra-link-errors.rs:23:6 + --> $DIR/intra-link-errors.rs:31:6 | LL | /// [f::A] | ^^^^ @@ -44,7 +60,7 @@ LL | /// [f::A] = note: `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` - --> $DIR/intra-link-errors.rs:27:6 + --> $DIR/intra-link-errors.rs:35:6 | LL | /// [S::A] | ^^^^ @@ -52,7 +68,7 @@ LL | /// [S::A] = note: the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` - --> $DIR/intra-link-errors.rs:31:6 + --> $DIR/intra-link-errors.rs:39:6 | LL | /// [S::fmt] | ^^^^^^ @@ -60,7 +76,7 @@ LL | /// [S::fmt] = note: the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` - --> $DIR/intra-link-errors.rs:35:6 + --> $DIR/intra-link-errors.rs:43:6 | LL | /// [E::D] | ^^^^ @@ -68,7 +84,7 @@ LL | /// [E::D] = note: the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` - --> $DIR/intra-link-errors.rs:39:6 + --> $DIR/intra-link-errors.rs:47:6 | LL | /// [u8::not_found] | ^^^^^^^^^^^^^ @@ -76,7 +92,7 @@ LL | /// [u8::not_found] = note: the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` - --> $DIR/intra-link-errors.rs:43:6 + --> $DIR/intra-link-errors.rs:51:6 | LL | /// [S!] | ^^ help: to link to the struct, prefix with `struct@`: `struct@S` @@ -84,7 +100,7 @@ LL | /// [S!] = note: this link resolves to the struct `S`, which is not in the macro namespace error: unresolved link to `T::g` - --> $DIR/intra-link-errors.rs:61:6 + --> $DIR/intra-link-errors.rs:69:6 | LL | /// [type@T::g] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `T::g()` @@ -92,7 +108,7 @@ LL | /// [type@T::g] = note: this link resolves to the associated function `g`, which is not in the type namespace error: unresolved link to `T::h` - --> $DIR/intra-link-errors.rs:66:6 + --> $DIR/intra-link-errors.rs:74:6 | LL | /// [T::h!] | ^^^^^ @@ -100,7 +116,7 @@ LL | /// [T::h!] = note: the trait `T` has no macro named `h` error: unresolved link to `S::h` - --> $DIR/intra-link-errors.rs:53:6 + --> $DIR/intra-link-errors.rs:61:6 | LL | /// [type@S::h] | ^^^^^^^^^ help: to link to the associated function, add parentheses: `S::h()` @@ -108,12 +124,12 @@ LL | /// [type@S::h] = note: this link resolves to the associated function `h`, which is not in the type namespace error: unresolved link to `m` - --> $DIR/intra-link-errors.rs:73:6 + --> $DIR/intra-link-errors.rs:81:6 | LL | /// [m()] | ^^^ help: to link to the macro, add an exclamation mark: `m!` | = note: this link resolves to the macro `m`, which is not in the value namespace -error: aborting due to 14 previous errors +error: aborting due to 16 previous errors From b869aa5f316ae065ce2215e69811e3216c6250cb Mon Sep 17 00:00:00 2001 From: marmeladema Date: Sun, 30 Aug 2020 18:31:34 +0100 Subject: [PATCH 23/54] Add saturating methods for `Duration` --- library/core/src/lib.rs | 1 + library/core/src/time.rs | 97 +++++++++++++++++++++++++ library/core/tests/lib.rs | 2 + library/core/tests/time.rs | 30 ++++++++ src/test/ui/consts/duration-consts-2.rs | 42 +++++++---- 5 files changed, 156 insertions(+), 16 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 29bbf062f22e4..c3cadcbb01e31 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -100,6 +100,7 @@ #![feature(doc_cfg)] #![feature(doc_spotlight)] #![feature(duration_consts_2)] +#![feature(duration_saturating_ops)] #![feature(extern_types)] #![feature(fundamental)] #![feature(intrinsics)] diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 5741f8a53b522..f39781788d7c0 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -108,6 +108,34 @@ impl Duration { #[unstable(feature = "duration_constants", issue = "57391")] pub const NANOSECOND: Duration = Duration::from_nanos(1); + /// The minimum duration. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MIN, Duration::new(0, 0)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MIN: Duration = Duration::from_nanos(0); + + /// The maximum duration. + /// + /// It is roughly equal to a duration of 584,942,417,355 years. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::MAX, Duration::new(u64::MAX, 1_000_000_000 - 1)); + /// ``` + #[unstable(feature = "duration_constants", issue = "57391")] + pub const MAX: Duration = Duration::new(u64::MAX, NANOS_PER_SEC - 1); + /// Creates a new `Duration` from the specified number of whole seconds and /// additional nanoseconds. /// @@ -450,6 +478,29 @@ impl Duration { } } + /// Saturating `Duration` addition. Computes `self + other`, returning [`Duration::MAX`] + /// if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); + /// assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_add(self, rhs: Duration) -> Duration { + match self.checked_add(rhs) { + Some(res) => res, + None => Duration::MAX, + } + } + /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`] /// if the result would be negative or if overflow occurred. /// @@ -485,6 +536,29 @@ impl Duration { } } + /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`] + /// if the result would be negative or if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); + /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_sub(self, rhs: Duration) -> Duration { + match self.checked_sub(rhs) { + Some(res) => res, + None => Duration::MIN, + } + } + /// Checked `Duration` multiplication. Computes `self * other`, returning /// [`None`] if overflow occurred. /// @@ -515,6 +589,29 @@ impl Duration { None } + /// Saturating `Duration` multiplication. Computes `self * other`, returning + /// [`Duration::MAX`] if overflow occurred. + /// + /// # Examples + /// + /// ``` + /// #![feature(duration_saturating_ops)] + /// #![feature(duration_constants)] + /// use std::time::Duration; + /// + /// assert_eq!(Duration::new(0, 500_000_001).saturating_mul(2), Duration::new(1, 2)); + /// assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); + /// ``` + #[unstable(feature = "duration_saturating_ops", issue = "76416")] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_2", issue = "72440")] + pub const fn saturating_mul(self, rhs: u32) -> Duration { + match self.checked_mul(rhs) { + Some(res) => res, + None => Duration::MAX, + } + } + /// Checked `Duration` division. Computes `self / other`, returning [`None`] /// if `other == 0`. /// diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 4a651e5aa0ee3..a2e294ace1860 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -10,6 +10,8 @@ #![feature(core_private_diy_float)] #![feature(debug_non_exhaustive)] #![feature(dec2flt)] +#![feature(duration_constants)] +#![feature(duration_saturating_ops)] #![feature(exact_size_is_empty)] #![feature(fixed_size_array)] #![feature(flt2dec)] diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs index 7a6675dc82fa6..4f90eb63b0472 100644 --- a/library/core/tests/time.rs +++ b/library/core/tests/time.rs @@ -89,6 +89,16 @@ fn checked_add() { assert_eq!(Duration::new(1, 0).checked_add(Duration::new(u64::MAX, 0)), None); } +#[test] +fn saturating_add() { + assert_eq!(Duration::new(0, 0).saturating_add(Duration::new(0, 1)), Duration::new(0, 1)); + assert_eq!( + Duration::new(0, 500_000_000).saturating_add(Duration::new(0, 500_000_001)), + Duration::new(1, 1) + ); + assert_eq!(Duration::new(1, 0).saturating_add(Duration::new(u64::MAX, 0)), Duration::MAX); +} + #[test] fn sub() { assert_eq!(Duration::new(0, 1) - Duration::new(0, 0), Duration::new(0, 1)); @@ -107,6 +117,17 @@ fn checked_sub() { assert_eq!(zero.checked_sub(one_sec), None); } +#[test] +fn saturating_sub() { + let zero = Duration::new(0, 0); + let one_nano = Duration::new(0, 1); + let one_sec = Duration::new(1, 0); + assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1)); + assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999)); + assert_eq!(zero.saturating_sub(one_nano), Duration::MIN); + assert_eq!(zero.saturating_sub(one_sec), Duration::MIN); +} + #[test] #[should_panic] fn sub_bad1() { @@ -136,6 +157,15 @@ fn checked_mul() { assert_eq!(Duration::new(u64::MAX - 1, 0).checked_mul(2), None); } +#[test] +fn saturating_mul() { + assert_eq!(Duration::new(0, 1).saturating_mul(2), Duration::new(0, 2)); + assert_eq!(Duration::new(1, 1).saturating_mul(3), Duration::new(3, 3)); + assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4), Duration::new(2, 4)); + assert_eq!(Duration::new(0, 500_000_001).saturating_mul(4000), Duration::new(2000, 4000)); + assert_eq!(Duration::new(u64::MAX - 1, 0).saturating_mul(2), Duration::MAX); +} + #[test] fn div() { assert_eq!(Duration::new(0, 1) / 2, Duration::new(0, 0)); diff --git a/src/test/ui/consts/duration-consts-2.rs b/src/test/ui/consts/duration-consts-2.rs index c8b3939933126..bc0969e4f1fba 100644 --- a/src/test/ui/consts/duration-consts-2.rs +++ b/src/test/ui/consts/duration-consts-2.rs @@ -3,6 +3,7 @@ #![feature(const_panic)] #![feature(duration_consts_2)] #![feature(div_duration)] +#![feature(duration_saturating_ops)] use std::time::Duration; @@ -15,29 +16,29 @@ fn duration() { const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1); - const MAX_ADD_ZERO : Option = MAX.checked_add(ZERO); - assert_eq!(MAX_ADD_ZERO, Some(MAX)); + const MAX_CHECKED_ADD_ZERO : Option = MAX.checked_add(ZERO); + assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX)); - const MAX_ADD_ONE : Option = MAX.checked_add(ONE); - assert_eq!(MAX_ADD_ONE, None); + const MAX_CHECKED_ADD_ONE : Option = MAX.checked_add(ONE); + assert_eq!(MAX_CHECKED_ADD_ONE, None); - const ONE_SUB_ONE : Option = ONE.checked_sub(ONE); - assert_eq!(ONE_SUB_ONE, Some(ZERO)); + const ONE_CHECKED_SUB_ONE : Option = ONE.checked_sub(ONE); + assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO)); - const ZERO_SUB_ONE : Option = ZERO.checked_sub(ONE); - assert_eq!(ZERO_SUB_ONE, None); + const ZERO_CHECKED_SUB_ONE : Option = ZERO.checked_sub(ONE); + assert_eq!(ZERO_CHECKED_SUB_ONE, None); - const ONE_MUL_ONE : Option = ONE.checked_mul(1); - assert_eq!(ONE_MUL_ONE, Some(ONE)); + const ONE_CHECKED_MUL_ONE : Option = ONE.checked_mul(1); + assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE)); - const MAX_MUL_TWO : Option = MAX.checked_mul(2); - assert_eq!(MAX_MUL_TWO, None); + const MAX_CHECKED_MUL_TWO : Option = MAX.checked_mul(2); + assert_eq!(MAX_CHECKED_MUL_TWO, None); - const ONE_DIV_ONE : Option = ONE.checked_div(1); - assert_eq!(ONE_DIV_ONE, Some(ONE)); + const ONE_CHECKED_DIV_ONE : Option = ONE.checked_div(1); + assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE)); - const ONE_DIV_ZERO : Option = ONE.checked_div(0); - assert_eq!(ONE_DIV_ZERO, None); + const ONE_CHECKED_DIV_ZERO : Option = ONE.checked_div(0); + assert_eq!(ONE_CHECKED_DIV_ZERO, None); const MAX_AS_F32 : f32 = MAX.as_secs_f32(); assert_eq!(MAX_AS_F32, 18446744000000000000.0_f32); @@ -50,6 +51,15 @@ fn duration() { const ONE_AS_F64 : f64 = ONE.div_duration_f64(ONE); assert_eq!(ONE_AS_F64, 1.0_f64); + + const MAX_SATURATING_ADD_ONE : Duration = MAX.saturating_add(ONE); + assert_eq!(MAX_SATURATING_ADD_ONE, MAX); + + const ZERO_SATURATING_SUB_ONE : Duration = ZERO.saturating_sub(ONE); + assert_eq!(ZERO_SATURATING_SUB_ONE, ZERO); + + const MAX_SATURATING_MUL_TWO : Duration = MAX.saturating_mul(2); + assert_eq!(MAX_SATURATING_MUL_TWO, MAX); } fn main() { From 75e471ade9ea6095eef6cd4ef35cfc0b8bfc9410 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 8 Sep 2020 19:01:05 +0200 Subject: [PATCH 24/54] Add MaybeUninit::drop. ManuallyDrop's documentation tells the user to use MaybeUninit instead when handling uninitialized data. However, the main functionality of ManuallyDrop (drop) was not available directly on MaybeUninit. Adding it makes it easier to switch from one to the other. --- library/core/src/mem/maybe_uninit.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index b64abf68c5e4a..e826ec0a5e3ee 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -2,6 +2,7 @@ use crate::any::type_name; use crate::fmt; use crate::intrinsics; use crate::mem::ManuallyDrop; +use crate::ptr; /// A wrapper type to construct uninitialized instances of `T`. /// @@ -573,6 +574,28 @@ impl MaybeUninit { } } + /// Drops the contained value in place. + /// + /// If you have ownership of the `MaybeUninit`, it is preferable to use + /// [`assume_init`] instead, which prevents duplicating the content. + /// + /// # Safety + /// + /// Calling this when the content is not yet fully initialized causes undefined + /// behavior: it is up to the caller to guarantee that the `MaybeUninit` really + /// is in an initialized state. + /// + /// This function runs the destructor of the contained value in place. + /// Afterwards, the memory is considered uninitialized again, but remains unmodified. + /// + /// [`assume_init`]: MaybeUninit::assume_init + #[unstable(feature = "maybe_uninit_extra", issue = "63567")] + pub unsafe fn drop(&mut self) { + // SAFETY: the caller must guarantee that `self` is initialized. + // Dropping the value in place is safe if that is the case. + unsafe { ptr::drop_in_place(self.as_mut_ptr()) } + } + /// Gets a shared reference to the contained value. /// /// This can be useful when we want to access a `MaybeUninit` that has been From caef83282bfede657faf38d43967f577841b3be4 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 8 Sep 2020 19:34:23 +0200 Subject: [PATCH 25/54] Fix doc comment on MaybeUninit::drop. --- library/core/src/mem/maybe_uninit.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index e826ec0a5e3ee..001ee0898d45e 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -576,8 +576,7 @@ impl MaybeUninit { /// Drops the contained value in place. /// - /// If you have ownership of the `MaybeUninit`, it is preferable to use - /// [`assume_init`] instead, which prevents duplicating the content. + /// If you have ownership of the `MaybeUninit`, you can use [`assume_init`] instead. /// /// # Safety /// From 656a17b44d2e7ee8096f479ed8a04baab2200cae Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 9 Sep 2020 11:27:09 +0200 Subject: [PATCH 26/54] Rename MaybeUninit::drop to assume_init_drop. --- library/core/src/mem/maybe_uninit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 001ee0898d45e..8d19412e4c414 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -589,7 +589,7 @@ impl MaybeUninit { /// /// [`assume_init`]: MaybeUninit::assume_init #[unstable(feature = "maybe_uninit_extra", issue = "63567")] - pub unsafe fn drop(&mut self) { + pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized. // Dropping the value in place is safe if that is the case. unsafe { ptr::drop_in_place(self.as_mut_ptr()) } From a14efd1d0a2f0fa112e4359b9db1e9857589c796 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 9 Sep 2020 11:27:42 +0200 Subject: [PATCH 27/54] Rename MaybeUninit::read to assume_init_read. --- library/core/src/array/iter.rs | 4 ++-- library/core/src/mem/maybe_uninit.rs | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 2e8b6419eea1e..cafb002c01a11 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -103,7 +103,7 @@ impl Iterator for IntoIter { // dead now (i.e. do not touch). As `idx` was the start of the // alive-zone, the alive zone is now `data[alive]` again, restoring // all invariants. - unsafe { self.data.get_unchecked(idx).read() } + unsafe { self.data.get_unchecked(idx).assume_init_read() } }) } @@ -136,7 +136,7 @@ impl DoubleEndedIterator for IntoIter { // dead now (i.e. do not touch). As `idx` was the end of the // alive-zone, the alive zone is now `data[alive]` again, restoring // all invariants. - unsafe { self.data.get_unchecked(idx).read() } + unsafe { self.data.get_unchecked(idx).assume_init_read() } }) } } diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 8d19412e4c414..38a006ce74ce0 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -520,8 +520,8 @@ impl MaybeUninit { /// this initialization invariant. /// /// Moreover, this leaves a copy of the same data behind in the `MaybeUninit`. When using - /// multiple copies of the data (by calling `read` multiple times, or first - /// calling `read` and then [`assume_init`]), it is your responsibility + /// multiple copies of the data (by calling `assume_init_read` multiple times, or first + /// calling `assume_init_read` and then [`assume_init`]), it is your responsibility /// to ensure that that data may indeed be duplicated. /// /// [inv]: #initialization-invariant @@ -537,16 +537,16 @@ impl MaybeUninit { /// /// let mut x = MaybeUninit::::uninit(); /// x.write(13); - /// let x1 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; /// // `u32` is `Copy`, so we may read multiple times. - /// let x2 = unsafe { x.read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// assert_eq!(x1, x2); /// /// let mut x = MaybeUninit::>>::uninit(); /// x.write(None); - /// let x1 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; /// // Duplicating a `None` value is okay, so we may read multiple times. - /// let x2 = unsafe { x.read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// assert_eq!(x1, x2); /// ``` /// @@ -558,14 +558,14 @@ impl MaybeUninit { /// /// let mut x = MaybeUninit::>>::uninit(); /// x.write(Some(vec![0,1,2])); - /// let x1 = unsafe { x.read() }; - /// let x2 = unsafe { x.read() }; + /// let x1 = unsafe { x.assume_init_read() }; + /// let x2 = unsafe { x.assume_init_read() }; /// // We now created two copies of the same vector, leading to a double-free ⚠️ when /// // they both get dropped! /// ``` #[unstable(feature = "maybe_uninit_extra", issue = "63567")] #[inline(always)] - pub unsafe fn read(&self) -> T { + pub unsafe fn assume_init_read(&self) -> T { // SAFETY: the caller must guarantee that `self` is initialized. // Reading from `self.as_ptr()` is safe since `self` should be initialized. unsafe { From a94b2cb034c2521d52e54632b775e181eb7e0bc7 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 9 Sep 2020 18:54:17 +0200 Subject: [PATCH 28/54] Add safety docs about T's invariants in MaybeUninit::assume_init_drop. --- library/core/src/mem/maybe_uninit.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 38a006ce74ce0..0d1d563b5ceec 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -580,17 +580,23 @@ impl MaybeUninit { /// /// # Safety /// - /// Calling this when the content is not yet fully initialized causes undefined - /// behavior: it is up to the caller to guarantee that the `MaybeUninit` really - /// is in an initialized state. - /// - /// This function runs the destructor of the contained value in place. - /// Afterwards, the memory is considered uninitialized again, but remains unmodified. + /// It is up to the caller to guarantee that the `MaybeUninit` really is + /// in an initialized state. Calling this when the content is not yet fully + /// initialized causes undefined behavior. + /// + /// On top of that, all additional invariants of the type `T` must be + /// satisfied, as the `Drop` implementation of `T` (or its members) may + /// rely on this. For example, a `1`-initialized [`Vec`] is considered + /// initialized (under the current implementation; this does not constitute + /// a stable guarantee) because the only requirement the compiler knows + /// about it is that the data pointer must be non-null. Dropping such a + /// `Vec` however will cause undefined behaviour. /// /// [`assume_init`]: MaybeUninit::assume_init #[unstable(feature = "maybe_uninit_extra", issue = "63567")] pub unsafe fn assume_init_drop(&mut self) { - // SAFETY: the caller must guarantee that `self` is initialized. + // SAFETY: the caller must guarantee that `self` is initialized and + // satisfies all invariants of `T`. // Dropping the value in place is safe if that is the case. unsafe { ptr::drop_in_place(self.as_mut_ptr()) } } From 43c7a9b72b284bee6ce8517550cb6ee7903c639e Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 9 Sep 2020 18:56:16 +0200 Subject: [PATCH 29/54] Fix broken doc links in MaybeUninit. --- library/core/src/mem/maybe_uninit.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 0d1d563b5ceec..f68e2b8208982 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -472,6 +472,8 @@ impl MaybeUninit { /// *immediate* undefined behavior, but will cause undefined behavior with most /// safe operations (including dropping it). /// + /// [`Vec`]: ../../std/vec/struct.Vec.html + /// /// # Examples /// /// Correct usage of this method: @@ -593,6 +595,7 @@ impl MaybeUninit { /// `Vec` however will cause undefined behaviour. /// /// [`assume_init`]: MaybeUninit::assume_init + /// [`Vec`]: ../../std/vec/struct.Vec.html #[unstable(feature = "maybe_uninit_extra", issue = "63567")] pub unsafe fn assume_init_drop(&mut self) { // SAFETY: the caller must guarantee that `self` is initialized and From f7b6ace029d5b5832afa168bfe7d742537e038e4 Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Thu, 10 Sep 2020 11:35:25 +0200 Subject: [PATCH 30/54] Use IOV_MAX and UIO_MAXIOV constants in limit vectored I/O Also updates the libc dependency to 0.2.77 (from 0.2.74) as the constants were only recently added. --- library/std/Cargo.toml | 2 +- library/std/src/sys/unix/fd.rs | 42 +++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index bfd05db6b1bbe..01babeffd98f0 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,7 +16,7 @@ cfg-if = { version = "0.1.8", features = ['rustc-dep-of-std'] } panic_unwind = { path = "../panic_unwind", optional = true } panic_abort = { path = "../panic_abort" } core = { path = "../core" } -libc = { version = "0.2.74", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.77", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.35" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index b2f15eda9d7b0..16adbe6d88c4f 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -7,7 +7,6 @@ use crate::cmp; use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; use crate::mem; #[cfg(not(any(target_os = "redox", target_env = "newlib")))] -use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::cvt; use crate::sys_common::AsInner; @@ -31,24 +30,35 @@ const READ_LIMIT: usize = c_int::MAX as usize - 1; #[cfg(not(target_os = "macos"))] const READ_LIMIT: usize = libc::ssize_t::MAX as usize; -#[cfg(not(any(target_os = "redox", target_env = "newlib")))] -fn max_iov() -> usize { - static LIM: AtomicUsize = AtomicUsize::new(0); - - let mut lim = LIM.load(Ordering::Relaxed); - if lim == 0 { - let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) }; - - // 16 is the minimum value required by POSIX. - lim = if ret > 0 { ret as usize } else { 16 }; - LIM.store(lim, Ordering::Relaxed); - } +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +))] +const fn max_iov() -> usize { + libc::IOV_MAX as usize +} - lim +#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))] +const fn max_iov() -> usize { + libc::UIO_MAXIOV as usize } -#[cfg(any(target_os = "redox", target_env = "newlib"))] -fn max_iov() -> usize { +#[cfg(not(any( + target_os = "android", + target_os = "dragonfly", + target_os = "emscripten", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd", +)))] +const fn max_iov() -> usize { 16 // The minimum value required by POSIX. } From 7c3e1ffd7a945b4044e5e80d7d74ba944ff54d0f Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Thu, 10 Sep 2020 14:37:50 +0200 Subject: [PATCH 31/54] Update libc in Cargo.lock --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b687e714d4fa2..cfc1cede0797b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1610,9 +1610,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.74" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" dependencies = [ "rustc-std-workspace-core", ] From 283d4c4d148c8c53a8b197c4ff0a9f79a1690eb2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 10 Sep 2020 16:20:05 -0400 Subject: [PATCH 32/54] Ignore `|` and `+` tokens during proc-macro pretty-print check Fixes #76182 This is an alternative to PR #76188 These tokens are not preserved in the AST in certain cases (e.g. a leading `|` in a pattern or a trailing `+` in a trait bound). This PR ignores them entirely during the pretty-print/reparse check to avoid spuriously using the re-parsed tokenstream. --- compiler/rustc_parse/src/lib.rs | 6 ++ .../issue-76182-leading-vert-pat.rs | 16 +++++ .../issue-76182-leading-vert-pat.stdout | 62 +++++++++++++++++++ src/test/ui/proc-macro/trailing-plus.rs | 14 +++++ src/test/ui/proc-macro/trailing-plus.stdout | 57 +++++++++++++++++ 5 files changed, 155 insertions(+) create mode 100644 src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs create mode 100644 src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout create mode 100644 src/test/ui/proc-macro/trailing-plus.rs create mode 100644 src/test/ui/proc-macro/trailing-plus.stdout diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index e7fd74f551aaa..0f6a111ad7018 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -353,6 +353,12 @@ pub fn tokenstream_probably_equal_for_proc_macro( | token::CloseDelim(DelimToken::NoDelim) // The pretty printer collapses many semicolons into one. | token::Semi + // We don't preserve leading `|` tokens in patterns, so + // we ignore them entirely + | token::BinOp(token::BinOpToken::Or) + // We don't preserve trailing '+' tokens in trait bounds, + // so we ignore them entirely + | token::BinOp(token::BinOpToken::Plus) // The pretty printer can turn `$crate` into `::crate_name` | token::ModSep = token.kind { return false; diff --git a/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs new file mode 100644 index 0000000000000..7d31de1d22df2 --- /dev/null +++ b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.rs @@ -0,0 +1,16 @@ +// check-pass +// aux-build:test-macros.rs +// compile-flags: -Z span-debug +// +// Regression test for issue #76182 +// Tests that we properly handle patterns with a leading vert + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +extern crate test_macros; + +#[test_macros::print_attr] +fn main() { + match () { | () => () } +} diff --git a/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout new file mode 100644 index 0000000000000..5493f9c7b606b --- /dev/null +++ b/src/test/ui/proc-macro/issue-76182-leading-vert-pat.stdout @@ -0,0 +1,62 @@ +PRINT-ATTR INPUT (DISPLAY): fn main() { match() { | () => () } } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "fn", + span: $DIR/issue-76182-leading-vert-pat.rs:14:1: 14:3 (#0), + }, + Ident { + ident: "main", + span: $DIR/issue-76182-leading-vert-pat.rs:14:4: 14:8 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:14:8: 14:10 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Ident { + ident: "match", + span: $DIR/issue-76182-leading-vert-pat.rs:15:5: 15:10 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:11: 15:13 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '|', + spacing: Alone, + span: $DIR/issue-76182-leading-vert-pat.rs:15:16: 15:17 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:18: 15:20 (#0), + }, + Punct { + ch: '=', + spacing: Joint, + span: $DIR/issue-76182-leading-vert-pat.rs:15:21: 15:23 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/issue-76182-leading-vert-pat.rs:15:21: 15:23 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/issue-76182-leading-vert-pat.rs:15:24: 15:26 (#0), + }, + ], + span: $DIR/issue-76182-leading-vert-pat.rs:15:14: 15:28 (#0), + }, + ], + span: $DIR/issue-76182-leading-vert-pat.rs:14:11: 16:2 (#0), + }, +] diff --git a/src/test/ui/proc-macro/trailing-plus.rs b/src/test/ui/proc-macro/trailing-plus.rs new file mode 100644 index 0000000000000..4f61de47d8545 --- /dev/null +++ b/src/test/ui/proc-macro/trailing-plus.rs @@ -0,0 +1,14 @@ +// check-pass +// aux-build:test-macros.rs +// compile-flags: -Z span-debug + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +extern crate test_macros; + +#[test_macros::print_attr] +fn foo() where T: Copy + { +} + +fn main() {} diff --git a/src/test/ui/proc-macro/trailing-plus.stdout b/src/test/ui/proc-macro/trailing-plus.stdout new file mode 100644 index 0000000000000..d60f400af2bb9 --- /dev/null +++ b/src/test/ui/proc-macro/trailing-plus.stdout @@ -0,0 +1,57 @@ +PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + { } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "fn", + span: $DIR/trailing-plus.rs:11:1: 11:3 (#0), + }, + Ident { + ident: "foo", + span: $DIR/trailing-plus.rs:11:4: 11:7 (#0), + }, + Punct { + ch: '<', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:7: 11:8 (#0), + }, + Ident { + ident: "T", + span: $DIR/trailing-plus.rs:11:8: 11:9 (#0), + }, + Punct { + ch: '>', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:9: 11:10 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/trailing-plus.rs:11:10: 11:12 (#0), + }, + Ident { + ident: "where", + span: $DIR/trailing-plus.rs:11:13: 11:18 (#0), + }, + Ident { + ident: "T", + span: $DIR/trailing-plus.rs:11:19: 11:20 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:20: 11:21 (#0), + }, + Ident { + ident: "Copy", + span: $DIR/trailing-plus.rs:11:22: 11:26 (#0), + }, + Punct { + ch: '+', + spacing: Alone, + span: $DIR/trailing-plus.rs:11:27: 11:28 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [], + span: $DIR/trailing-plus.rs:11:29: 12:2 (#0), + }, +] From 15aa6f31b974d41ecc0312faf8d50cde19a74cca Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Thu, 10 Sep 2020 14:58:45 -0700 Subject: [PATCH 33/54] add debug-logging to config.toml --- compiler/rustc/Cargo.toml | 1 + compiler/rustc_driver/Cargo.toml | 3 ++- config.toml.example | 6 ++++++ src/bootstrap/config.rs | 6 ++++++ src/bootstrap/lib.rs | 10 ++++++++++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index cf011e63e02fd..b740793a3e554 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -19,3 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] jemalloc = ['jemalloc-sys'] llvm = ['rustc_driver/llvm'] +release_max_level_info = ['rustc_driver/release_max_level_info'] diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 0d9dcb262b269..6f0d7bc4d58e8 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["dylib"] [dependencies] libc = "0.2" -tracing = { version = "0.1.18", features = ["release_max_level_info"] } +tracing = { version = "0.1.18" } tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } @@ -38,3 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] +release_max_level_info = ['tracing/release_max_level_info'] diff --git a/config.toml.example b/config.toml.example index 9abb8add785a9..a162c543a50c3 100644 --- a/config.toml.example +++ b/config.toml.example @@ -329,6 +329,12 @@ # Defaults to rust.debug-assertions value #debug-assertions-std = false +# Whether or not to leave debug! and trace! calls in the rust binary. +# Overrides the `debug-assertions` option, if defined. +# +# Defaults to rust.debug-assertions value +#debug-logging = true + # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info # `1` - line tables only - sufficient to generate backtraces that include line diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index ad2f48778674f..49a0855b97884 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -100,6 +100,7 @@ pub struct Config { pub rust_codegen_units_std: Option, pub rust_debug_assertions: bool, pub rust_debug_assertions_std: bool, + pub rust_debug_logging: bool, pub rust_debuginfo_level_rustc: u32, pub rust_debuginfo_level_std: u32, pub rust_debuginfo_level_tools: u32, @@ -381,6 +382,7 @@ struct Rust { codegen_units_std: Option, debug_assertions: Option, debug_assertions_std: Option, + debug_logging: Option, debuginfo_level: Option, debuginfo_level_rustc: Option, debuginfo_level_std: Option, @@ -591,6 +593,7 @@ impl Config { let mut debug = None; let mut debug_assertions = None; let mut debug_assertions_std = None; + let mut debug_logging = None; let mut debuginfo_level = None; let mut debuginfo_level_rustc = None; let mut debuginfo_level_std = None; @@ -634,6 +637,7 @@ impl Config { debug = rust.debug; debug_assertions = rust.debug_assertions; debug_assertions_std = rust.debug_assertions_std; + debug_logging = rust.debug_logging; debuginfo_level = rust.debuginfo_level; debuginfo_level_rustc = rust.debuginfo_level_rustc; debuginfo_level_std = rust.debuginfo_level_std; @@ -737,6 +741,8 @@ impl Config { config.rust_debug_assertions_std = debug_assertions_std.unwrap_or(config.rust_debug_assertions); + config.rust_debug_logging = debug_logging.unwrap_or(config.rust_debug_assertions); + let with_defaults = |debuginfo_level_specific: Option| { debuginfo_level_specific.or(debuginfo_level).unwrap_or(if debug == Some(true) { 1 diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index c1dec8ed18131..3c2663f6d9f57 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -541,6 +541,16 @@ impl Build { if self.config.llvm_enabled() { features.push_str(" llvm"); } + + // If debug logging is on, then we want the default for tracing: + // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26 + // which is everything (including debug/trace/etc.) + // if its unset, if debug_assertions is on, then debug_logging will also be on + // as well as tracing *ignoring* this feature when debug_assertions is on + if !self.config.rust_debug_logging { + features.push_str(" release_max_level_info"); + } + features } From fb8d0704bf0d3c3ea975ba8102729db63807df09 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Fri, 11 Sep 2020 03:01:25 +0000 Subject: [PATCH 34/54] Add ui test for 74672 and 76571 These tests will fall without the next commit. --- src/test/rustdoc/auxiliary/real_gimli.rs | 13 +++++++++++++ src/test/rustdoc/auxiliary/realcore.rs | 15 +++++++++++++++ src/test/rustdoc/issue-75588.rs | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/test/rustdoc/auxiliary/real_gimli.rs create mode 100644 src/test/rustdoc/auxiliary/realcore.rs create mode 100644 src/test/rustdoc/issue-75588.rs diff --git a/src/test/rustdoc/auxiliary/real_gimli.rs b/src/test/rustdoc/auxiliary/real_gimli.rs new file mode 100644 index 0000000000000..80d5c4ba8bb09 --- /dev/null +++ b/src/test/rustdoc/auxiliary/real_gimli.rs @@ -0,0 +1,13 @@ +// aux-build:realcore.rs + +#![crate_name = "real_gimli"] +#![feature(staged_api, extremely_unstable)] +#![unstable(feature = "rustc_private", issue = "none")] + +extern crate realcore; + +#[unstable(feature = "rustc_private", issue = "none")] +pub struct EndianSlice; + +#[unstable(feature = "rustc_private", issue = "none")] +impl realcore::Deref for EndianSlice {} diff --git a/src/test/rustdoc/auxiliary/realcore.rs b/src/test/rustdoc/auxiliary/realcore.rs new file mode 100644 index 0000000000000..e0a906df002da --- /dev/null +++ b/src/test/rustdoc/auxiliary/realcore.rs @@ -0,0 +1,15 @@ +#![crate_name = "realcore"] +#![feature(staged_api)] +#![unstable(feature = "extremely_unstable", issue = "none")] + +#[unstable(feature = "extremely_unstable_foo", issue = "none")] +pub struct Foo {} + +#[unstable(feature = "extremely_unstable_foo", issue = "none")] +pub trait Join {} + +#[unstable(feature = "extremely_unstable_foo", issue = "none")] +impl Join for Foo {} + +#[stable(feature = "faked_deref", since = "1.47.0")] +pub trait Deref {} diff --git a/src/test/rustdoc/issue-75588.rs b/src/test/rustdoc/issue-75588.rs new file mode 100644 index 0000000000000..835ed02ac00db --- /dev/null +++ b/src/test/rustdoc/issue-75588.rs @@ -0,0 +1,18 @@ +// ignore-tidy-linelength +// aux-build:realcore.rs +// aux-build:real_gimli.rs + +// Ensure unstably exported traits have their Implementors sections. + +#![crate_name = "foo"] +#![feature(extremely_unstable_foo)] + +extern crate realcore; +extern crate real_gimli; + +// issue #74672 +// @!has foo/trait.Deref.html '//*[@id="impl-Deref-for-EndianSlice"]//code' 'impl Deref for EndianSlice' +pub use realcore::Deref; + +// @has foo/trait.Join.html '//*[@id="impl-Join-for-Foo"]//code' 'impl Join for Foo' +pub use realcore::Join; From c3d048abfd12645ef07a0d5199b63bf7effc1d85 Mon Sep 17 00:00:00 2001 From: Lzu Tao Date: Thu, 10 Sep 2020 14:05:33 +0000 Subject: [PATCH 35/54] Ignore rustc_private items from std docs Apply suggestions from code review Co-authored-by: Joshua Nelson --- src/librustdoc/clean/inline.rs | 19 ++++++++++--------- src/tools/linkchecker/main.rs | 10 ++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 50cb987cf0870..f8987c6beca33 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -337,18 +337,13 @@ pub fn build_impl( // reachable in rustdoc generated documentation if !did.is_local() { if let Some(traitref) = associated_trait { - if !cx.renderinfo.borrow().access_levels.is_public(traitref.def_id) { + let did = traitref.def_id; + if !cx.renderinfo.borrow().access_levels.is_public(did) { return; } - } - // Skip foreign unstable traits from lists of trait implementations and - // such. This helps prevent dependencies of the standard library, for - // example, from getting documented as "traits `u32` implements" which - // isn't really too helpful. - if let Some(trait_did) = associated_trait { - if let Some(stab) = cx.tcx.lookup_stability(trait_did.def_id) { - if stab.level.is_unstable() { + if let Some(stab) = tcx.lookup_stability(did) { + if stab.level.is_unstable() && stab.feature == sym::rustc_private { return; } } @@ -372,6 +367,12 @@ pub fn build_impl( if !cx.renderinfo.borrow().access_levels.is_public(did) { return; } + + if let Some(stab) = tcx.lookup_stability(did) { + if stab.level.is_unstable() && stab.feature == sym::rustc_private { + return; + } + } } } diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 7ec12116c2ca1..de410922571b6 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -142,6 +142,16 @@ fn is_exception(file: &Path, link: &str) -> bool { if let Some(entry) = LINKCHECK_EXCEPTIONS.iter().find(|&(f, _)| file.ends_with(f)) { entry.1.contains(&link) } else { + // FIXME(#63351): Concat trait in alloc/slice reexported in primitive page + // + // NOTE: This cannot be added to `LINKCHECK_EXCEPTIONS` because the resolved path + // calculated in `check` function is outside `build//doc` dir. + // So the `strip_prefix` method just returns the old absolute broken path. + if file.ends_with("std/primitive.slice.html") { + if link.ends_with("std/primitive.slice.html") { + return true; + } + } false } } From 439b7661616ec26092176baf905a0ceeb4a3c620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Deharbe?= Date: Fri, 11 Sep 2020 11:11:11 +0200 Subject: [PATCH 36/54] replacing sub's that can wrap by saturating_sub's --- compiler/rustc_errors/src/emitter.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 5a654e83aed8e..4555168af0ab5 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -959,15 +959,15 @@ impl EmitterWriter { '_', line_offset + pos, width_offset + depth, - code_offset + annotation.start_col - left, + (code_offset + annotation.start_col).saturating_sub(left), style, ); } _ if self.teach => { buffer.set_style_range( line_offset, - code_offset + annotation.start_col - left, - code_offset + annotation.end_col - left, + (code_offset + annotation.start_col).saturating_sub(left), + (code_offset + annotation.end_col).saturating_sub(left), style, annotation.is_primary, ); From 1854f8b3d8bb5f35554f3bdf61f63853ae720742 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 9 Sep 2020 22:16:13 +0200 Subject: [PATCH 37/54] Warn for #[unstable] on trait impls when it has no effect. --- compiler/rustc_passes/src/stability.rs | 66 +++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 91edc7d9db775..d3141c34d0e11 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{Generics, HirId, Item, StructField, Variant}; +use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; @@ -538,7 +538,31 @@ impl Visitor<'tcx> for Checker<'tcx> { // For implementations of traits, check the stability of each item // individually as it's possible to have a stable trait with unstable // items. - hir::ItemKind::Impl { of_trait: Some(ref t), items, .. } => { + hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => { + // If this impl block has an #[unstable] attribute, give an + // error if all involved types and traits are stable, because + // it will have no effect. + // See: https://github.com/rust-lang/rust/issues/55436 + if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) = + attr::find_stability(&self.tcx.sess, &item.attrs, item.span) + { + let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; + c.visit_ty(self_ty); + c.visit_trait_ref(t); + if c.fully_stable { + let span = item + .attrs + .iter() + .find(|a| a.has_name(sym::unstable)) + .map_or(item.span, |a| a.span); + self.tcx.sess.span_warn( + span, + "An `#[unstable]` annotation here has no effect. \ + See issue #55436 for more information.", + ); + } + } + if let Res::Def(DefKind::Trait, trait_did) = t.path.res { for impl_item_ref in items { let impl_item = self.tcx.hir().impl_item(impl_item_ref.id); @@ -598,6 +622,44 @@ impl Visitor<'tcx> for Checker<'tcx> { } } +struct CheckTraitImplStable<'tcx> { + tcx: TyCtxt<'tcx>, + fully_stable: bool, +} + +impl Visitor<'tcx> for CheckTraitImplStable<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _id: hir::HirId) { + if let Some(def_id) = path.res.opt_def_id() { + if let Some(stab) = self.tcx.lookup_stability(def_id) { + self.fully_stable &= stab.level.is_stable(); + } + } + intravisit::walk_path(self, path) + } + + fn visit_trait_ref(&mut self, t: &'tcx TraitRef<'tcx>) { + if let Res::Def(DefKind::Trait, trait_did) = t.path.res { + if let Some(stab) = self.tcx.lookup_stability(trait_did) { + self.fully_stable &= stab.level.is_stable(); + } + } + intravisit::walk_trait_ref(self, t) + } + + fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + if let TyKind::Never = t.kind { + self.fully_stable = false; + } + intravisit::walk_ty(self, t) + } +} + /// Given the list of enabled features that were not language features (i.e., that /// were expected to be library features), and the list of features used from /// libraries, identify activated features that don't exist and error about them. From e5c645f40e98bfcda81a10929aaaf18dd0a00414 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 09:36:17 +0200 Subject: [PATCH 38/54] Turn useless #[unstable] attributes into errors. --- compiler/rustc_passes/src/stability.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d3141c34d0e11..dc82c80d205b6 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -555,7 +555,7 @@ impl Visitor<'tcx> for Checker<'tcx> { .iter() .find(|a| a.has_name(sym::unstable)) .map_or(item.span, |a| a.span); - self.tcx.sess.span_warn( + self.tcx.sess.span_err( span, "An `#[unstable]` annotation here has no effect. \ See issue #55436 for more information.", From f6fbf669ab0c47034afe53103da7706a593480b9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 09:40:40 +0200 Subject: [PATCH 39/54] Mark RefUnwindSafe impls for stable atomic types as stable. These impls were effectively stable. #[unstable] had no effect here, since both RefUnwindSafe and these types were already stable. These effectively became stable as soon as the types became stable, which was in 1.34.0. --- library/std/src/panic.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 87493945db6dc..18d9c2f11b544 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -232,16 +232,16 @@ impl RefUnwindSafe for RwLock {} #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] impl RefUnwindSafe for atomic::AtomicIsize {} #[cfg(target_has_atomic_load_store = "8")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicI8 {} #[cfg(target_has_atomic_load_store = "16")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicI16 {} #[cfg(target_has_atomic_load_store = "32")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicI32 {} #[cfg(target_has_atomic_load_store = "64")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicI64 {} #[cfg(target_has_atomic_load_store = "128")] #[unstable(feature = "integer_atomics", issue = "32976")] @@ -251,16 +251,16 @@ impl RefUnwindSafe for atomic::AtomicI128 {} #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] impl RefUnwindSafe for atomic::AtomicUsize {} #[cfg(target_has_atomic_load_store = "8")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicU8 {} #[cfg(target_has_atomic_load_store = "16")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicU16 {} #[cfg(target_has_atomic_load_store = "32")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicU32 {} #[cfg(target_has_atomic_load_store = "64")] -#[unstable(feature = "integer_atomics", issue = "32976")] +#[stable(feature = "integer_atomics_stable", since = "1.34.0")] impl RefUnwindSafe for atomic::AtomicU64 {} #[cfg(target_has_atomic_load_store = "128")] #[unstable(feature = "integer_atomics", issue = "32976")] From cf8e5d1bc969987aba074bd7f4b01565badca434 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 09:40:40 +0200 Subject: [PATCH 40/54] Mark Error impl for LayoutErr as stable. This impl was effectively stable. #[unstable] had no effect here, since both Error and LayoutErr were already stable. This effectively became stable as soon as LayoutErr became stable, which was in 1.28.0. --- library/std/src/error.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 8da033439768e..ee25311d3b7ee 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -389,11 +389,7 @@ impl Error for ! {} )] impl Error for AllocErr {} -#[unstable( - feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked.", - issue = "32838" -)] +#[stable(feature = "alloc_layout", since = "1.28.0")] impl Error for LayoutErr {} #[stable(feature = "rust1", since = "1.0.0")] From 89fb34fea79fd803c9ea8a333a18ccb32e0c991d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 20:43:13 +0200 Subject: [PATCH 41/54] Turn unstable trait impl error into a lint, so it can be disabled. --- compiler/rustc_passes/src/stability.rs | 49 ++++++++++++---------- compiler/rustc_session/src/lint/builtin.rs | 9 +++- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index dc82c80d205b6..4eb983365ffb5 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -16,6 +16,7 @@ use rustc_middle::middle::stability::{DeprecationEntry, Index}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint; +use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; @@ -539,27 +540,33 @@ impl Visitor<'tcx> for Checker<'tcx> { // individually as it's possible to have a stable trait with unstable // items. hir::ItemKind::Impl { of_trait: Some(ref t), self_ty, items, .. } => { - // If this impl block has an #[unstable] attribute, give an - // error if all involved types and traits are stable, because - // it will have no effect. - // See: https://github.com/rust-lang/rust/issues/55436 - if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) = - attr::find_stability(&self.tcx.sess, &item.attrs, item.span) - { - let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; - c.visit_ty(self_ty); - c.visit_trait_ref(t); - if c.fully_stable { - let span = item - .attrs - .iter() - .find(|a| a.has_name(sym::unstable)) - .map_or(item.span, |a| a.span); - self.tcx.sess.span_err( - span, - "An `#[unstable]` annotation here has no effect. \ - See issue #55436 for more information.", - ); + if self.tcx.features().staged_api { + // If this impl block has an #[unstable] attribute, give an + // error if all involved types and traits are stable, because + // it will have no effect. + // See: https://github.com/rust-lang/rust/issues/55436 + if let (Some(Stability { level: attr::Unstable { .. }, .. }), _) = + attr::find_stability(&self.tcx.sess, &item.attrs, item.span) + { + let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; + c.visit_ty(self_ty); + c.visit_trait_ref(t); + if c.fully_stable { + let span = item + .attrs + .iter() + .find(|a| a.has_name(sym::unstable)) + .map_or(item.span, |a| a.span); + self.tcx.struct_span_lint_hir( + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + item.hir_id, + span, + |lint| lint.build( + "An `#[unstable]` annotation here has no effect. \ + See issue #55436 for more information.", + ).emit() + ); + } } } diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_session/src/lint/builtin.rs index a9deaaae0daeb..0fd6cc1038284 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_session/src/lint/builtin.rs @@ -5,7 +5,7 @@ //! lints are all available in `rustc_lint::builtin`. use crate::lint::FutureIncompatibleInfo; -use crate::{declare_lint, declare_lint_pass}; +use crate::{declare_lint, declare_lint_pass, declare_tool_lint}; use rustc_span::edition::Edition; use rustc_span::symbol::sym; @@ -555,6 +555,12 @@ declare_lint! { }; } +declare_tool_lint! { + pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, + Deny, + "detects `#[unstable]` on stable trait implementations for stable types" +} + declare_lint_pass! { /// Does nothing as a lint pass, but registers some `Lint`s /// that are used by other parts of the compiler. @@ -630,6 +636,7 @@ declare_lint_pass! { INCOMPLETE_INCLUDE, CENUM_IMPL_DROP_CAST, CONST_EVALUATABLE_UNCHECKED, + INEFFECTIVE_UNSTABLE_TRAIT_IMPL, ] } From 471fb622aaa234c0a69d59ee24b76092244a1c6c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 20:44:42 +0200 Subject: [PATCH 42/54] Allow unstable From impl for [Raw]Waker. --- library/alloc/src/task.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 5edc579605669..fcab3fd0badce 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -33,6 +33,7 @@ pub trait Wake { } } +#[allow(rustc::ineffective_unstable_trait_impl)] #[unstable(feature = "wake_trait", issue = "69912")] impl From> for Waker { fn from(waker: Arc) -> Waker { @@ -42,6 +43,7 @@ impl From> for Waker { } } +#[allow(rustc::ineffective_unstable_trait_impl)] #[unstable(feature = "wake_trait", issue = "69912")] impl From> for RawWaker { fn from(waker: Arc) -> RawWaker { From 1c1bfba84a92e0fe8bf1eb85e8c3e3d10caf07cb Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 10 Sep 2020 20:55:04 +0200 Subject: [PATCH 43/54] Add test for unstable trait impl lint. --- .../stability-attribute-trait-impl.rs | 28 +++++++++++++++++++ .../stability-attribute-trait-impl.stderr | 10 +++++++ 2 files changed, 38 insertions(+) create mode 100644 src/test/ui/stability-attribute/stability-attribute-trait-impl.rs create mode 100644 src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs new file mode 100644 index 0000000000000..9be772a7be323 --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs @@ -0,0 +1,28 @@ +#![feature(staged_api)] + +#[stable(feature = "x", since = "1")] +struct StableType; + +#[unstable(feature = "x", issue = "none")] +struct UnstableType; + +#[stable(feature = "x", since = "1")] +trait StableTrait {} + +#[unstable(feature = "x", issue = "none")] +trait UnstableTrait {} + +#[unstable(feature = "x", issue = "none")] +impl UnstableTrait for UnstableType {} + +#[unstable(feature = "x", issue = "none")] +impl StableTrait for UnstableType {} + +#[unstable(feature = "x", issue = "none")] +impl UnstableTrait for StableType {} + +#[unstable(feature = "x", issue = "none")] +//~^ ERROR An `#[unstable]` annotation here has no effect. +impl StableTrait for StableType {} + +fn main() {} diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr new file mode 100644 index 0000000000000..808636238fcc2 --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr @@ -0,0 +1,10 @@ +error: An `#[unstable]` annotation here has no effect. See issue #55436 for more information. + --> $DIR/stability-attribute-trait-impl.rs:24:1 + | +LL | #[unstable(feature = "x", issue = "none")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[deny(rustc::ineffective_unstable_trait_impl)]` on by default + +error: aborting due to previous error + From f9059a41b48813845e1ffa2d1bdcbc455ffcd10c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Deharbe?= Date: Fri, 11 Sep 2020 13:58:03 +0200 Subject: [PATCH 44/54] add non-regression test for issue #76597 --- src/test/ui/issue-76597.rs | 7 +++++++ src/test/ui/issue-76597.stderr | 14 ++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/test/ui/issue-76597.rs create mode 100644 src/test/ui/issue-76597.stderr diff --git a/src/test/ui/issue-76597.rs b/src/test/ui/issue-76597.rs new file mode 100644 index 0000000000000..879e6b49e9b77 --- /dev/null +++ b/src/test/ui/issue-76597.rs @@ -0,0 +1,7 @@ +fn f( + x: u8 + y: u8, +) {} +//~^^ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + +fn main() {} diff --git a/src/test/ui/issue-76597.stderr b/src/test/ui/issue-76597.stderr new file mode 100644 index 0000000000000..163ce61cb18e3 --- /dev/null +++ b/src/test/ui/issue-76597.stderr @@ -0,0 +1,14 @@ +error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + --> $DIR/issue-76597.rs:3:38 + | +LL | ... x: u8 + | - expected one of 7 possible tokens + | ____________________________| + | | +LL | | ... y: u8, +| | | ^ unexpected token +| | | + | help: missing `,` + +error: aborting due to previous error + From 62068a59eea44ff78c4293d5fcc676279b1deab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Deharbe?= Date: Fri, 11 Sep 2020 17:31:52 +0200 Subject: [PATCH 45/54] repairing broken error message and rustfix application for the new test case --- compiler/rustc_parse/src/parser/mod.rs | 6 +++++- src/test/ui/issue-76597.fixed | 11 +++++++++++ src/test/ui/issue-76597.rs | 4 ++++ src/test/ui/issue-76597.stderr | 17 ++++++++--------- 4 files changed, 28 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/issue-76597.fixed diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 5eefae3af60e9..7340c5744808c 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -694,9 +694,13 @@ impl<'a> Parser<'a> { Ok(t) => { // Parsed successfully, therefore most probably the code only // misses a separator. + let mut exp_span = self.sess.source_map().next_point(sp); + if self.sess.source_map().is_multiline(exp_span) { + exp_span = sp; + } expect_err .span_suggestion_short( - self.sess.source_map().next_point(sp), + exp_span, &format!("missing `{}`", token_str), token_str, Applicability::MaybeIncorrect, diff --git a/src/test/ui/issue-76597.fixed b/src/test/ui/issue-76597.fixed new file mode 100644 index 0000000000000..2d7a30b8361ad --- /dev/null +++ b/src/test/ui/issue-76597.fixed @@ -0,0 +1,11 @@ +// run-rustfix + +#![allow(dead_code)] +#![allow(unused_variables)] +fn f( + x: u8, + y: u8, +) {} +//~^^ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + +fn main() {} diff --git a/src/test/ui/issue-76597.rs b/src/test/ui/issue-76597.rs index 879e6b49e9b77..521b9c64b1c57 100644 --- a/src/test/ui/issue-76597.rs +++ b/src/test/ui/issue-76597.rs @@ -1,3 +1,7 @@ +// run-rustfix + +#![allow(dead_code)] +#![allow(unused_variables)] fn f( x: u8 y: u8, diff --git a/src/test/ui/issue-76597.stderr b/src/test/ui/issue-76597.stderr index 163ce61cb18e3..50b23329f0ceb 100644 --- a/src/test/ui/issue-76597.stderr +++ b/src/test/ui/issue-76597.stderr @@ -1,14 +1,13 @@ error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` - --> $DIR/issue-76597.rs:3:38 + --> $DIR/issue-76597.rs:7:38 | -LL | ... x: u8 - | - expected one of 7 possible tokens - | ____________________________| - | | -LL | | ... y: u8, -| | | ^ unexpected token -| | | - | help: missing `,` +LL | ... x: u8 + | - + | | + | expected one of 7 possible tokens + | help: missing `,` +LL | ... y: u8, + | ^ unexpected token error: aborting due to previous error From 56f5c7f95f3f7772cb3263d48cd2d8a136706767 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Fri, 11 Sep 2020 09:01:31 -0700 Subject: [PATCH 46/54] comments + add max_level_info so false works with debug_assertions on --- compiler/rustc_driver/Cargo.toml | 2 +- config.toml.example | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 6f0d7bc4d58e8..1e371f8254ddd 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -38,4 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] -release_max_level_info = ['tracing/release_max_level_info'] +release_max_level_info = ['tracing/release_max_level_info', 'tracing/max_level_info'] diff --git a/config.toml.example b/config.toml.example index a162c543a50c3..3bb6d3cad55e7 100644 --- a/config.toml.example +++ b/config.toml.example @@ -321,19 +321,19 @@ # binary, otherwise they are omitted. # # Defaults to rust.debug value -#debug-assertions = false +#debug-assertions = debug # Whether or not debug assertions are enabled for the standard library. # Overrides the `debug-assertions` option, if defined. # # Defaults to rust.debug-assertions value -#debug-assertions-std = false +#debug-assertions-std = debug-assertions # Whether or not to leave debug! and trace! calls in the rust binary. # Overrides the `debug-assertions` option, if defined. # # Defaults to rust.debug-assertions value -#debug-logging = true +#debug-logging = debug-assertions # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info From 0be66d7f30ba31638e14c00bd2303dd0327964f4 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Fri, 11 Sep 2020 09:37:51 -0700 Subject: [PATCH 47/54] just max_level_info --- compiler/rustc/Cargo.toml | 2 +- compiler/rustc_driver/Cargo.toml | 2 +- src/bootstrap/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index b740793a3e554..6e6c0c71a1f3b 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -19,4 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms'] [features] jemalloc = ['jemalloc-sys'] llvm = ['rustc_driver/llvm'] -release_max_level_info = ['rustc_driver/release_max_level_info'] +max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 1e371f8254ddd..adfce1008e1ed 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -38,4 +38,4 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] [features] llvm = ['rustc_interface/llvm'] -release_max_level_info = ['tracing/release_max_level_info', 'tracing/max_level_info'] +max_level_info = ['tracing/max_level_info'] diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 3c2663f6d9f57..87a893cd83664 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -548,7 +548,7 @@ impl Build { // if its unset, if debug_assertions is on, then debug_logging will also be on // as well as tracing *ignoring* this feature when debug_assertions is on if !self.config.rust_debug_logging { - features.push_str(" release_max_level_info"); + features.push_str(" max_level_info"); } features From c394624471c66c42bd9641ab30d2a2ccea894dbc Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Fri, 11 Sep 2020 19:12:06 +0200 Subject: [PATCH 48/54] Ignore unnecessary unsafe warnings This is a work-around for a libc issue: https://github.com/rust-lang/libc/issues/1888. --- library/std/src/sys/unix/fd.rs | 1 - .../std/src/sys/unix/process/process_unix.rs | 22 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 16adbe6d88c4f..2224a055d6d87 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -6,7 +6,6 @@ mod tests; use crate::cmp; use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; use crate::mem; -#[cfg(not(any(target_os = "redox", target_env = "newlib")))] use crate::sys::cvt; use crate::sys_common::AsInner; diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 0f349dfa30216..08efe154e4c3b 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -459,7 +459,15 @@ impl ExitStatus { } fn exited(&self) -> bool { - unsafe { libc::WIFEXITED(self.0) } + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] + unsafe { + libc::WIFEXITED(self.0) + } } pub fn success(&self) -> bool { @@ -467,10 +475,22 @@ impl ExitStatus { } pub fn code(&self) -> Option { + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None } } pub fn signal(&self) -> Option { + // On Linux-like OSes this function is safe, on others it is not. See + // libc issue: https://github.com/rust-lang/libc/issues/1888. + #[cfg_attr( + any(target_os = "linux", target_os = "android", target_os = "emscripten"), + allow(unused_unsafe) + )] if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None } } } From c213c68500f5fff51f26997d27a346c896232df7 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 13:29:55 -0400 Subject: [PATCH 49/54] box ResolutionFailures on the heap This decreases the size of the `Result`s being returned, improving performance in the common case. --- .../passes/collect_intra_doc_links.rs | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index d8abf411de7c2..7fea70253b301 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -48,10 +48,16 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } enum ErrorKind<'a> { - Resolve(ResolutionFailure<'a>), + Resolve(Box>), AnchorFailure(AnchorFailure), } +impl<'a> From> for ErrorKind<'a> { + fn from(err: ResolutionFailure<'a>) -> Self { + ErrorKind::Resolve(box err) + } +} + #[derive(Debug)] enum ResolutionFailure<'a> { /// This resolved, but with the wrong namespace. @@ -142,10 +148,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .expect("fold_item should ensure link is non-empty"); let variant_name = // we're not sure this is a variant at all, so use the full string - split.next().map(|f| Symbol::intern(f)).ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope{ + split.next().map(|f| Symbol::intern(f)).ok_or_else(|| ResolutionFailure::NotInScope { module_id, name: path_str.into(), - }))?; + })?; let path = split .next() .map(|f| { @@ -156,10 +162,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::Resolve(ResolutionFailure::NotInScope { + .ok_or_else(|| ResolutionFailure::NotInScope { module_id, name: variant_name.to_string().into(), - }))?; + })?; let ty_res = cx .enter_resolver(|resolver| { resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) @@ -167,10 +173,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { .map(|(_, res)| res) .unwrap_or(Res::Err); if let Res::Err = ty_res { - return Err(ErrorKind::Resolve(ResolutionFailure::NotInScope { - module_id, - name: path.into(), - })); + return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into()); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -184,7 +187,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { { // This is just to let `fold_item` know that this shouldn't be considered; // it's a bug for the error to make it to the user - return Err(ErrorKind::Resolve(ResolutionFailure::Dummy)); + return Err(ResolutionFailure::Dummy.into()); } match cx.tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { @@ -197,10 +200,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { )), )) } else { - Err(ErrorKind::Resolve(ResolutionFailure::NotAVariant( - ty_res, - variant_field_name, - ))) + Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into()) } } _ => unreachable!(), @@ -226,7 +226,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // Even with the shorter path, it didn't resolve, so say that. ResolutionFailure::NoAssocItem(ty_res, variant_name) }; - Err(ErrorKind::Resolve(kind)) + Err(kind.into()) } } } @@ -344,7 +344,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; if value != (ns == ValueNS) { - return Err(ErrorKind::Resolve(ResolutionFailure::WrongNamespace(res, ns))); + return Err(ResolutionFailure::WrongNamespace(res, ns).into()); } } else if let Some((path, prim)) = is_primitive(path_str, ns) { if extra_fragment.is_some() { @@ -373,16 +373,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved. .ok_or_else(|| { debug!("found no `::`, assumming {} was correctly not in scope", item_name); - ErrorKind::Resolve(ResolutionFailure::NotInScope { - module_id, - name: item_name.to_string().into(), - }) + ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() } })?; if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { - let impls = primitive_impl(cx, &path).ok_or_else(|| { - ErrorKind::Resolve(ResolutionFailure::NoPrimitiveImpl(prim, path_root.into())) - })?; + let impls = primitive_impl(cx, &path) + .ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?; for &impl_ in impls { let link = cx .tcx @@ -409,11 +405,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { item_name, ns.descr() ); - return Err(ErrorKind::Resolve(ResolutionFailure::NoPrimitiveAssocItem { + return Err(ResolutionFailure::NoPrimitiveAssocItem { res: prim, prim_name: path, assoc_item: item_name, - })); + } + .into()); } let ty_res = cx @@ -445,7 +442,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } ResolutionFailure::NotInScope { module_id, name: path_root.into() } }); - Err(ErrorKind::Resolve(kind)) + Err(kind.into()) }; } Ok(res) => res, @@ -548,9 +545,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } else { // We already know this isn't in ValueNS, so no need to check variant_field - return Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem( - ty_res, item_name, - ))); + return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()); } } Res::Def(DefKind::Trait, did) => cx @@ -585,12 +580,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { if ns == Namespace::ValueNS { self.variant_field(path_str, current_item, module_id, extra_fragment) } else { - Err(ErrorKind::Resolve(ResolutionFailure::NoAssocItem(ty_res, item_name))) + Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()) } }) } else { debug!("attempting to resolve item without parent module: {}", path_str); - Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)) + Err(ResolutionFailure::NoParentItem.into()) } } @@ -611,7 +606,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let check_full_res_inner = |this: &Self, result: Result>| { let res = match result { Ok(res) => Some(res), - Err(ErrorKind::Resolve(kind)) => kind.full_res(), + Err(ErrorKind::Resolve(box kind)) => kind.full_res(), Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => { Some(res) } @@ -626,7 +621,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; let check_full_res_macro = |this: &Self| { let result = this.macro_resolve(path_str, base_node); - check_full_res_inner(this, result.map_err(ErrorKind::Resolve)) + check_full_res_inner(this, result.map_err(ErrorKind::from)) }; match ns { Namespace::MacroNS => check_full_res_macro(self), @@ -970,7 +965,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { match self.resolve(path_str, ns, ¤t_item, base_node, &extra_fragment) { Ok(res) => res, - Err(ErrorKind::Resolve(mut kind)) => { + Err(ErrorKind::Resolve(box mut kind)) => { // We only looked in one namespace. Try to give a better error if possible. if kind.full_res().is_none() { let other_ns = if ns == ValueNS { TypeNS } else { ValueNS }; @@ -1028,7 +1023,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - Err(ErrorKind::Resolve(kind)) => Err(kind), + Err(ErrorKind::Resolve(box kind)) => Err(kind), }, value_ns: match self.resolve( path_str, @@ -1042,7 +1037,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { anchor_failure(cx, &item, &ori_link, &dox, link_range, msg); continue; } - Err(ErrorKind::Resolve(kind)) => Err(kind), + Err(ErrorKind::Resolve(box kind)) => Err(kind), } .and_then(|(res, fragment)| { // Constructors are picked up in the type namespace. @@ -1816,7 +1811,7 @@ fn handle_variant( let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { parent } else { - return Err(ErrorKind::Resolve(ResolutionFailure::NoParentItem)); + return Err(ResolutionFailure::NoParentItem.into()); }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); From 57250eff55db7c034b9d3468b45b0df2d6ee4bb3 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 13:42:56 -0400 Subject: [PATCH 50/54] Use `span_label` instead of `note` This puts the error message closer to the link, making it easier to see what went wrong. --- .../passes/collect_intra_doc_links.rs | 84 +++++++++++-------- .../intra-links-warning-crlf.stderr | 12 +-- .../rustdoc-ui/intra-links-warning.stderr | 47 ++++------- 3 files changed, 65 insertions(+), 78 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 7fea70253b301..0319096635211 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1516,16 +1516,15 @@ fn resolution_failure( collector.cx.tcx.item_name(res.def_id()).to_string() ) }; - let assoc_item_not_allowed = |res: Res, diag: &mut DiagnosticBuilder<'_>| { + let assoc_item_not_allowed = |res: Res| { let def_id = res.def_id(); let name = collector.cx.tcx.item_name(def_id); - let note = format!( + format!( "`{}` is {} {}, not a module or type, and cannot have associated items", name, res.article(), res.descr() - ); - diag.note(¬e); + ) }; // ignore duplicates let mut variants_seen = SmallVec::<[_; 3]>::new(); @@ -1559,12 +1558,18 @@ fn resolution_failure( continue; } variants_seen.push(variant); - match failure { + let note = match failure { ResolutionFailure::NotInScope { name, .. } => { if in_scope { continue; } - diag.note(&format!("no item named `{}` is in scope", name)); + // NOTE: uses an explicit `continue` so the `note:` will come before the `help:` + let note = format!("no item named `{}` is in scope", name); + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } // If the link has `::` in the path, assume it's meant to be an intra-doc link if !path_str.contains("::") { // Otherwise, the `[]` might be unrelated. @@ -1572,16 +1577,10 @@ fn resolution_failure( // don't show this for autolinks (`<>`), `()` style links, or reference links diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); } + continue; } ResolutionFailure::Dummy => continue, ResolutionFailure::WrongNamespace(res, expected_ns) => { - let note = format!( - "this link resolves to {}, which is not in the {} namespace", - item(res), - expected_ns.descr() - ); - diag.note(¬e); - if let Res::Def(kind, _) = res { let disambiguator = Disambiguator::Kind(kind); suggest_disambiguator( @@ -1593,24 +1592,26 @@ fn resolution_failure( &link_range, ) } + + format!( + "this link resolves to {}, which is not in the {} namespace", + item(res), + expected_ns.descr() + ) } ResolutionFailure::NoParentItem => { diag.level = rustc_errors::Level::Bug; - diag.note("all intra doc links should have a parent item"); - } - ResolutionFailure::NoPrimitiveImpl(res, _) => { - let note = format!( - "this link partially resolves to {}, which does not have any associated items", - item(res), - ); - diag.note(¬e); + "all intra doc links should have a parent item".to_owned() } + ResolutionFailure::NoPrimitiveImpl(res, _) => format!( + "this link partially resolves to {}, which does not have any associated items", + item(res), + ), ResolutionFailure::NoPrimitiveAssocItem { prim_name, assoc_item, .. } => { - let note = format!( + format!( "the builtin type `{}` does not have an associated item named `{}`", prim_name, assoc_item - ); - diag.note(¬e); + ) } ResolutionFailure::NoAssocItem(res, assoc_item) => { use DefKind::*; @@ -1645,32 +1646,41 @@ fn resolution_failure( | Use | LifetimeParam | Ctor(_, _) - | AnonConst => return assoc_item_not_allowed(res, diag), + | AnonConst => { + let note = assoc_item_not_allowed(res); + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); + } + return; + } Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam | Static => "associated item", Impl | GlobalAsm => unreachable!("not a path"), } }; - let note = format!( + format!( "the {} `{}` has no {} named `{}`", res.descr(), name, path_description, assoc_item - ); - diag.note(¬e); + ) } ResolutionFailure::CannotHaveAssociatedItems(res, _) => { - assoc_item_not_allowed(res, diag) - } - ResolutionFailure::NotAVariant(res, variant) => { - let note = format!( - "this link partially resolves to {}, but there is no variant named {}", - item(res), - variant - ); - diag.note(¬e); + assoc_item_not_allowed(res) } + ResolutionFailure::NotAVariant(res, variant) => format!( + "this link partially resolves to {}, but there is no variant named {}", + item(res), + variant + ), + }; + if let Some(span) = sp { + diag.span_label(span, ¬e); + } else { + diag.note(¬e); } } }, diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index 1da27b786180f..d64c7e14ba61d 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -2,37 +2,33 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] - | ^^^^^ + | ^^^^^ no item named `error` is in scope | = note: `#[warn(broken_intra_doc_links)]` on by default - = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error1` --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] - | ^^^^^^ + | ^^^^^^ no item named `error1` is in scope | - = note: no item named `error1` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error2` --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] - | ^^^^^^ + | ^^^^^^ no item named `error2` is in scope | - = note: no item named `error2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. - | ^^^^^ + | ^^^^^ no item named `error` is in scope | - = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: 4 warnings emitted diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 25c6975d3a509..0eaad25501a32 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -2,76 +2,62 @@ warning: unresolved link to `Foo::baz` --> $DIR/intra-links-warning.rs:3:23 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ + | ^^^^^^^^ the struct `Foo` has no field or associated item named `baz` | = note: `#[warn(broken_intra_doc_links)]` on by default - = note: the struct `Foo` has no field or associated item named `baz` warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ - | - = note: no item named `Bar` is in scope + | ^^^^^^^^ no item named `Bar` is in scope warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ - | - = note: no item named `Uniooon` is in scope + | ^^^^^^^^^^ no item named `Uniooon` is in scope warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ - | - = note: no item named `Qux` is in scope + | ^^^^^^ no item named `Qux` is in scope warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ - | - = note: no item named `Uniooon` is in scope + | ^^^^^^^^^^ no item named `Uniooon` is in scope warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ - | - = note: no item named `Qux` is in scope + | ^^^^^^ no item named `Qux` is in scope warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ + | ^^^^^ no item named `Qux:Y` is in scope | - = note: no item named `Qux:Y` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ + | ^^^^^ no item named `error` is in scope | - = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ + | ^^^^^ no item named `error` is in scope | - = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -119,45 +105,40 @@ warning: unresolved link to `error1` --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] - | ^^^^^^ + | ^^^^^^ no item named `error1` is in scope | - = note: no item named `error1` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ + | ^^^^^^ no item named `error2` is in scope | - = note: no item named `error2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ + | ^^^^ no item named `BarA` is in scope | - = note: no item named `BarA` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ + | ^^^^ no item named `BarB` is in scope | - = note: no item named `BarB` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ + | ^^^^ no item named `BarC` is in scope | - = note: no item named `BarC` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarD` From b2a5a7a8c21c9cc271f3ce44b4cffdaefaa7f6d1 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 13:49:45 -0400 Subject: [PATCH 51/54] Remove unnecessary clone --- src/librustdoc/passes/collect_intra_doc_links.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 0319096635211..3e3eebcb51827 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1056,12 +1056,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { }), }; - let mut candidates_iter = - candidates.iter().filter_map(|res| res.as_ref().ok()); - let len = candidates_iter.clone().count(); + let len = candidates.iter().filter(|res| res.is_ok()).count(); if len == 0 { - drop(candidates_iter); resolution_failure( self, &item, @@ -1076,12 +1073,10 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } if len == 1 { - candidates_iter.next().unwrap().clone() + candidates.into_iter().filter_map(|res| res.ok()).next().unwrap() } else if len == 2 && is_derive_trait_collision(&candidates) { - drop(candidates_iter); candidates.type_ns.unwrap() } else { - drop(candidates_iter); if is_derive_trait_collision(&candidates) { candidates.macro_ns = Err(ResolutionFailure::Dummy); } From 5ea3eaf237793c978cad9d48f7850619c7f49d69 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 11 Sep 2020 14:00:40 -0400 Subject: [PATCH 52/54] Name the current module 'not in scope' -> 'not in `module`' --- .../passes/collect_intra_doc_links.rs | 5 +- .../rustdoc-ui/assoc-item-not-in-scope.stderr | 3 +- .../deny-intra-link-resolution-failure.stderr | 3 +- .../rustdoc-ui/intra-doc-alias-ice.stderr | 3 +- src/test/rustdoc-ui/intra-link-errors.rs | 6 +- src/test/rustdoc-ui/intra-link-errors.stderr | 75 +++++++------------ .../intra-link-span-ice-55723.stderr | 3 +- .../intra-links-warning-crlf.stderr | 8 +- .../rustdoc-ui/intra-links-warning.stderr | 36 ++++----- src/test/rustdoc-ui/lint-group.stderr | 3 +- 10 files changed, 61 insertions(+), 84 deletions(-) diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 3e3eebcb51827..5780610c86210 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1554,12 +1554,13 @@ fn resolution_failure( } variants_seen.push(variant); let note = match failure { - ResolutionFailure::NotInScope { name, .. } => { + ResolutionFailure::NotInScope { module_id, name, .. } => { if in_scope { continue; } // NOTE: uses an explicit `continue` so the `note:` will come before the `help:` - let note = format!("no item named `{}` is in scope", name); + let module_name = collector.cx.tcx.item_name(module_id); + let note = format!("no item named `{}` in `{}`", name, module_name); if let Some(span) = sp { diag.span_label(span, ¬e); } else { diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr index 8acfcca2139e3..92d27179e8c3f 100644 --- a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr +++ b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr @@ -2,14 +2,13 @@ error: unresolved link to `S::fmt` --> $DIR/assoc-item-not-in-scope.rs:4:14 | LL | /// Link to [`S::fmt`] - | ^^^^^^^^ + | ^^^^^^^^ the struct `S` has no field or associated item named `fmt` | note: the lint level is defined here --> $DIR/assoc-item-not-in-scope.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: the struct `S` has no field or associated item named `fmt` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index 4ae53e83613e0..5020b97b2f201 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -2,14 +2,13 @@ error: unresolved link to `v2` --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] - | ^^ + | ^^ no item named `v2` in `deny_intra_link_resolution_failure` | note: the lint level is defined here --> $DIR/deny-intra-link-resolution-failure.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: no item named `v2` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index 5379a91883753..771fc2204f5f8 100644 --- a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr +++ b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr @@ -2,14 +2,13 @@ error: unresolved link to `TypeAlias::hoge` --> $DIR/intra-doc-alias-ice.rs:5:30 | LL | /// [broken cross-reference](TypeAlias::hoge) - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ the type alias `TypeAlias` has no associated item named `hoge` | note: the lint level is defined here --> $DIR/intra-doc-alias-ice.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: the type alias `TypeAlias` has no associated item named `hoge` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 477ad6464f887..26b629b1313da 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -6,15 +6,15 @@ /// [path::to::nonexistent::module] //~^ ERROR unresolved link -//~| NOTE no item named `path` is in scope +//~| NOTE no item named `path` in `intra_link_errors` /// [path::to::nonexistent::macro!] //~^ ERROR unresolved link -//~| NOTE no item named `path` is in scope +//~| NOTE no item named `path` in `intra_link_errors` /// [type@path::to::nonexistent::type] //~^ ERROR unresolved link -//~| NOTE no item named `path` is in scope +//~| NOTE no item named `path` in `intra_link_errors` /// [std::io::not::here] //~^ ERROR unresolved link diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index 3b1a09f913e9f..fbf3dcbbec29a 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -2,134 +2,115 @@ error: unresolved link to `path::to::nonexistent::module` --> $DIR/intra-link-errors.rs:7:6 | LL | /// [path::to::nonexistent::module] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` | note: the lint level is defined here --> $DIR/intra-link-errors.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: no item named `path` is in scope error: unresolved link to `path::to::nonexistent::macro` --> $DIR/intra-link-errors.rs:11:6 | LL | /// [path::to::nonexistent::macro!] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: no item named `path` is in scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` error: unresolved link to `path::to::nonexistent::type` --> $DIR/intra-link-errors.rs:15:6 | LL | /// [type@path::to::nonexistent::type] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: no item named `path` is in scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in `intra_link_errors` error: unresolved link to `std::io::not::here` --> $DIR/intra-link-errors.rs:19:6 | LL | /// [std::io::not::here] - | ^^^^^^^^^^^^^^^^^^ - | - = note: the module `io` has no inner item named `not` + | ^^^^^^^^^^^^^^^^^^ the module `io` has no inner item named `not` error: unresolved link to `std::io::Error::x` --> $DIR/intra-link-errors.rs:23:6 | LL | /// [std::io::Error::x] - | ^^^^^^^^^^^^^^^^^ - | - = note: the struct `Error` has no field or associated item named `x` + | ^^^^^^^^^^^^^^^^^ the struct `Error` has no field or associated item named `x` error: unresolved link to `std::io::ErrorKind::x` --> $DIR/intra-link-errors.rs:27:6 | LL | /// [std::io::ErrorKind::x] - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the enum `ErrorKind` has no variant or associated item named `x` + | ^^^^^^^^^^^^^^^^^^^^^ the enum `ErrorKind` has no variant or associated item named `x` error: unresolved link to `f::A` --> $DIR/intra-link-errors.rs:31:6 | LL | /// [f::A] - | ^^^^ - | - = note: `f` is a function, not a module or type, and cannot have associated items + | ^^^^ `f` is a function, not a module or type, and cannot have associated items error: unresolved link to `S::A` --> $DIR/intra-link-errors.rs:35:6 | LL | /// [S::A] - | ^^^^ - | - = note: the struct `S` has no field or associated item named `A` + | ^^^^ the struct `S` has no field or associated item named `A` error: unresolved link to `S::fmt` --> $DIR/intra-link-errors.rs:39:6 | LL | /// [S::fmt] - | ^^^^^^ - | - = note: the struct `S` has no field or associated item named `fmt` + | ^^^^^^ the struct `S` has no field or associated item named `fmt` error: unresolved link to `E::D` --> $DIR/intra-link-errors.rs:43:6 | LL | /// [E::D] - | ^^^^ - | - = note: the enum `E` has no variant or associated item named `D` + | ^^^^ the enum `E` has no variant or associated item named `D` error: unresolved link to `u8::not_found` --> $DIR/intra-link-errors.rs:47:6 | LL | /// [u8::not_found] - | ^^^^^^^^^^^^^ - | - = note: the builtin type `u8` does not have an associated item named `not_found` + | ^^^^^^^^^^^^^ the builtin type `u8` does not have an associated item named `not_found` error: unresolved link to `S` --> $DIR/intra-link-errors.rs:51:6 | LL | /// [S!] - | ^^ help: to link to the struct, prefix with `struct@`: `struct@S` - | - = note: this link resolves to the struct `S`, which is not in the macro namespace + | ^^ + | | + | this link resolves to the struct `S`, which is not in the macro namespace + | help: to link to the struct, prefix with `struct@`: `struct@S` error: unresolved link to `T::g` --> $DIR/intra-link-errors.rs:69:6 | LL | /// [type@T::g] - | ^^^^^^^^^ help: to link to the associated function, add parentheses: `T::g()` - | - = note: this link resolves to the associated function `g`, which is not in the type namespace + | ^^^^^^^^^ + | | + | this link resolves to the associated function `g`, which is not in the type namespace + | help: to link to the associated function, add parentheses: `T::g()` error: unresolved link to `T::h` --> $DIR/intra-link-errors.rs:74:6 | LL | /// [T::h!] - | ^^^^^ - | - = note: the trait `T` has no macro named `h` + | ^^^^^ the trait `T` has no macro named `h` error: unresolved link to `S::h` --> $DIR/intra-link-errors.rs:61:6 | LL | /// [type@S::h] - | ^^^^^^^^^ help: to link to the associated function, add parentheses: `S::h()` - | - = note: this link resolves to the associated function `h`, which is not in the type namespace + | ^^^^^^^^^ + | | + | this link resolves to the associated function `h`, which is not in the type namespace + | help: to link to the associated function, add parentheses: `S::h()` error: unresolved link to `m` --> $DIR/intra-link-errors.rs:81:6 | LL | /// [m()] - | ^^^ help: to link to the macro, add an exclamation mark: `m!` - | - = note: this link resolves to the macro `m`, which is not in the value namespace + | ^^^ + | | + | this link resolves to the macro `m`, which is not in the value namespace + | help: to link to the macro, add an exclamation mark: `m!` error: aborting due to 16 previous errors diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index 47b6a08baf37d..3c13df20588d8 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr @@ -2,14 +2,13 @@ error: unresolved link to `i` --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) - | ^ + | ^ no item named `i` in `intra_link_span_ice_55723` | note: the lint level is defined here --> $DIR/intra-link-span-ice-55723.rs:1:9 | LL | #![deny(broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^ - = note: no item named `i` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to previous error diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index d64c7e14ba61d..351f8fafa64d8 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -2,7 +2,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] - | ^^^^^ no item named `error` is in scope + | ^^^^^ no item named `error` in `intra_links_warning_crlf` | = note: `#[warn(broken_intra_doc_links)]` on by default = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -11,7 +11,7 @@ warning: unresolved link to `error1` --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] - | ^^^^^^ no item named `error1` is in scope + | ^^^^^^ no item named `error1` in `intra_links_warning_crlf` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -19,7 +19,7 @@ warning: unresolved link to `error2` --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] - | ^^^^^^ no item named `error2` is in scope + | ^^^^^^ no item named `error2` in `intra_links_warning_crlf` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -27,7 +27,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. - | ^^^^^ no item named `error` is in scope + | ^^^^^ no item named `error` in `intra_links_warning_crlf` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 0eaad25501a32..0832e00d35a00 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -10,37 +10,37 @@ warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ no item named `Bar` is in scope + | ^^^^^^^^ no item named `Bar` in `intra_links_warning` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ no item named `Uniooon` is in scope + | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ no item named `Qux` is in scope + | ^^^^^^ no item named `Qux` in `intra_links_warning` warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ no item named `Uniooon` is in scope + | ^^^^^^^^^^ no item named `Uniooon` in `intra_links_warning` warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ no item named `Qux` is in scope + | ^^^^^^ no item named `Qux` in `intra_links_warning` warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ no item named `Qux:Y` is in scope + | ^^^^^ no item named `Qux:Y` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -48,7 +48,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ no item named `error` is in scope + | ^^^^^ no item named `error` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -56,7 +56,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ no item named `error` is in scope + | ^^^^^ no item named `error` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -70,7 +70,7 @@ LL | #[doc = "single line [error]"] single line [error] ^^^^^ - = note: no item named `error` is in scope + = note: no item named `error` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -83,7 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"] single line with "escaping" [error] ^^^^^ - = note: no item named `error` is in scope + = note: no item named `error` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -98,14 +98,14 @@ LL | | /// [error] [error] ^^^^^ - = note: no item named `error` is in scope + = note: no item named `error` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error1` --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] - | ^^^^^^ no item named `error1` is in scope + | ^^^^^^ no item named `error1` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -113,7 +113,7 @@ warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ no item named `error2` is in scope + | ^^^^^^ no item named `error2` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -121,7 +121,7 @@ warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ no item named `BarA` is in scope + | ^^^^ no item named `BarA` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -129,7 +129,7 @@ warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ no item named `BarB` is in scope + | ^^^^ no item named `BarB` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -137,7 +137,7 @@ warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ no item named `BarC` is in scope + | ^^^^ no item named `BarC` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -151,7 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] bar [BarD] bar ^^^^ - = note: no item named `BarD` is in scope + = note: no item named `BarD` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` @@ -167,7 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); bar [BarF] bar ^^^^ - = note: no item named `BarF` is in scope + = note: no item named `BarF` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index 10fcebb27fe0c..550b79f6e8928 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -32,7 +32,7 @@ error: unresolved link to `error` --> $DIR/lint-group.rs:9:29 | LL | /// what up, let's make an [error] - | ^^^^^ + | ^^^^^ no item named `error` in `lint_group` | note: the lint level is defined here --> $DIR/lint-group.rs:7:9 @@ -40,7 +40,6 @@ note: the lint level is defined here LL | #![deny(rustdoc)] | ^^^^^^^ = note: `#[deny(broken_intra_doc_links)]` implied by `#[deny(rustdoc)]` - = note: no item named `error` is in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` error: aborting due to 3 previous errors From 14cc17759de0863e85eea46c0f5bfcc362d1bfe8 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 11 Sep 2020 21:42:28 +0200 Subject: [PATCH 53/54] Improve `ineffective_unstable_trait_impl` error message. --- compiler/rustc_passes/src/stability.rs | 8 ++++---- .../stability-attribute/stability-attribute-trait-impl.rs | 2 +- .../stability-attribute-trait-impl.stderr | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 4eb983365ffb5..4ca52f405fb94 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -561,10 +561,10 @@ impl Visitor<'tcx> for Checker<'tcx> { INEFFECTIVE_UNSTABLE_TRAIT_IMPL, item.hir_id, span, - |lint| lint.build( - "An `#[unstable]` annotation here has no effect. \ - See issue #55436 for more information.", - ).emit() + |lint| lint + .build("an `#[unstable]` annotation here has no effect") + .note("see issue #55436 for more information") + .emit() ); } } diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs index 9be772a7be323..cc57071b87cea 100644 --- a/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.rs @@ -22,7 +22,7 @@ impl StableTrait for UnstableType {} impl UnstableTrait for StableType {} #[unstable(feature = "x", issue = "none")] -//~^ ERROR An `#[unstable]` annotation here has no effect. +//~^ ERROR an `#[unstable]` annotation here has no effect [rustc::ineffective_unstable_trait_impl] impl StableTrait for StableType {} fn main() {} diff --git a/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr index 808636238fcc2..1915d03fb0aaf 100644 --- a/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr @@ -1,10 +1,11 @@ -error: An `#[unstable]` annotation here has no effect. See issue #55436 for more information. +error: an `#[unstable]` annotation here has no effect --> $DIR/stability-attribute-trait-impl.rs:24:1 | LL | #[unstable(feature = "x", issue = "none")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[deny(rustc::ineffective_unstable_trait_impl)]` on by default + = note: see issue #55436 for more information error: aborting due to previous error From 5e126c944b83d8402e0414914ef7d6c05004d483 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Tue, 1 Sep 2020 15:53:04 -0700 Subject: [PATCH 54/54] better diag when const ranges are used in patterns --- compiler/rustc_typeck/src/check/pat.rs | 39 ++++++++++++++++++++++---- src/test/ui/issues/issue-76191.rs | 14 +++++++++ src/test/ui/issues/issue-76191.stderr | 21 ++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 src/test/ui/issues/issue-76191.rs create mode 100644 src/test/ui/issues/issue-76191.stderr diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 1896155e327d8..171d5ee4ff206 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -10,7 +10,7 @@ use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BindingMode, Ty, TypeFoldable}; +use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::{Span, Spanned}; use rustc_span::symbol::Ident; @@ -735,7 +735,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) { - self.emit_bad_pat_path(err, pat.span, res, pat_res, segments, ti.parent_pat); + self.emit_bad_pat_path(err, pat.span, res, pat_res, pat_ty, segments, ti.parent_pat); } pat_ty } @@ -746,6 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat_span: Span, res: Res, pat_res: Res, + pat_ty: Ty<'tcx>, segments: &'b [hir::PathSegment<'b>], parent_pat: Option<&Pat<'_>>, ) { @@ -771,9 +772,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - let msg = "introduce a new binding instead"; - let sugg = format!("other_{}", ident.as_str().to_lowercase()); - e.span_suggestion(ident.span, msg, sugg, Applicability::HasPlaceholders); + let const_def_id = match pat_ty.kind() { + Adt(def, _) => match res { + Res::Def(DefKind::Const, _) => Some(def.did), + _ => None, + }, + _ => None, + }; + + let ranges = &[ + self.tcx.lang_items().range_struct(), + self.tcx.lang_items().range_from_struct(), + self.tcx.lang_items().range_to_struct(), + self.tcx.lang_items().range_full_struct(), + self.tcx.lang_items().range_inclusive_struct(), + self.tcx.lang_items().range_to_inclusive_struct(), + ]; + if const_def_id != None && ranges.contains(&const_def_id) { + let msg = "constants only support matching by type, \ + if you meant to match against a range of values, \ + consider using a range pattern like `min ..= max` in the match block"; + e.note(msg); + } else { + let msg = "introduce a new binding instead"; + let sugg = format!("other_{}", ident.as_str().to_lowercase()); + e.span_suggestion( + ident.span, + msg, + sugg, + Applicability::HasPlaceholders, + ); + } } }; } diff --git a/src/test/ui/issues/issue-76191.rs b/src/test/ui/issues/issue-76191.rs new file mode 100644 index 0000000000000..bc327123c6fe9 --- /dev/null +++ b/src/test/ui/issues/issue-76191.rs @@ -0,0 +1,14 @@ +// Regression test for diagnostic issue #76191 +#![allow(non_snake_case)] + +use std::ops::RangeInclusive; +const RANGE: RangeInclusive = 0..=255; + +fn main() { + let n: i32 = 1; + match n { + RANGE => {} + //~^ ERROR mismatched types + _ => {} + } +} diff --git a/src/test/ui/issues/issue-76191.stderr b/src/test/ui/issues/issue-76191.stderr new file mode 100644 index 0000000000000..a5544d9e9da40 --- /dev/null +++ b/src/test/ui/issues/issue-76191.stderr @@ -0,0 +1,21 @@ +error[E0308]: mismatched types + --> $DIR/issue-76191.rs:10:9 + | +LL | const RANGE: RangeInclusive = 0..=255; + | ------------------------------------------- constant defined here +... +LL | match n { + | - this expression has type `i32` +LL | RANGE => {} + | ^^^^^ + | | + | expected `i32`, found struct `RangeInclusive` + | `RANGE` is interpreted as a constant, not a new binding + | + = note: expected type `i32` + found struct `RangeInclusive` + = note: constants only support matching by type, if you meant to match against a range of values, consider using a range pattern like `min ..= max` in the match block + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`.