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", ] diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index cf011e63e02fd..6e6c0c71a1f3b 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'] +max_level_info = ['rustc_driver/max_level_info'] diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index 0d9dcb262b269..adfce1008e1ed 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'] +max_level_info = ['tracing/max_level_info'] 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, ); 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/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index dedb9850b5a19..0becdf24c532b 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -364,6 +364,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/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/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 91edc7d9db775..4ca52f405fb94 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -9,13 +9,14 @@ 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}; 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}; @@ -538,7 +539,37 @@ 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 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") + .note("see issue #55436 for more information") + .emit() + ); + } + } + } + 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 +629,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. 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, ] } 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/config.toml.example b/config.toml.example index 2d5b3136450b9..03baf411efa2e 100644 --- a/config.toml.example +++ b/config.toml.example @@ -322,13 +322,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 = debug-assertions # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info 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 { 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/future/mod.rs b/library/core/src/future/mod.rs index ddce0fe4b7dc5..ec343f75f34ce 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/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/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3a7489aa27955..b0ebaa6a12b51 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`. /// @@ -471,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: @@ -519,8 +522,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 @@ -536,16 +539,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); /// ``` /// @@ -557,14 +560,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 { @@ -573,6 +576,34 @@ impl MaybeUninit { } } + /// Drops the contained value in place. + /// + /// If you have ownership of the `MaybeUninit`, you can use [`assume_init`] instead. + /// + /// # Safety + /// + /// 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 + /// [`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 + // 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()) } + } + /// Gets a shared reference to the contained value. /// /// This can be useful when we want to access a `MaybeUninit` that has been 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/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/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/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")] 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 dc57c1c1f44db..307e222f713b7 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -258,7 +258,6 @@ #![feature(external_doc)] #![feature(fn_traits)] #![feature(format_args_nl)] -#![feature(future_readiness_fns)] #![feature(gen_future)] #![feature(generator_trait)] #![feature(global_asm)] 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")] diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index b2f15eda9d7b0..2224a055d6d87 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -6,8 +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::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::cvt; use crate::sys_common::AsInner; @@ -31,24 +29,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. } 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 } } } diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 5a79d3db5c905..274f68476bb3e 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, @@ -379,6 +380,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, @@ -587,6 +589,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; @@ -630,6 +633,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; @@ -733,6 +737,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 f0224d88226fb..63cfb2a7170f9 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(" max_level_info"); + } + features } 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/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5d10e2e149b32..5780610c86210 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -17,8 +17,9 @@ 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::borrow::Cow; use std::cell::Cell; use std::ops::Range; @@ -46,19 +47,73 @@ pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { } } -enum ErrorKind { - ResolutionFailure, +enum ErrorKind<'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. + /// `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), + /// `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 + NoPrimitiveImpl(Res, String), + /// `[u8::not_found]` + /// the `Res` is the primitive it resolved to + 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, Symbol), + /// should not ever happen + NoParentItem, + /// 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), + /// used to communicate that this should be ignored, but shouldn't be reported to the user + Dummy, +} + +impl ResolutionFailure<'a> { + // A partial or full resolution + fn res(&self) -> Option { + use ResolutionFailure::*; + match self { + NoPrimitiveAssocItem { res, .. } + | NoAssocItem(res, _) + | NoPrimitiveImpl(res, _) + | NotAVariant(res, _) + | WrongNamespace(res, _) + | CannotHaveAssociatedItems(res, _) => Some(*res), + 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 { MultipleAnchors, - Primitive, - Variant, - AssocConstant, - AssocType, - Field, - Method, + RustdocAnchorConflict(Res), } struct LinkCollector<'a, 'tcx> { @@ -68,7 +123,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> { @@ -78,17 +133,25 @@ 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> { + extra_fragment: &Option, + ) -> 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().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_else(|| ResolutionFailure::NotInScope { + module_id, + name: path_str.into(), + })?; let path = split .next() .map(|f| { @@ -99,14 +162,18 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } f.to_owned() }) - .ok_or(ErrorKind::ResolutionFailure)?; - let (_, ty_res) = cx + .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) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; + .map(|(_, res)| res) + .unwrap_or(Res::Err); if let Res::Err = ty_res { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::NotInScope { module_id, name: path.into() }.into()); } let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); match ty_res { @@ -118,7 +185,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(ResolutionFailure::Dummy.into()); } match cx.tcx.type_of(did).kind() { ty::Adt(def, _) if def.is_enum() => { @@ -131,18 +200,43 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { )), )) } else { - Err(ErrorKind::ResolutionFailure) + Err(ResolutionFailure::NotAVariant(ty_res, variant_field_name).into()) } } - _ => Err(ErrorKind::ResolutionFailure), + _ => unreachable!(), } } - _ => Err(ErrorKind::ResolutionFailure), + // `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(kind.into()) + } } } /// Resolves a string as a macro. - fn macro_resolve(&self, path_str: &str, parent_id: Option) -> Option { + fn macro_resolve( + &self, + path_str: &'a 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 +248,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { false, ) { if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind { - return Some(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 Some(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); @@ -168,25 +262,47 @@ 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 Some(Ok(res)); } } } else { debug!("attempting to resolve item without parent module: {}", path_str); + return Some(Err(ResolutionFailure::NoParentItem)); } 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 { + 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 /// 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. @@ -196,8 +312,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(()), + x => x, }; if let Ok((_, res)) = result { @@ -213,7 +329,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()))); } @@ -226,20 +344,22 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }; if value != (ns == ValueNS) { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::WrongNamespace(res, ns).into()); } } 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()))); } // 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 +369,17 @@ 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_else(|| { + debug!("found no `::`, assumming {} was correctly not in scope", item_name); + 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(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?; + for &impl_ in impls { let link = cx .tcx .associated_items(impl_) @@ -272,21 +399,54 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { return Ok(link); } } - return Err(ErrorKind::ResolutionFailure); + debug!( + "returning primitive error for {}::{} in {} namespace", + path, + item_name, + ns.descr() + ); + return Err(ResolutionFailure::NoPrimitiveAssocItem { + res: prim, + prim_name: path, + assoc_item: item_name, + } + .into()); } - let (_, ty_res) = cx + let ty_res = cx .enter_resolver(|resolver| { - resolver.resolve_str_path_error(DUMMY_SP, &path, TypeNS, module_id) + // only types can have associated items + resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) }) - .map_err(|_| ErrorKind::ResolutionFailure)?; - if let Res::Err = ty_res { - return if ns == Namespace::ValueNS { - self.variant_field(path_str, current_item, module_id) - } else { - Err(ErrorKind::ResolutionFailure) - }; - } + .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, extra_fragment) + } 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) + { + Ok((_, Res::Err)) | Err(()) => {} + Ok((_, res)) => { + let res = res.map_id(|_| panic!("unexpected node_id")); + return ResolutionFailure::CannotHaveAssociatedItems( + res, ns, + ); + } + } + } + ResolutionFailure::NotInScope { module_id, name: path_root.into() } + }); + Err(kind.into()) + }; + } + Ok(res) => res, + }; let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); let res = match ty_res { Res::Def( @@ -295,7 +455,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() @@ -307,7 +467,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` @@ -323,26 +483,25 @@ 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", 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. // 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 { + 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() { @@ -355,11 +514,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, @@ -380,7 +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::ResolutionFailure); + return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()); } } Res::Def(DefKind::Trait, did) => cx @@ -401,13 +566,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)))) @@ -417,14 +578,54 @@ 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::ResolutionFailure) + Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into()) } }) } else { debug!("attempting to resolve item without parent module: {}", path_str); - Err(ErrorKind::ResolutionFailure) + Err(ResolutionFailure::NoParentItem.into()) + } + } + + /// 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, + 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(box 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::from)) + }; + match ns { + Namespace::MacroNS => check_full_res_macro(self), + Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns), } } } @@ -435,7 +636,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); @@ -463,7 +664,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); @@ -485,7 +686,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"), @@ -502,12 +703,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. @@ -536,7 +737,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(), @@ -562,10 +763,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 +965,32 @@ 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(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 }; + for &new_ns in &[other_ns, MacroNS] { + if let Some(res) = self.check_full_res( + new_ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { + kind = ResolutionFailure::WrongNamespace(res, ns); + break; + } + } + } + resolution_failure( + self, + &item, + path_str, + disambiguator, + &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 +1017,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(box kind)) => Err(kind), }, value_ns: match self.resolve( path_str, @@ -807,48 +1032,57 @@ 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(box 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 len = candidates.iter().filter(|res| res.is_ok()).count(); + + if len == 0 { + resolution_failure( + self, + &item, + path_str, + disambiguator, + &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.into_iter().filter_map(|res| res.ok()).next().unwrap() } else if len == 2 && is_derive_trait_collision(&candidates) { candidates.type_ns.unwrap() } else { if is_derive_trait_collision(&candidates) { - candidates.macro_ns = None; + candidates.macro_ns = Err(ResolutionFailure::Dummy); } + // 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 +1095,33 @@ 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] { + if let Some(res) = self.check_full_res( + ns, + path_str, + base_node, + ¤t_item, + &extra_fragment, + ) { + kind = ResolutionFailure::WrongNamespace(res, MacroNS); + break; + } + } + resolution_failure( + self, + &item, + path_str, + disambiguator, + &dox, + link_range, + smallvec![kind], + ); + continue; + } } } } @@ -889,7 +1145,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { path_str, &dox, link_range, - AnchorFailure::Primitive, + AnchorFailure::RustdocAnchorConflict(prim), ); continue; } @@ -907,7 +1163,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(), @@ -940,7 +1196,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. @@ -1074,21 +1330,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 { @@ -1113,8 +1364,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 { @@ -1146,6 +1396,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 @@ -1161,7 +1436,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) { @@ -1213,24 +1488,197 @@ fn report_diagnostic( } fn resolution_failure( - cx: &DocContext<'_>, + collector: &LinkCollector<'_, '_>, item: &Item, path_str: &str, + disambiguator: Option, dox: &str, link_range: Option>, + kinds: SmallVec<[ResolutionFailure<'_>; 3]>, ) { report_diagnostic( - cx, + collector.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 item = |res: Res| { + format!( + "the {} `{}`", + res.descr(), + collector.cx.tcx.item_name(res.def_id()).to_string() + ) + }; + let assoc_item_not_allowed = |res: Res| { + let def_id = res.def_id(); + let name = collector.cx.tcx.item_name(def_id); + format!( + "`{}` is {} {}, not a module or type, and cannot have associated items", + name, + res.article(), + res.descr() + ) + }; + // ignore duplicates + let mut variants_seen = SmallVec::<[_; 3]>::new(); + 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); + let note = match failure { + ResolutionFailure::NotInScope { module_id, name, .. } => { + if in_scope { + continue; + } + // NOTE: uses an explicit `continue` so the `note:` will come before the `help:` + 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 { + 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. + // 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 `\]`"#); + } + continue; + } + ResolutionFailure::Dummy => continue, + ResolutionFailure::WrongNamespace(res, expected_ns) => { + if let Res::Def(kind, _) = res { + let disambiguator = Disambiguator::Kind(kind); + suggest_disambiguator( + disambiguator, + diag, + path_str, + dox, + sp, + &link_range, + ) + } - diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); + format!( + "this link resolves to {}, which is not in the {} namespace", + item(res), + expected_ns.descr() + ) + } + ResolutionFailure::NoParentItem => { + diag.level = rustc_errors::Level::Bug; + "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, .. } => { + format!( + "the builtin type `{}` does not have an associated item named `{}`", + prim_name, assoc_item + ) + } + ResolutionFailure::NoAssocItem(res, assoc_item) => { + use DefKind::*; + + let (kind, def_id) = match res { + Res::Def(kind, def_id) => (kind, def_id), + x => unreachable!( + "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}", + x, + ), + }; + let name = collector.cx.tcx.item_name(def_id); + 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 => { + 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"), + } + }; + format!( + "the {} `{}` has no {} named `{}`", + res.descr(), + name, + path_description, + assoc_item + ) + } + ResolutionFailure::CannotHaveAssociatedItems(res, _) => { + 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); + } + } }, ); } @@ -1245,31 +1693,14 @@ 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| { + report_diagnostic(cx, &msg, item, dox, &link_range, |diag, sp| { if let Some(sp) = sp { diag.span_label(sp, "contains invalid anchor"); } @@ -1308,7 +1739,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 { @@ -1330,18 +1761,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))); } } @@ -1356,7 +1789,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"); } @@ -1375,16 +1808,16 @@ 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() { - 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 } else { - return Err(ErrorKind::ResolutionFailure); + return Err(ResolutionFailure::NoParentItem.into()); }; let parent_def = Res::Def(DefKind::Enum, parent); let variant = cx.tcx.expect_variant_res(res); 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..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`] - | ^^^^^^^^ unresolved link + | ^^^^^^^^ 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)] | ^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 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 7530e3ad0f551..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,7 +2,7 @@ error: unresolved link to `v2` --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] - | ^^ unresolved link + | ^^ 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 diff --git a/src/test/rustdoc-ui/intra-doc-alias-ice.stderr b/src/test/rustdoc-ui/intra-doc-alias-ice.stderr index f1c07e31cd753..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) - | ^^^^^^^^^^^^^^^ unresolved link + | ^^^^^^^^^^^^^^^ 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)] | ^^^^^^^^^^^^^^^^^^^^^^ - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` 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..26b629b1313da --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -0,0 +1,88 @@ +#![deny(broken_intra_doc_links)] +//~^ NOTE lint level is defined + +// FIXME: this should say that it was skipped (maybe an allowed by default lint?) +/// [] + +/// [path::to::nonexistent::module] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [path::to::nonexistent::macro!] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [type@path::to::nonexistent::type] +//~^ ERROR unresolved link +//~| NOTE no item named `path` in `intra_link_errors` + +/// [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 + +/// [S::A] +//~^ ERROR unresolved link +//~| NOTE struct `S` has no field or associated item + +/// [S::fmt] +//~^ ERROR unresolved link +//~| NOTE struct `S` has no field or associated item + +/// [E::D] +//~^ ERROR unresolved link +//~| NOTE enum `E` has no variant or associated item + +/// [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 struct, prefix with `struct@` +//~| NOTE this link resolves to the struct `S` +pub fn f() {} +#[derive(Debug)] +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!] +//~^ ERROR unresolved link +//~| NOTE `T` has no macro named `h` +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 new file mode 100644 index 0000000000000..fbf3dcbbec29a --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -0,0 +1,116 @@ +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)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `path::to::nonexistent::macro` + --> $DIR/intra-link-errors.rs:11:6 + | +LL | /// [path::to::nonexistent::macro!] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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] + | ^^^^^^^^^^^^^^^^^^ 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] + | ^^^^^^^^^^^^^^^^^ 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] + | ^^^^^^^^^^^^^^^^^^^^^ 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] + | ^^^^ `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] + | ^^^^ 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] + | ^^^^^^ 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] + | ^^^^ 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] + | ^^^^^^^^^^^^^ 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!] + | ^^ + | | + | 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] + | ^^^^^^^^^ + | | + | 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!] + | ^^^^^ 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] + | ^^^^^^^^^ + | | + | 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()] + | ^^^ + | | + | 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-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-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index 6b0ff8f116295..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,7 +2,7 @@ error: unresolved link to `i` --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) - | ^ unresolved link + | ^ 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 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-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] 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 diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index 1e3a26fadfa9f..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] - | ^^^^^ unresolved link + | ^^^^^ 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] - | ^^^^^^ unresolved link + | ^^^^^^ 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] - | ^^^^^^ unresolved link + | ^^^^^^ 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]. - | ^^^^^ unresolved link + | ^^^^^ 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 53f2476295ebb..0832e00d35a00 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -2,56 +2,45 @@ warning: unresolved link to `Foo::baz` --> $DIR/intra-links-warning.rs:3:23 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ unresolved link + | ^^^^^^^^ the struct `Foo` has no field or associated item named `baz` | = note: `#[warn(broken_intra_doc_links)]` on by default - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ unresolved link - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + | ^^^^^^^^ 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]. - | ^^^^^^^^^^ unresolved link - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + | ^^^^^^^^^^ 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]. - | ^^^^^^ unresolved link - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + | ^^^^^^ 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]. - | ^^^^^^^^^^ unresolved link - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + | ^^^^^^^^^^ 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]. - | ^^^^^^ unresolved link - | - = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` + | ^^^^^^ no item named `Qux` in `intra_links_warning` warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ unresolved link + | ^^^^^ no item named `Qux:Y` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -59,7 +48,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ unresolved link + | ^^^^^ no item named `error` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -67,7 +56,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ unresolved link + | ^^^^^ no item named `error` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -81,6 +70,7 @@ LL | #[doc = "single line [error]"] single line [error] ^^^^^ + = note: no item named `error` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -93,6 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"] single line with "escaping" [error] ^^^^^ + = note: no item named `error` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -107,13 +98,14 @@ LL | | /// [error] [error] ^^^^^ + = 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] - | ^^^^^^ unresolved link + | ^^^^^^ no item named `error1` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -121,7 +113,7 @@ warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ unresolved link + | ^^^^^^ no item named `error2` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -129,7 +121,7 @@ warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ unresolved link + | ^^^^ no item named `BarA` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -137,7 +129,7 @@ warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ unresolved link + | ^^^^ no item named `BarB` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -145,7 +137,7 @@ warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ unresolved link + | ^^^^ no item named `BarC` in `intra_links_warning` | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -159,6 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] bar [BarD] bar ^^^^ + = note: no item named `BarD` in `intra_links_warning` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` @@ -174,6 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); bar [BarF] bar ^^^^ + = 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 04296d2e44a7c..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] - | ^^^^^ unresolved link + | ^^^^^ no item named `error` in `lint_group` | note: the lint level is defined here --> $DIR/lint-group.rs:7:9 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; 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() { 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 new file mode 100644 index 0000000000000..521b9c64b1c57 --- /dev/null +++ b/src/test/ui/issue-76597.rs @@ -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.stderr b/src/test/ui/issue-76597.stderr new file mode 100644 index 0000000000000..50b23329f0ceb --- /dev/null +++ b/src/test/ui/issue-76597.stderr @@ -0,0 +1,13 @@ +error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `y` + --> $DIR/issue-76597.rs:7:38 + | +LL | ... x: u8 + | - + | | + | expected one of 7 possible tokens + | help: missing `,` +LL | ... y: u8, + | ^ unexpected token + +error: aborting due to previous error + 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`. 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), + }, +] 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..cc57071b87cea --- /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 [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 new file mode 100644 index 0000000000000..1915d03fb0aaf --- /dev/null +++ b/src/test/ui/stability-attribute/stability-attribute-trait-impl.stderr @@ -0,0 +1,11 @@ +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 + 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 } }