Finish off revisions for const generics UI tests.
This time it really does fix it. 😅 The only ones left are `min-and-full-same-time.rs`, which doesn't need it, and `array-impls/` which check the feature indirectly.
Fixes #75279.
r? @lcnr
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.
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
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<T, 3> {
+ 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<T> ::std::ops::Index<Namespace> for PerNS<T> {
//!
//! [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)]
vis
};
+ let mut ret_fields = Vec::with_capacity(vdata.fields().len());
+
for field in vdata.fields() {
// NOTE: The field may be an expansion placeholder, but expansion sets
// correct visibilities for unnamed field placeholders specifically, so the
// constructor visibility should still be determined correctly.
- if let Ok(field_vis) = self.resolve_visibility_speculative(&field.vis, true)
- {
- if ctor_vis.is_at_least(field_vis, &*self.r) {
- ctor_vis = field_vis;
- }
+ let field_vis = self
+ .resolve_visibility_speculative(&field.vis, true)
+ .unwrap_or(ty::Visibility::Public);
+ if ctor_vis.is_at_least(field_vis, &*self.r) {
+ ctor_vis = field_vis;
}
+ ret_fields.push(field_vis);
}
let ctor_res = Res::Def(
DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)),
self.r.local_def_id(ctor_node_id).to_def_id(),
);
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
- self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis));
+ self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields));
}
}
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), def_id) => {
let parent = cstore.def_key(def_id).parent;
if let Some(struct_def_id) = parent.map(|index| DefId { index, ..def_id }) {
- self.r.struct_constructors.insert(struct_def_id, (res, vis));
+ self.r.struct_constructors.insert(struct_def_id, (res, vis, vec![]));
}
}
_ => {}
// Paths in struct expressions and patterns `Path { .. }`.
Struct,
// Paths in tuple struct patterns `Path(..)`.
- TupleStruct(Span),
+ TupleStruct(Span, &'a [Span]),
// `m::A::B` in `<T as m::A>::B::C`.
TraitItem(Namespace),
}
fn namespace(self) -> Namespace {
match self {
PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
- PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(_) => ValueNS,
+ PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) => ValueNS,
PathSource::TraitItem(ns) => ns,
}
}
| PathSource::Expr(..)
| PathSource::Pat
| PathSource::Struct
- | PathSource::TupleStruct(_) => true,
+ | PathSource::TupleStruct(..) => true,
PathSource::Trait(_) | PathSource::TraitItem(..) => false,
}
}
PathSource::Trait(_) => "trait",
PathSource::Pat => "unit struct, unit variant or constant",
PathSource::Struct => "struct, variant or union type",
- PathSource::TupleStruct(_) => "tuple struct or tuple variant",
+ PathSource::TupleStruct(..) => "tuple struct or tuple variant",
PathSource::TraitItem(ns) => match ns {
TypeNS => "associated type",
ValueNS => "method or associated constant",
| Res::SelfCtor(..) => true,
_ => false,
},
- PathSource::TupleStruct(_) => match res {
+ PathSource::TupleStruct(..) => match res {
Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true,
_ => false,
},
(PathSource::Struct, false) => error_code!(E0422),
(PathSource::Expr(..), true) => error_code!(E0423),
(PathSource::Expr(..), false) => error_code!(E0425),
- (PathSource::Pat | PathSource::TupleStruct(_), true) => error_code!(E0532),
- (PathSource::Pat | PathSource::TupleStruct(_), false) => error_code!(E0531),
+ (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
+ (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
(PathSource::TraitItem(..), true) => error_code!(E0575),
(PathSource::TraitItem(..), false) => error_code!(E0576),
}
}
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
-impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
+impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
fn visit_item(&mut self, item: &'ast Item) {
let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
// Always report errors in items we just entered.
}
}
-impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
+impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn new(resolver: &'b mut Resolver<'a>) -> LateResolutionVisitor<'a, 'b, 'ast> {
// During late resolution we only track the module component of the parent scope,
// although it may be useful to track other components as well for diagnostics.
.unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
self.r.record_partial_res(pat.id, PartialRes::new(res));
}
- PatKind::TupleStruct(ref path, ..) => {
- self.smart_resolve_path(pat.id, None, path, PathSource::TupleStruct(pat.span));
+ PatKind::TupleStruct(ref path, ref sub_patterns) => {
+ self.smart_resolve_path(
+ pat.id,
+ None,
+ path,
+ PathSource::TupleStruct(
+ pat.span,
+ self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
+ ),
+ );
}
PatKind::Path(ref qself, ref path) => {
self.smart_resolve_path(pat.id, qself.as_ref(), path, PathSource::Pat);
(variant_path_string, enum_path_string)
}
-impl<'a> LateResolutionVisitor<'a, '_, '_> {
+impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
fn def_span(&self, def_id: DefId) -> Option<Span> {
match def_id.krate {
LOCAL_CRATE => self.r.opt_span(def_id),
);
}
}
- PathSource::Expr(_) | PathSource::TupleStruct(_) | PathSource::Pat => {
+ PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
let span = match &source {
PathSource::Expr(Some(Expr {
span, kind: ExprKind::Call(_, _), ..
}))
- | PathSource::TupleStruct(span) => {
+ | PathSource::TupleStruct(span, _) => {
// We want the main underline to cover the suggested code as well for
// cleaner output.
err.set_span(*span);
err.span_label(span, &format!("`{}` defined here", path_str));
}
let (tail, descr, applicability) = match source {
- PathSource::Pat | PathSource::TupleStruct(_) => {
+ PathSource::Pat | PathSource::TupleStruct(..) => {
("", "pattern", Applicability::MachineApplicable)
}
_ => (": val", "literal", Applicability::HasPlaceholders),
}
(
Res::Def(DefKind::Enum, def_id),
- PathSource::TupleStruct(_) | PathSource::Expr(..),
+ PathSource::TupleStruct(..) | PathSource::Expr(..),
) => {
if self
.diagnostic_metadata
}
}
(Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => {
- if let Some((ctor_def, ctor_vis)) = self.r.struct_constructors.get(&def_id).cloned()
+ if let Some((ctor_def, ctor_vis, fields)) =
+ self.r.struct_constructors.get(&def_id).cloned()
{
let accessible_ctor =
self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
if is_expected(ctor_def) && !accessible_ctor {
- err.span_label(
- span,
- "constructor is not visible here due to private fields".to_string(),
- );
+ let mut better_diag = false;
+ if let PathSource::TupleStruct(_, pattern_spans) = source {
+ if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() {
+ let non_visible_spans: Vec<Span> = fields
+ .iter()
+ .zip(pattern_spans.iter())
+ .filter_map(|(vis, span)| {
+ match self
+ .r
+ .is_accessible_from(*vis, self.parent_scope.module)
+ {
+ true => None,
+ false => Some(*span),
+ }
+ })
+ .collect();
+ // Extra check to be sure
+ if non_visible_spans.len() > 0 {
+ let mut m: rustc_span::MultiSpan =
+ non_visible_spans.clone().into();
+ non_visible_spans.into_iter().for_each(|s| {
+ m.push_span_label(s, "private field".to_string())
+ });
+ err.span_note(
+ m,
+ "constructor is not visible here due to private fields",
+ );
+ better_diag = true;
+ }
+ }
+ }
+
+ if !better_diag {
+ err.span_label(
+ span,
+ "constructor is not visible here due to private fields".to_string(),
+ );
+ }
}
} else {
bad_struct_syntax_suggestion(def_id);
/// Table for mapping struct IDs into struct constructor IDs,
/// it's not used during normal resolution, only for better error reporting.
- struct_constructors: DefIdMap<(Res, ty::Visibility)>,
+ /// Also includes of list of each fields visibility
+ struct_constructors: DefIdMap<(Res, ty::Visibility, Vec<ty::Visibility>)>,
/// Features enabled for this crate.
active_features: FxHashSet<Symbol>,
name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
macro_rules_bindings: TypedArena<MacroRulesBinding<'a>>,
ast_paths: TypedArena<ast::Path>,
+ pattern_spans: TypedArena<Span>,
}
impl<'a> ResolverArenas<'a> {
fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
self.ast_paths.alloc_from_iter(paths.iter().cloned())
}
+ fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
+ self.pattern_spans.alloc_from_iter(spans)
+ }
}
impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
};
use crate::errors::AssocTypeBindingNotAllowed;
use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::{GenericArg, GenericArgs};
}
if position != GenericArgPosition::Type && !args.bindings.is_empty() {
- Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
+ AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span);
}
let explicit_late_bound =
}
if silent {
- return Err(true);
+ return Err((0i32, None));
}
// Unfortunately lifetime and type parameter mismatches are typically styled
for span in spans {
err.span_label(span, label.as_str());
}
- err.emit();
- Err(true)
+ assert_ne!(bound, provided);
+ Err((bound as i32 - provided as i32, Some(err)))
};
- let mut arg_count_correct = Ok(());
let mut unexpected_spans = vec![];
+ let mut lifetime_count_correct = Ok(());
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
- arg_count_correct = check_kind_count(
+ lifetime_count_correct = check_kind_count(
"lifetime",
param_counts.lifetimes,
param_counts.lifetimes,
0,
&mut unexpected_spans,
explicit_late_bound == ExplicitLateBound::Yes,
- )
- .and(arg_count_correct);
+ );
}
+
// FIXME(const_generics:defaults)
+ let mut const_count_correct = Ok(());
if !infer_args || arg_counts.consts > param_counts.consts {
- arg_count_correct = check_kind_count(
+ const_count_correct = check_kind_count(
"const",
param_counts.consts,
param_counts.consts,
arg_counts.lifetimes + arg_counts.types,
&mut unexpected_spans,
false,
- )
- .and(arg_count_correct);
+ );
}
+
// Note that type errors are currently be emitted *after* const errors.
+ let mut type_count_correct = Ok(());
if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize
{
- arg_count_correct = check_kind_count(
+ type_count_correct = check_kind_count(
"type",
param_counts.types - defaults.types - has_self as usize,
param_counts.types - has_self as usize,
arg_counts.lifetimes,
&mut unexpected_spans,
false,
- )
- .and(arg_count_correct);
+ );
}
+ // Emit a help message if it's possible that a type could be surrounded in braces
+ if let Err((c_mismatch, Some(ref mut _const_err))) = const_count_correct {
+ if let Err((_, Some(ref mut type_err))) = type_count_correct {
+ let possible_matches = args.args[arg_counts.lifetimes..]
+ .iter()
+ .filter(|arg| {
+ matches!(
+ arg,
+ GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })
+ )
+ })
+ .take(c_mismatch.max(0) as usize);
+ for arg in possible_matches {
+ let suggestions = vec![
+ (arg.span().shrink_to_lo(), String::from("{ ")),
+ (arg.span().shrink_to_hi(), String::from(" }")),
+ ];
+ type_err.multipart_suggestion(
+ "If this generic argument was intended as a const parameter, \
+ try surrounding it with braces:",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ let emit_correct =
+ |correct: Result<(), (_, Option<rustc_errors::DiagnosticBuilder<'_>>)>| match correct {
+ Ok(()) => Ok(()),
+ Err((_, None)) => Err(()),
+ Err((_, Some(mut err))) => {
+ err.emit();
+ Err(())
+ }
+ };
+
+ let arg_count_correct = emit_correct(lifetime_count_correct)
+ .and(emit_correct(const_count_correct))
+ .and(emit_correct(type_count_correct));
+
GenericArgCountResult {
explicit_late_bound,
- correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch {
- reported: if reported_err { Some(ErrorReported) } else { None },
+ correct: arg_count_correct.map_err(|()| GenericArgCountMismatch {
+ reported: Some(ErrorReported),
invalid_args: unexpected_spans,
}),
}
# Defaults to host platform
#build = "x86_64-unknown-linux-gnu"
-# In addition to the build triple, other triples to produce full compiler
-# toolchains for. Each of these triples will be bootstrapped from the build
-# triple and then will continue to bootstrap themselves. This platform must
-# currently be able to run all of the triples provided here.
+# Which triples to produce a compiler toolchain for. Each of these triples will
+# be bootstrapped from the build triple themselves.
#
# Defaults to just the build triple
#host = ["x86_64-unknown-linux-gnu"]
-# In addition to all host triples, other triples to produce the standard library
-# for. Each host triple will be used to produce a copy of the standard library
-# for each target triple.
+# Which triples to build libraries (core/alloc/std/test/proc_macro) for. Each of
+# these triples will be bootstrapped from the build triple themselves.
#
-# Defaults to just the build triple
+# Defaults to `host`. If you set this explicitly, you likely want to add all
+# host triples to this list as well in order for those host toolchains to be
+# able to compile programs for their native target.
#target = ["x86_64-unknown-linux-gnu"]
# Use this directory to store build artifacts.
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Rc<U>> for Rc<T> {}
impl<T: ?Sized> Rc<T> {
+ #[inline(always)]
+ fn inner(&self) -> &RcBox<T> {
+ // This unsafety is ok because while this Rc is alive we're guaranteed
+ // that the inner pointer is valid.
+ unsafe { self.ptr.as_ref() }
+ }
+
fn from_inner(ptr: NonNull<RcBox<T>>) -> Self {
Self { ptr, phantom: PhantomData }
}
// the strong count, and then remove the implicit "strong weak"
// pointer while also handling drop logic by just crafting a
// fake Weak.
- this.dec_strong();
+ this.inner().dec_strong();
let _weak = Weak { ptr: this.ptr };
forget(this);
Ok(val)
/// ```
#[stable(feature = "rc_weak", since = "1.4.0")]
pub fn downgrade(this: &Self) -> Weak<T> {
- this.inc_weak();
+ this.inner().inc_weak();
// Make sure we do not create a dangling Weak
debug_assert!(!is_dangling(this.ptr));
Weak { ptr: this.ptr }
#[inline]
#[stable(feature = "rc_counts", since = "1.15.0")]
pub fn weak_count(this: &Self) -> usize {
- this.weak() - 1
+ this.inner().weak() - 1
}
/// Gets the number of strong (`Rc`) pointers to this allocation.
#[inline]
#[stable(feature = "rc_counts", since = "1.15.0")]
pub fn strong_count(this: &Self) -> usize {
- this.strong()
+ this.inner().strong()
}
/// Returns `true` if there are no other `Rc` or [`Weak`] pointers to
#[inline]
#[unstable(feature = "get_mut_unchecked", issue = "63292")]
pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T {
- unsafe { &mut this.ptr.as_mut().value }
+ // We are careful to *not* create a reference covering the "count" fields, as
+ // this would conflict with accesses to the reference counts (e.g. by `Weak`).
+ unsafe { &mut (*this.ptr.as_ptr()).value }
}
#[inline]
unsafe {
let mut swap = Rc::new(ptr::read(&this.ptr.as_ref().value));
mem::swap(this, &mut swap);
- swap.dec_strong();
+ swap.inner().dec_strong();
// Remove implicit strong-weak ref (no need to craft a fake
// Weak here -- we know other Weaks can clean up for us)
- swap.dec_weak();
+ swap.inner().dec_weak();
forget(swap);
}
}
/// ```
fn drop(&mut self) {
unsafe {
- self.dec_strong();
- if self.strong() == 0 {
+ self.inner().dec_strong();
+ if self.inner().strong() == 0 {
// destroy the contained object
- ptr::drop_in_place(self.ptr.as_mut());
+ ptr::drop_in_place(Self::get_mut_unchecked(self));
// remove the implicit "strong weak" pointer now that we've
// destroyed the contents.
- self.dec_weak();
+ self.inner().dec_weak();
- if self.weak() == 0 {
+ if self.inner().weak() == 0 {
Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
}
}
/// ```
#[inline]
fn clone(&self) -> Rc<T> {
- self.inc_strong();
+ self.inner().inc_strong();
Self::from_inner(self.ptr)
}
}
address == usize::MAX
}
+/// Helper type to allow accessing the reference counts without
+/// making any assertions about the data field.
+struct WeakInner<'a> {
+ weak: &'a Cell<usize>,
+ strong: &'a Cell<usize>,
+}
+
impl<T: ?Sized> Weak<T> {
/// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying
/// dropping of the inner value if successful.
.unwrap_or(0)
}
- /// Returns `None` when the pointer is dangling and there is no allocated `RcBox`
+ /// Returns `None` when the pointer is dangling and there is no allocated `RcBox`,
/// (i.e., when this `Weak` was created by `Weak::new`).
#[inline]
- fn inner(&self) -> Option<&RcBox<T>> {
- if is_dangling(self.ptr) { None } else { Some(unsafe { self.ptr.as_ref() }) }
+ fn inner(&self) -> Option<WeakInner<'_>> {
+ if is_dangling(self.ptr) {
+ None
+ } else {
+ // We are careful to *not* create a reference covering the "data" field, as
+ // the field may be mutated concurrently (for example, if the last `Rc`
+ // is dropped, the data field will be dropped in-place).
+ Some(unsafe {
+ let ptr = self.ptr.as_ptr();
+ WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak }
+ })
+ }
}
/// Returns `true` if the two `Weak`s point to the same allocation (similar to
/// assert!(other_weak_foo.upgrade().is_none());
/// ```
fn drop(&mut self) {
- if let Some(inner) = self.inner() {
- inner.dec_weak();
- // the weak count starts at 1, and will only go to zero if all
- // the strong pointers have disappeared.
- if inner.weak() == 0 {
- unsafe {
- Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
- }
+ let inner = if let Some(inner) = self.inner() { inner } else { return };
+
+ inner.dec_weak();
+ // the weak count starts at 1, and will only go to zero if all
+ // the strong pointers have disappeared.
+ if inner.weak() == 0 {
+ unsafe {
+ Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref()));
}
}
}
// clone these much in Rust thanks to ownership and move-semantics.
#[doc(hidden)]
-trait RcBoxPtr<T: ?Sized> {
- fn inner(&self) -> &RcBox<T>;
+trait RcInnerPtr {
+ fn weak_ref(&self) -> &Cell<usize>;
+ fn strong_ref(&self) -> &Cell<usize>;
#[inline]
fn strong(&self) -> usize {
- self.inner().strong.get()
+ self.strong_ref().get()
}
#[inline]
if strong == 0 || strong == usize::MAX {
abort();
}
- self.inner().strong.set(strong + 1);
+ self.strong_ref().set(strong + 1);
}
#[inline]
fn dec_strong(&self) {
- self.inner().strong.set(self.strong() - 1);
+ self.strong_ref().set(self.strong() - 1);
}
#[inline]
fn weak(&self) -> usize {
- self.inner().weak.get()
+ self.weak_ref().get()
}
#[inline]
if weak == 0 || weak == usize::MAX {
abort();
}
- self.inner().weak.set(weak + 1);
+ self.weak_ref().set(weak + 1);
}
#[inline]
fn dec_weak(&self) {
- self.inner().weak.set(self.weak() - 1);
+ self.weak_ref().set(self.weak() - 1);
}
}
-impl<T: ?Sized> RcBoxPtr<T> for Rc<T> {
+impl<T: ?Sized> RcInnerPtr for RcBox<T> {
#[inline(always)]
- fn inner(&self) -> &RcBox<T> {
- unsafe { self.ptr.as_ref() }
+ fn weak_ref(&self) -> &Cell<usize> {
+ &self.weak
+ }
+
+ #[inline(always)]
+ fn strong_ref(&self) -> &Cell<usize> {
+ &self.strong
}
}
-impl<T: ?Sized> RcBoxPtr<T> for RcBox<T> {
+impl<'a> RcInnerPtr for WeakInner<'a> {
#[inline(always)]
- fn inner(&self) -> &RcBox<T> {
- self
+ fn weak_ref(&self) -> &Cell<usize> {
+ self.weak
+ }
+
+ #[inline(always)]
+ fn strong_ref(&self) -> &Cell<usize> {
+ self.strong
}
}
#[unstable(feature = "array_chunks", issue = "74985")]
pub use core::slice::ArrayChunks;
+#[unstable(feature = "array_chunks", issue = "74985")]
+pub use core::slice::ArrayChunksMut;
#[stable(feature = "slice_get_slice", since = "1.28.0")]
pub use core::slice::SliceIndex;
#[stable(feature = "from_ref", since = "1.28.0")]
#[inline]
fn next(&mut self) -> Option<T> {
- unsafe {
- if self.ptr as *const _ == self.end {
- None
- } else {
- if mem::size_of::<T>() == 0 {
- // purposefully don't use 'ptr.offset' because for
- // vectors with 0-size elements this would return the
- // same pointer.
- self.ptr = arith_offset(self.ptr as *const i8, 1) as *mut T;
-
- // Make up a value of this ZST.
- Some(mem::zeroed())
- } else {
- let old = self.ptr;
- self.ptr = self.ptr.offset(1);
-
- Some(ptr::read(old))
- }
- }
+ if self.ptr as *const _ == self.end {
+ None
+ } else if mem::size_of::<T>() == 0 {
+ // purposefully don't use 'ptr.offset' because for
+ // vectors with 0-size elements this would return the
+ // same pointer.
+ self.ptr = unsafe { arith_offset(self.ptr as *const i8, 1) as *mut T };
+
+ // Make up a value of this ZST.
+ Some(unsafe { mem::zeroed() })
+ } else {
+ let old = self.ptr;
+ self.ptr = unsafe { self.ptr.offset(1) };
+
+ Some(unsafe { ptr::read(old) })
}
}
impl<T> DoubleEndedIterator for IntoIter<T> {
#[inline]
fn next_back(&mut self) -> Option<T> {
- unsafe {
- if self.end == self.ptr {
- None
- } else {
- if mem::size_of::<T>() == 0 {
- // See above for why 'ptr.offset' isn't used
- self.end = arith_offset(self.end as *const i8, -1) as *mut T;
+ if self.end == self.ptr {
+ None
+ } else if mem::size_of::<T>() == 0 {
+ // See above for why 'ptr.offset' isn't used
+ self.end = unsafe { arith_offset(self.end as *const i8, -1) as *mut T };
- // Make up a value of this ZST.
- Some(mem::zeroed())
- } else {
- self.end = self.end.offset(-1);
+ // Make up a value of this ZST.
+ Some(unsafe { mem::zeroed() })
+ } else {
+ self.end = unsafe { self.end.offset(-1) };
- Some(ptr::read(self.end))
- }
- }
+ Some(unsafe { ptr::read(self.end) })
}
}
}
// 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() }
})
}
// 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() }
})
}
}
#[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")]
+use crate::fmt::{self, Debug};
use crate::future::Future;
use crate::marker;
use crate::pin::Pin;
/// 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<T> {
_data: marker::PhantomData<T>,
/// # Examples
///
/// ```no_run
-/// #![feature(future_readiness_fns)]
/// use core::future;
///
/// # async fn run() {
/// unreachable!();
/// # }
/// ```
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
pub fn pending<T>() -> Pending<T> {
Pending { _data: marker::PhantomData }
}
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
impl<T> Future for Pending<T> {
type Output = T;
}
}
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
impl<T> Unpin for Pending<T> {}
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
+impl<T> Debug for Pending<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Pending").finish()
+ }
+}
+
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
impl<T> Clone for Pending<T> {
fn clone(&self) -> Self {
pending()
/// 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<T>(Option<T>);
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
impl<T> Unpin for Ready<T> {}
-#[unstable(feature = "future_readiness_fns", issue = "70921")]
+#[stable(feature = "future_readiness_fns", since = "1.47.0")]
impl<T> Future for Ready<T> {
type Output = T;
/// 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() {
/// 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: T) -> Ready<T> {
Ready(Some(t))
}
#![feature(doc_cfg)]
#![feature(doc_spotlight)]
#![feature(duration_consts_2)]
+#![feature(duration_saturating_ops)]
#![feature(extern_types)]
#![feature(fundamental)]
#![feature(intrinsics)]
use crate::fmt;
use crate::intrinsics;
use crate::mem::ManuallyDrop;
+use crate::ptr;
/// A wrapper type to construct uninitialized instances of `T`.
///
/// *immediate* undefined behavior, but will cause undefined behavior with most
/// safe operations (including dropping it).
///
+ /// [`Vec<T>`]: ../../std/vec/struct.Vec.html
+ ///
/// # Examples
///
/// Correct usage of this method:
/// this initialization invariant.
///
/// Moreover, this leaves a copy of the same data behind in the `MaybeUninit<T>`. 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
///
/// let mut x = MaybeUninit::<u32>::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::<Option<Vec<u32>>>::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);
/// ```
///
///
/// let mut x = MaybeUninit::<Option<Vec<u32>>>::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 {
}
}
+ /// 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<T>` 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<T>`] 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<T>` however will cause undefined behaviour.
+ ///
+ /// [`assume_init`]: MaybeUninit::assume_init
+ /// [`Vec<T>`]: ../../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
/// Returns an iterator over `N` elements of the slice at a time, starting at the
/// beginning of the slice.
///
- /// The chunks are slices and do not overlap. If `N` does not divide the length of the
- /// slice, then the last up to `N-1` elements will be omitted and can be retrieved
- /// from the `remainder` function of the iterator.
+ /// The chunks are array references and do not overlap. If `N` does not divide the
+ /// length of the slice, then the last up to `N-1` elements will be omitted and can be
+ /// retrieved from the `remainder` function of the iterator.
///
/// This method is the const generic equivalent of [`chunks_exact`].
///
ArrayChunks { iter: array_slice.iter(), rem: snd }
}
+ /// Returns an iterator over `N` elements of the slice at a time, starting at the
+ /// beginning of the slice.
+ ///
+ /// The chunks are mutable array references and do not overlap. If `N` does not divide
+ /// the length of the slice, then the last up to `N-1` elements will be omitted and
+ /// can be retrieved from the `into_remainder` function of the iterator.
+ ///
+ /// This method is the const generic equivalent of [`chunks_exact_mut`].
+ ///
+ /// # Panics
+ ///
+ /// Panics if `N` is 0. This check will most probably get changed to a compile time
+ /// error before this method gets stabilized.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(array_chunks)]
+ /// let v = &mut [0, 0, 0, 0, 0];
+ /// let mut count = 1;
+ ///
+ /// for chunk in v.array_chunks_mut() {
+ /// *chunk = [count; 2];
+ /// count += 1;
+ /// }
+ /// assert_eq!(v, &[1, 1, 2, 2, 0]);
+ /// ```
+ ///
+ /// [`chunks_exact_mut`]: #method.chunks_exact_mut
+ #[unstable(feature = "array_chunks", issue = "74985")]
+ #[inline]
+ pub fn array_chunks_mut<const N: usize>(&mut self) -> ArrayChunksMut<'_, T, N> {
+ assert_ne!(N, 0);
+ let len = self.len() / N;
+ let (fst, snd) = self.split_at_mut(len * N);
+ // SAFETY: We cast a slice of `len * N` elements into
+ // a slice of `len` many `N` elements chunks.
+ unsafe {
+ let array_slice: &mut [[T; N]] = from_raw_parts_mut(fst.as_mut_ptr().cast(), len);
+ ArrayChunksMut { iter: array_slice.iter_mut(), rem: snd }
+ }
+ }
+
/// Returns an iterator over `chunk_size` elements of the slice at a time, starting at the end
/// of the slice.
///
/// time), starting at the beginning of the slice.
///
/// When the slice len is not evenly divided by the chunk size, the last
-/// up to `chunk_size-1` elements will be omitted but can be retrieved from
+/// up to `N-1` elements will be omitted but can be retrieved from
/// the [`remainder`] function from the iterator.
///
/// This struct is created by the [`array_chunks`] method on [slices].
impl<'a, T, const N: usize> ArrayChunks<'a, T, N> {
/// Returns the remainder of the original slice that is not going to be
- /// returned by the iterator. The returned slice has at most `chunk_size-1`
+ /// returned by the iterator. The returned slice has at most `N-1`
/// elements.
#[unstable(feature = "array_chunks", issue = "74985")]
pub fn remainder(&self) -> &'a [T] {
}
}
+/// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements
+/// at a time), starting at the beginning of the slice.
+///
+/// When the slice len is not evenly divided by the chunk size, the last
+/// up to `N-1` elements will be omitted but can be retrieved from
+/// the [`into_remainder`] function from the iterator.
+///
+/// This struct is created by the [`array_chunks_mut`] method on [slices].
+///
+/// [`array_chunks_mut`]: ../../std/primitive.slice.html#method.array_chunks_mut
+/// [`into_remainder`]: ../../std/slice/struct.ArrayChunksMut.html#method.into_remainder
+/// [slices]: ../../std/primitive.slice.html
+#[derive(Debug)]
+#[unstable(feature = "array_chunks", issue = "74985")]
+pub struct ArrayChunksMut<'a, T: 'a, const N: usize> {
+ iter: IterMut<'a, [T; N]>,
+ rem: &'a mut [T],
+}
+
+impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> {
+ /// Returns the remainder of the original slice that is not going to be
+ /// returned by the iterator. The returned slice has at most `N-1`
+ /// elements.
+ #[unstable(feature = "array_chunks", issue = "74985")]
+ pub fn into_remainder(self) -> &'a mut [T] {
+ self.rem
+ }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<'a, T, const N: usize> Iterator for ArrayChunksMut<'a, T, N> {
+ type Item = &'a mut [T; N];
+
+ #[inline]
+ fn next(&mut self) -> Option<&'a mut [T; N]> {
+ self.iter.next()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+
+ #[inline]
+ fn count(self) -> usize {
+ self.iter.count()
+ }
+
+ #[inline]
+ fn nth(&mut self, n: usize) -> Option<Self::Item> {
+ self.iter.nth(n)
+ }
+
+ #[inline]
+ fn last(self) -> Option<Self::Item> {
+ self.iter.last()
+ }
+
+ unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut [T; N] {
+ // SAFETY: The safety guarantees of `get_unchecked` are transferred to
+ // the caller.
+ unsafe { self.iter.get_unchecked(i) }
+ }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunksMut<'a, T, N> {
+ #[inline]
+ fn next_back(&mut self) -> Option<&'a mut [T; N]> {
+ self.iter.next_back()
+ }
+
+ #[inline]
+ fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
+ self.iter.nth_back(n)
+ }
+}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<T, const N: usize> ExactSizeIterator for ArrayChunksMut<'_, T, N> {
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<T, const N: usize> TrustedLen for ArrayChunksMut<'_, T, N> {}
+
+#[unstable(feature = "array_chunks", issue = "74985")]
+impl<T, const N: usize> FusedIterator for ArrayChunksMut<'_, T, N> {}
+
+#[doc(hidden)]
+#[unstable(feature = "array_chunks", issue = "74985")]
+unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> {
+ fn may_have_side_effect() -> bool {
+ false
+ }
+}
+
/// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a
/// time), starting at the end of the slice.
///
/// # Examples
///
/// ```
-/// #![feature(future_readiness_fns)]
/// #![feature(ready_macro)]
///
/// use core::task::{ready, Context, Poll};
/// The `ready!` call expands to:
///
/// ```
-/// # #![feature(future_readiness_fns)]
/// # #![feature(ready_macro)]
/// #
/// # use core::task::{Context, Poll};
#[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.
///
}
}
+ /// 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.
///
}
}
+ /// 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.
///
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`.
///
#![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)]
assert_eq!(res, vec![14, 22]);
}
+#[test]
+fn test_array_chunks_mut_infer() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+ for a in v.array_chunks_mut() {
+ let sum = a.iter().sum::<i32>();
+ *a = [sum; 3];
+ }
+ assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]);
+
+ let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+ v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b));
+ assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]);
+}
+
+#[test]
+fn test_array_chunks_mut_count() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+ let c = v.array_chunks_mut::<3>();
+ assert_eq!(c.count(), 2);
+
+ let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let c2 = v2.array_chunks_mut::<2>();
+ assert_eq!(c2.count(), 2);
+
+ let v3: &mut [i32] = &mut [];
+ let c3 = v3.array_chunks_mut::<2>();
+ assert_eq!(c3.count(), 0);
+}
+
+#[test]
+fn test_array_chunks_mut_nth() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+ let mut c = v.array_chunks_mut::<2>();
+ assert_eq!(c.nth(1).unwrap(), &[2, 3]);
+ assert_eq!(c.next().unwrap(), &[4, 5]);
+
+ let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6];
+ let mut c2 = v2.array_chunks_mut::<3>();
+ assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]);
+ assert_eq!(c2.next(), None);
+}
+
+#[test]
+fn test_array_chunks_mut_nth_back() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+ let mut c = v.array_chunks_mut::<2>();
+ assert_eq!(c.nth_back(1).unwrap(), &[2, 3]);
+ assert_eq!(c.next().unwrap(), &[0, 1]);
+ assert_eq!(c.next(), None);
+
+ let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let mut c2 = v2.array_chunks_mut::<3>();
+ assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]);
+ assert_eq!(c2.next(), None);
+ assert_eq!(c2.next_back(), None);
+
+ let v3: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let mut c3 = v3.array_chunks_mut::<10>();
+ assert_eq!(c3.nth_back(0), None);
+}
+
+#[test]
+fn test_array_chunks_mut_last() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5];
+ let c = v.array_chunks_mut::<2>();
+ assert_eq!(c.last().unwrap(), &[4, 5]);
+
+ let v2: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let c2 = v2.array_chunks_mut::<2>();
+ assert_eq!(c2.last().unwrap(), &[2, 3]);
+}
+
+#[test]
+fn test_array_chunks_mut_remainder() {
+ let v: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let c = v.array_chunks_mut::<2>();
+ assert_eq!(c.into_remainder(), &[4]);
+}
+
+#[test]
+fn test_array_chunks_mut_zip() {
+ let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+ let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+ for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) {
+ let sum = b.iter().sum::<i32>();
+ for v in a {
+ *v += sum;
+ }
+ }
+ assert_eq!(v1, [13, 14, 19, 20, 4]);
+}
+
#[test]
fn test_rchunks_count() {
let v: &[i32] = &[0, 1, 2, 3, 4, 5];
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));
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() {
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));
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)]
#![feature(external_doc)]
#![feature(fn_traits)]
#![feature(format_args_nl)]
-#![feature(future_readiness_fns)]
#![feature(gen_future)]
#![feature(generator_trait)]
#![feature(global_asm)]
+//! Linux-specific extensions to primitives in the `std::fs` module.
+
#![stable(feature = "metadata_ext", since = "1.1.0")]
use crate::fs::Metadata;
-//! Linux-specific definitions
+//! Linux-specific definitions.
#![stable(feature = "raw_ext", since = "1.1.0")]
-//! Linux-specific raw type definitions
+//! Linux-specific raw type definitions.
#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(
-//! Unix-specific extension to the primitives in the `std::ffi` module
+//! Unix-specific extension to the primitives in the `std::ffi` module.
//!
//! # Examples
//!
-//! Unix-specific extensions to general I/O primitives
+//! Unix-specific extensions to general I/O primitives.
#![stable(feature = "rust1", since = "1.0.0")]
-#![stable(feature = "unix_socket", since = "1.10.0")]
+//! Unix-specific networking functionality.
-//! Unix-specific networking functionality
+#![stable(feature = "unix_socket", since = "1.10.0")]
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.0.shutdown(how)
}
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected, without removing that data from the queue. On success,
+ /// returns the number of bytes peeked.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixStream;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixStream::connect("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let len = socket.peek(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "none")]
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.peek(buf)
+ }
}
#[stable(feature = "unix_socket", since = "1.10.0")]
SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) })
}
+ fn recv_from_flags(
+ &self,
+ buf: &mut [u8],
+ flags: libc::c_int,
+ ) -> io::Result<(usize, SocketAddr)> {
+ let mut count = 0;
+ let addr = SocketAddr::new(|addr, len| unsafe {
+ count = libc::recvfrom(
+ *self.0.as_inner(),
+ buf.as_mut_ptr() as *mut _,
+ buf.len(),
+ flags,
+ addr,
+ len,
+ );
+ if count > 0 {
+ 1
+ } else if count == 0 {
+ 0
+ } else {
+ -1
+ }
+ })?;
+
+ Ok((count as usize, addr))
+ }
+
/// Receives data from the socket.
///
/// On success, returns the number of bytes read and the address from
/// ```
#[stable(feature = "unix_socket", since = "1.10.0")]
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
- let mut count = 0;
- let addr = SocketAddr::new(|addr, len| unsafe {
- count = libc::recvfrom(
- *self.0.as_inner(),
- buf.as_mut_ptr() as *mut _,
- buf.len(),
- 0,
- addr,
- len,
- );
- if count > 0 {
- 1
- } else if count == 0 {
- 0
- } else {
- -1
- }
- })?;
-
- Ok((count as usize, addr))
+ self.recv_from_flags(buf, 0)
}
/// Receives data from the socket.
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.0.shutdown(how)
}
+
+ /// Receives data on the socket from the remote address to which it is
+ /// connected, without removing that data from the queue. On success,
+ /// returns the number of bytes peeked.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recv` system call.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::bind("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let len = socket.peek(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "none")]
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.peek(buf)
+ }
+
+ /// Receives a single datagram message on the socket, without removing it from the
+ /// queue. On success, returns the number of bytes read and the origin.
+ ///
+ /// The function must be called with valid byte array `buf` of sufficient size to
+ /// hold the message bytes. If a message is too long to fit in the supplied buffer,
+ /// excess bytes may be discarded.
+ ///
+ /// Successive calls return the same data. This is accomplished by passing
+ /// `MSG_PEEK` as a flag to the underlying `recvfrom` system call.
+ ///
+ /// Do not use this function to implement busy waiting, instead use `libc::poll` to
+ /// synchronize IO events on one or more sockets.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #![feature(unix_socket_peek)]
+ ///
+ /// use std::os::unix::net::UnixDatagram;
+ ///
+ /// fn main() -> std::io::Result<()> {
+ /// let socket = UnixDatagram::bind("/tmp/sock")?;
+ /// let mut buf = [0; 10];
+ /// let (len, addr) = socket.peek_from(&mut buf).expect("peek failed");
+ /// Ok(())
+ /// }
+ /// ```
+ #[unstable(feature = "unix_socket_peek", issue = "none")]
+ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ self.recv_from_flags(buf, libc::MSG_PEEK)
+ }
}
#[stable(feature = "unix_socket", since = "1.10.0")]
fn abstract_namespace_not_allowed() {
assert!(UnixStream::connect("\0asdf").is_err());
}
+
+#[test]
+fn test_unix_stream_peek() {
+ let (txdone, rxdone) = crate::sync::mpsc::channel();
+
+ let dir = tmpdir();
+ let path = dir.path().join("sock");
+
+ let listener = or_panic!(UnixListener::bind(&path));
+ let thread = thread::spawn(move || {
+ let mut stream = or_panic!(listener.accept()).0;
+ or_panic!(stream.write_all(&[1, 3, 3, 7]));
+ or_panic!(rxdone.recv());
+ });
+
+ let mut stream = or_panic!(UnixStream::connect(&path));
+ let mut buf = [0; 10];
+ for _ in 0..2 {
+ assert_eq!(or_panic!(stream.peek(&mut buf)), 4);
+ }
+ assert_eq!(or_panic!(stream.read(&mut buf)), 4);
+
+ or_panic!(stream.set_nonblocking(true));
+ match stream.peek(&mut buf) {
+ Ok(_) => panic!("expected error"),
+ Err(ref e) if e.kind() == ErrorKind::WouldBlock => {}
+ Err(e) => panic!("unexpected error: {}", e),
+ }
+
+ or_panic!(txdone.send(()));
+ thread.join().unwrap();
+}
+
+#[test]
+fn test_unix_datagram_peek() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock2.connect(&path1));
+
+ let msg = b"hello world";
+ or_panic!(sock2.send(msg));
+ for _ in 0..2 {
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.peek(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+}
+
+#[test]
+fn test_unix_datagram_peek_from() {
+ let dir = tmpdir();
+ let path1 = dir.path().join("sock");
+
+ let sock1 = or_panic!(UnixDatagram::bind(&path1));
+ let sock2 = or_panic!(UnixDatagram::unbound());
+ or_panic!(sock2.connect(&path1));
+
+ let msg = b"hello world";
+ or_panic!(sock2.send(msg));
+ for _ in 0..2 {
+ let mut buf = [0; 11];
+ let (size, _) = or_panic!(sock1.peek_from(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+ }
+
+ let mut buf = [0; 11];
+ let size = or_panic!(sock1.recv(&mut buf));
+ assert_eq!(size, 11);
+ assert_eq!(msg, &buf[..]);
+}
-//! Unix-specific primitives available on all unix platforms
+//! Unix-specific primitives available on all unix platforms.
#![stable(feature = "raw_ext", since = "1.1.0")]
#![rustc_deprecated(
+//! Windows-specific extensions to general I/O primitives.
+
#![stable(feature = "rust1", since = "1.0.0")]
use crate::fs;
-//! Windows-specific primitives
+//! Windows-specific primitives.
#![stable(feature = "raw_ext", since = "1.1.0")]
pub struct RunConfig<'a> {
pub builder: &'a Builder<'a>,
- pub host: TargetSelection,
pub target: TargetSelection,
pub path: PathBuf,
}
+impl RunConfig<'_> {
+ pub fn build_triple(&self) -> TargetSelection {
+ self.builder.build.build
+ }
+}
+
struct StepDescription {
default: bool,
only_hosts: bool,
pathset, self.name, builder.config.exclude
);
}
- let hosts = &builder.hosts;
// Determine the targets participating in this rule.
let targets = if self.only_hosts {
&builder.targets
};
- for host in hosts {
- for target in targets {
- let run = RunConfig {
- builder,
- path: pathset.path(builder),
- host: *host,
- target: *target,
- };
- (self.make_run)(run);
- }
+ for target in targets {
+ let run = RunConfig { builder, path: pathset.path(builder), target: *target };
+ (self.make_run)(run);
}
}
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
]
);
+ assert_eq!(
+ first(builder.cache.all::<compile::Std>()),
+ &[
+ compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
+ compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
+ compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
+ compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
+ compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
+ ],
+ );
assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
}
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
]
);
assert!(!builder.cache.all::<compile::Assemble>().is_empty());
compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: a },
- compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
compile::Rustc { compiler: Compiler { host: a, stage: 2 }, target: b },
- compile::Rustc { compiler: Compiler { host: b, stage: 2 }, target: b },
]
);
}
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: a },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: b },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
- compile::Std { compiler: Compiler { host: b, stage: 2 }, target: c },
]
);
assert_eq!(
compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
compile::Assemble { target_compiler: Compiler { host: a, stage: 2 } },
- compile::Assemble { target_compiler: Compiler { host: b, stage: 2 } },
]
);
assert_eq!(
&[
compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: a },
- compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
]
);
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Std {
- compiler: run.builder.compiler(run.builder.top_stage, run.host),
+ compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
target: run.target,
});
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(StartupObjects {
- compiler: run.builder.compiler(run.builder.top_stage, run.host),
+ compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
target: run.target,
});
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(Rustc {
- compiler: run.builder.compiler(run.builder.top_stage, run.host),
+ compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
target: run.target,
});
}
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
struct Build {
build: Option<String>,
- #[serde(default)]
- host: Vec<String>,
- #[serde(default)]
- target: Vec<String>,
+ host: Option<Vec<String>>,
+ target: Option<Vec<String>>,
// This is ignored, the rust code always gets the build directory from the `BUILD_DIR` env variable
build_dir: Option<String>,
cargo: Option<String>,
config.out = dir;
}
- // If --target was specified but --host wasn't specified, don't run any host-only tests.
- let has_hosts = !flags.host.is_empty();
- let has_targets = !flags.target.is_empty();
- config.skip_only_host_steps = !has_hosts && has_targets;
-
let toml = file
.map(|file| {
let contents = t!(fs::read_to_string(&file));
.unwrap_or_else(TomlConfig::default);
let build = toml.build.clone().unwrap_or_default();
- // set by bootstrap.py
- config.hosts.push(config.build);
- for host in build.host.iter().map(|h| TargetSelection::from_user(h)) {
- if !config.hosts.contains(&host) {
- config.hosts.push(host);
- }
- }
- for target in config
- .hosts
- .iter()
- .copied()
- .chain(build.target.iter().map(|h| TargetSelection::from_user(h)))
- {
- if !config.targets.contains(&target) {
- config.targets.push(target);
- }
- }
- config.hosts = if !flags.host.is_empty() { flags.host } else { config.hosts };
- config.targets = if !flags.target.is_empty() { flags.target } else { config.targets };
+
+ // If --target was specified but --host wasn't specified, don't run any host-only tests.
+ let has_hosts = build.host.is_some() || flags.host.is_some();
+ let has_targets = build.target.is_some() || flags.target.is_some();
+ config.skip_only_host_steps = !has_hosts && has_targets;
+
+ config.hosts = if let Some(arg_host) = flags.host.clone() {
+ arg_host
+ } else if let Some(file_host) = build.host {
+ file_host.iter().map(|h| TargetSelection::from_user(h)).collect()
+ } else {
+ vec![config.build]
+ };
+ config.targets = if let Some(arg_target) = flags.target.clone() {
+ arg_target
+ } else if let Some(file_target) = build.target {
+ file_target.iter().map(|h| TargetSelection::from_user(h)).collect()
+ } else {
+ // If target is *not* configured, then default to the host
+ // toolchains.
+ config.hosts.clone()
+ };
config.nodejs = build.nodejs.map(PathBuf::from);
config.gdb = build.gdb.map(PathBuf::from);
fn make_run(run: RunConfig<'_>) {
run.builder.ensure(DebuggerScripts {
- sysroot: run.builder.sysroot(run.builder.compiler(run.builder.top_stage, run.host)),
+ sysroot: run
+ .builder
+ .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())),
host: run.target,
});
}
fn run(self, builder: &Builder<'_>) -> Option<PathBuf> {
let target = self.target;
+ /* run only if llvm-config isn't used */
+ if let Some(config) = builder.config.target_config.get(&target) {
+ if let Some(ref _s) = config.llvm_config {
+ builder.info(&format!("Skipping RustDev ({}): external LLVM", target));
+ return None;
+ }
+ }
+
builder.info(&format!("Dist RustDev ({})", target));
let _time = timeit(builder);
let src = builder.src.join("src/llvm-project/llvm");
// Prepare the image directory
let dst_bindir = image.join("bin");
t!(fs::create_dir_all(&dst_bindir));
+
let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target));
builder.install(&exe, &dst_bindir, 0o755);
builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755);
pub stage: Option<u32>,
pub keep_stage: Vec<u32>,
- pub host: Vec<TargetSelection>,
- pub target: Vec<TargetSelection>,
+ pub host: Option<Vec<TargetSelection>>,
+ pub target: Option<Vec<TargetSelection>>,
pub config: Option<PathBuf>,
pub jobs: Option<u32>,
pub cmd: Subcommand,
.into_iter()
.map(|j| j.parse().expect("`keep-stage` should be a number"))
.collect(),
- host: split(&matches.opt_strs("host"))
- .into_iter()
- .map(|x| TargetSelection::from_user(&x))
- .collect::<Vec<_>>(),
- target: split(&matches.opt_strs("target"))
- .into_iter()
- .map(|x| TargetSelection::from_user(&x))
- .collect::<Vec<_>>(),
+ host: if matches.opt_present("host") {
+ Some(
+ split(&matches.opt_strs("host"))
+ .into_iter()
+ .map(|x| TargetSelection::from_user(&x))
+ .collect::<Vec<_>>(),
+ )
+ } else {
+ None
+ },
+ target: if matches.opt_present("target") {
+ Some(
+ split(&matches.opt_strs("target"))
+ .into_iter()
+ .map(|x| TargetSelection::from_user(&x))
+ .collect::<Vec<_>>(),
+ )
+ } else {
+ None
+ },
config: cfg_file,
jobs: matches.opt_str("jobs").map(|j| j.parse().expect("`jobs` should be a number")),
cmd,
}
fn make_run(run: RunConfig<'_>) {
- let compiler = run.builder.compiler(run.builder.top_stage, run.host);
+ let compiler = run.builder.compiler(run.builder.top_stage, run.target);
run.builder.ensure(RustdocTheme { compiler });
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct RustdocJSNotStd {
- pub host: TargetSelection,
pub target: TargetSelection,
pub compiler: Compiler,
}
}
fn make_run(run: RunConfig<'_>) {
- let compiler = run.builder.compiler(run.builder.top_stage, run.host);
- run.builder.ensure(RustdocJSNotStd { host: run.host, target: run.target, compiler });
+ let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+ run.builder.ensure(RustdocJSNotStd { target: run.target, compiler });
}
fn run(self, builder: &Builder<'_>) {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct RustdocUi {
- pub host: TargetSelection,
pub target: TargetSelection,
pub compiler: Compiler,
}
}
fn make_run(run: RunConfig<'_>) {
- let compiler = run.builder.compiler(run.builder.top_stage, run.host);
- run.builder.ensure(RustdocUi { host: run.host, target: run.target, compiler });
+ let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
+ run.builder.ensure(RustdocUi { target: run.target, compiler });
}
fn run(self, builder: &Builder<'_>) {
}
fn make_run(run: RunConfig<'_>) {
- let compiler = run.builder.compiler(run.builder.top_stage, run.host);
+ let compiler = run.builder.compiler(run.builder.top_stage, run.build_triple());
run.builder.ensure($name { compiler, target: run.target });
}
fn make_run(run: RunConfig<'_>) {
run.builder.ensure($name {
- compiler: run.builder.compiler(run.builder.top_stage, run.host),
+ compiler: run.builder.compiler(run.builder.top_stage, run.target),
});
}
// error_index_generator depends on librustdoc. Use the compiler that
// is normally used to build rustdoc for other tests (like compiletest
// tests in src/test/rustdoc) so that it shares the same artifacts.
- let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host);
+ let compiler = run.builder.compiler_for(run.builder.top_stage, run.target, run.target);
run.builder.ensure(ErrorIndex { compiler });
}
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
- let compiler = builder.compiler(builder.top_stage, run.host);
+ let compiler = builder.compiler(builder.top_stage, run.build_triple());
for krate in builder.in_tree_crates("rustc-main") {
if krate.path.ends_with(&run.path) {
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
- let compiler = builder.compiler(builder.top_stage, run.host);
+ let compiler = builder.compiler(builder.top_stage, run.build_triple());
let test_kind = builder.kind.into();
fn make_run(run: RunConfig<'_>) {
let builder = run.builder;
- let compiler = builder.compiler(builder.top_stage, run.host);
+ let compiler = builder.compiler(builder.top_stage, run.build_triple());
let make = |mode: Mode, krate: &CargoCrate| {
let test_kind = builder.kind.into();
let test_kind = builder.kind.into();
- builder.ensure(CrateRustdoc { host: run.host, test_kind });
+ builder.ensure(CrateRustdoc { host: run.target, test_kind });
}
fn run(self, builder: &Builder<'_>) {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct TierCheck {
pub compiler: Compiler,
- target: TargetSelection,
}
impl Step for TierCheck {
}
fn make_run(run: RunConfig<'_>) {
- let compiler = run.builder.compiler_for(run.builder.top_stage, run.host, run.host);
- run.builder.ensure(TierCheck { compiler, target: run.host });
+ let compiler =
+ run.builder.compiler_for(run.builder.top_stage, run.builder.build.build, run.target);
+ run.builder.ensure(TierCheck { compiler });
}
/// Tests the Platform Support page in the rustc book.
fn run(self, builder: &Builder<'_>) {
- builder.ensure(compile::Std { compiler: self.compiler, target: self.target });
+ builder.ensure(compile::Std { compiler: self.compiler, target: self.compiler.host });
let mut cargo = tool::prepare_tool_cargo(
builder,
self.compiler,
- Mode::ToolRustc,
- self.target,
+ Mode::ToolStd,
+ self.compiler.host,
"run",
"src/tools/tier-check",
SourceType::InTree,
}
fn make_run(run: RunConfig<'_>) {
- run.builder
- .ensure(Rustdoc { compiler: run.builder.compiler(run.builder.top_stage, run.host) });
+ run.builder.ensure(Rustdoc {
+ compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()),
+ });
}
fn run(self, builder: &Builder<'_>) -> PathBuf {
TyKind::Slice(ref ty) => Slice(box ty.clean(cx)),
TyKind::Array(ref ty, ref length) => {
let def_id = cx.tcx.hir().local_def_id(length.hir_id);
- let length = match cx.tcx.const_eval_poly(def_id.to_def_id()) {
- Ok(length) => {
- print_const(cx, ty::Const::from_value(cx.tcx, length, cx.tcx.types.usize))
- }
- Err(_) => cx
- .sess()
- .source_map()
- .span_to_snippet(cx.tcx.def_span(def_id))
- .unwrap_or_else(|_| "_".to_string()),
- };
+ // NOTE(min_const_generics): We can't use `const_eval_poly` for constants
+ // as we currently do not supply the parent generics to anonymous constants
+ // but do allow `ConstKind::Param`.
+ //
+ // `const_eval_poly` tries to to first substitute generic parameters which
+ // results in an ICE while manually constructing the constant and using `eval`
+ // does nothing for `ConstKind::Param`.
+ let ct = ty::Const::from_anon_const(cx.tcx, def_id);
+ let param_env = cx.tcx.param_env(def_id);
+ let length = print_const(cx, ct.eval(cx.tcx, param_env));
Array(box ty.clean(cx), length)
}
TyKind::Tup(ref tys) => Tuple(tys.clean(cx)),
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;
}
}
-enum ErrorKind {
- ResolutionFailure,
+enum ErrorKind<'a> {
+ Resolve(Box<ResolutionFailure<'a>>),
AnchorFailure(AnchorFailure),
}
+impl<'a> From<ResolutionFailure<'a>> 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<Res> {
+ 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<Res> {
+ match self {
+ Self::WrongNamespace(res, _) => Some(*res),
+ _ => None,
+ }
+ }
+}
+
enum AnchorFailure {
MultipleAnchors,
- Primitive,
- Variant,
- AssocConstant,
- AssocType,
- Field,
- Method,
+ RustdocAnchorConflict(Res),
}
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<Option<DefKind>>,
+ kind_side_channel: Cell<Option<(DefKind, DefId)>>,
}
impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
fn variant_field(
&self,
- path_str: &str,
+ path_str: &'path str,
current_item: &Option<String>,
module_id: DefId,
- ) -> Result<(Res, Option<String>), ErrorKind> {
+ extra_fragment: &Option<String>,
+ ) -> Result<(Res, Option<String>), 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| {
}
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 {
.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() => {
)),
))
} 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<DefId>) -> Option<Res> {
+ fn macro_resolve(
+ &self,
+ path_str: &'a str,
+ parent_id: Option<DefId>,
+ ) -> Result<Res, ResolutionFailure<'a>> {
let cx = self.cx;
let path = ast::Path::from_ident(Ident::from_str(path_str));
cx.enter_resolver(|resolver| {
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);
// 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<String>,
parent_id: Option<DefId>,
extra_fragment: &Option<String>,
- ) -> Result<(Res, Option<String>), ErrorKind> {
+ ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
let cx = self.cx;
// In case we're in a module, try to resolve the relative path.
});
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 {
// 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())));
}
};
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" {
}
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_)
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(
) => {
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()
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`
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() {
};
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,
}
} 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
};
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))))
};
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<DefId>,
+ current_item: &Option<String>,
+ extra_fragment: &Option<String>,
+ ) -> Option<Res> {
+ let check_full_res_inner = |this: &Self, result: Result<Res, ErrorKind<'_>>| {
+ 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),
}
}
}
item_name: Symbol,
ns: Namespace,
cx: &DocContext<'_>,
-) -> Option<ty::AssocKind> {
+) -> Option<(ty::AssocKind, DefId)> {
let ty = cx.tcx.type_of(did);
// First consider automatic impls: `impl From<T> for T`
let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
// 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);
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"),
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.
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(),
/// 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<T>(ns: &PerNS<Option<(Res, T)>>) -> bool {
+fn is_derive_trait_collision<T>(ns: &PerNS<Result<(Res, T), ResolutionFailure<'_>>>) -> 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
{
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.
) {
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,
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,
}
}
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;
+ }
}
}
}
path_str,
&dox,
link_range,
- AnchorFailure::Primitive,
+ AnchorFailure::RustdocAnchorConflict(prim),
);
continue;
}
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(),
// 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.
}
}
- /// 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 {
},
};
- // 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 {
}
}
+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
msg: &str,
item: &Item,
dox: &str,
- link_range: Option<Range<usize>>,
+ link_range: &Option<Range<usize>>,
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
) {
let hir_id = match cx.as_local_hir_id(item.def_id) {
}
fn resolution_failure(
- cx: &DocContext<'_>,
+ collector: &LinkCollector<'_, '_>,
item: &Item,
path_str: &str,
+ disambiguator: Option<Disambiguator>,
dox: &str,
link_range: Option<Range<usize>>,
+ 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);
+ }
+ }
},
);
}
) {
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");
}
}
}
- 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 {
sp: Option<rustc_span::Span>,
link_range: &Option<Range<usize>>,
) {
- 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)));
}
}
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");
}
cx: &DocContext<'_>,
res: Res,
extra_fragment: &Option<String>,
-) -> Result<(Res, Option<String>), ErrorKind> {
+) -> Result<(Res, Option<String>), 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);
--> $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
--> $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
--> $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
--- /dev/null
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+
+// FIXME: this should say that it was skipped (maybe an allowed by default lint?)
+/// [<invalid syntax>]
+
+/// [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 {
+ () => {};
+}
--- /dev/null
+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
+
/// [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
}
|
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]
| ^^^^^^^^^
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]
| ^^^^^^^^^
--> $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
--> $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
|
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.
| ^^^^^^^^^^^^^^^^^^
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.
| ^^^^^^^^^^^^^^^^
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.
| ^^^^^^^^^^^^^^^^^^^^^^^
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].
| ^^^^^^^^^^^^^^^^^^^^
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`].
| ^^^^^^^^^^^^^^^
-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].
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]
/// 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`
/// 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`
--> $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
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
--> $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
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
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`
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`
[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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
--> $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 `\]`
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`
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)
--> $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
--- /dev/null
+// ignore-tidy-linelength
+#![feature(min_const_generics)]
+#![crate_name = "foo"]
+
+// @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex<const D: usize> = [i64; D];'
+pub type CellIndex<const D: usize> = [i64; D];
--- /dev/null
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+#[derive(PartialEq, Eq)]
+enum CompileFlag {
+ A,
+ B,
+}
+
+pub fn test_1<const CF: CompileFlag>() {}
+pub fn test_2<T, const CF: CompileFlag>(x: T) {}
+pub struct Example<const CF: CompileFlag, T=u32>{
+ x: T,
+}
+
+impl<const CF: CompileFlag, T> Example<CF, T> {
+ const ASSOC_FLAG: CompileFlag = CompileFlag::A;
+}
+
+pub fn main() {
+ test_1::<CompileFlag::A>();
+ //~^ ERROR: expected type, found variant
+ //~| ERROR: wrong number of const arguments
+ //~| ERROR: wrong number of type arguments
+
+ test_2::<_, CompileFlag::A>(0);
+ //~^ ERROR: expected type, found variant
+ //~| ERROR: wrong number of const arguments
+ //~| ERROR: wrong number of type arguments
+
+ let _: Example<CompileFlag::A, _> = Example { x: 0 };
+ //~^ ERROR: expected type, found variant
+ //~| ERROR: wrong number of const arguments
+ //~| ERROR: wrong number of type arguments
+
+ let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+ //~^ ERROR: wrong number of const arguments
+ //~| ERROR: wrong number of type arguments
+}
--- /dev/null
+error[E0573]: expected type, found variant `CompileFlag::A`
+ --> $DIR/invalid-enum.rs:21:12
+ |
+LL | test_1::<CompileFlag::A>();
+ | ^^^^^^^^^^^^^^
+ | |
+ | not a type
+ | help: try using the variant's enum: `CompileFlag`
+
+error[E0573]: expected type, found variant `CompileFlag::A`
+ --> $DIR/invalid-enum.rs:26:15
+ |
+LL | test_2::<_, CompileFlag::A>(0);
+ | ^^^^^^^^^^^^^^
+ | |
+ | not a type
+ | help: try using the variant's enum: `CompileFlag`
+
+error[E0573]: expected type, found variant `CompileFlag::A`
+ --> $DIR/invalid-enum.rs:31:18
+ |
+LL | let _: Example<CompileFlag::A, _> = Example { x: 0 };
+ | ^^^^^^^^^^^^^^
+ | |
+ | not a type
+ | help: try using the variant's enum: `CompileFlag`
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+ --> $DIR/invalid-enum.rs:31:10
+ |
+LL | let _: Example<CompileFlag::A, _> = Example { x: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected at most 1, found 2
+ --> $DIR/invalid-enum.rs:31:10
+ |
+LL | let _: Example<CompileFlag::A, _> = Example { x: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument
+ |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+ |
+LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 };
+ | ^ ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+ --> $DIR/invalid-enum.rs:36:10
+ |
+LL | let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected at most 1, found 2
+ --> $DIR/invalid-enum.rs:36:10
+ |
+LL | let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected at most 1 type argument
+ |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+ |
+LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 };
+ | ^ ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+ --> $DIR/invalid-enum.rs:21:3
+ |
+LL | test_1::<CompileFlag::A>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected 0, found 1
+ --> $DIR/invalid-enum.rs:21:12
+ |
+LL | test_1::<CompileFlag::A>();
+ | ^^^^^^^^^^^^^^ unexpected type argument
+ |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+ |
+LL | test_1::<{ CompileFlag::A }>();
+ | ^ ^
+
+error[E0107]: wrong number of const arguments: expected 1, found 0
+ --> $DIR/invalid-enum.rs:26:3
+ |
+LL | test_2::<_, CompileFlag::A>(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument
+
+error[E0107]: wrong number of type arguments: expected 1, found 2
+ --> $DIR/invalid-enum.rs:26:15
+ |
+LL | test_2::<_, CompileFlag::A>(0);
+ | ^^^^^^^^^^^^^^ unexpected type argument
+ |
+help: If this generic argument was intended as a const parameter, try surrounding it with braces:
+ |
+LL | test_2::<_, { CompileFlag::A }>(0);
+ | ^ ^
+
+error: aborting due to 11 previous errors
+
+Some errors have detailed explanations: E0107, E0573.
+For more information about an error, try `rustc --explain E0107`.
#![feature(const_panic)]
#![feature(duration_consts_2)]
#![feature(div_duration)]
+#![feature(duration_saturating_ops)]
use std::time::Duration;
const MAX : Duration = Duration::new(u64::MAX, 1_000_000_000 - 1);
- const MAX_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
- assert_eq!(MAX_ADD_ZERO, Some(MAX));
+ const MAX_CHECKED_ADD_ZERO : Option<Duration> = MAX.checked_add(ZERO);
+ assert_eq!(MAX_CHECKED_ADD_ZERO, Some(MAX));
- const MAX_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
- assert_eq!(MAX_ADD_ONE, None);
+ const MAX_CHECKED_ADD_ONE : Option<Duration> = MAX.checked_add(ONE);
+ assert_eq!(MAX_CHECKED_ADD_ONE, None);
- const ONE_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
- assert_eq!(ONE_SUB_ONE, Some(ZERO));
+ const ONE_CHECKED_SUB_ONE : Option<Duration> = ONE.checked_sub(ONE);
+ assert_eq!(ONE_CHECKED_SUB_ONE, Some(ZERO));
- const ZERO_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
- assert_eq!(ZERO_SUB_ONE, None);
+ const ZERO_CHECKED_SUB_ONE : Option<Duration> = ZERO.checked_sub(ONE);
+ assert_eq!(ZERO_CHECKED_SUB_ONE, None);
- const ONE_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
- assert_eq!(ONE_MUL_ONE, Some(ONE));
+ const ONE_CHECKED_MUL_ONE : Option<Duration> = ONE.checked_mul(1);
+ assert_eq!(ONE_CHECKED_MUL_ONE, Some(ONE));
- const MAX_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
- assert_eq!(MAX_MUL_TWO, None);
+ const MAX_CHECKED_MUL_TWO : Option<Duration> = MAX.checked_mul(2);
+ assert_eq!(MAX_CHECKED_MUL_TWO, None);
- const ONE_DIV_ONE : Option<Duration> = ONE.checked_div(1);
- assert_eq!(ONE_DIV_ONE, Some(ONE));
+ const ONE_CHECKED_DIV_ONE : Option<Duration> = ONE.checked_div(1);
+ assert_eq!(ONE_CHECKED_DIV_ONE, Some(ONE));
- const ONE_DIV_ZERO : Option<Duration> = ONE.checked_div(0);
- assert_eq!(ONE_DIV_ZERO, None);
+ const ONE_CHECKED_DIV_ZERO : Option<Duration> = 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);
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() {
--- /dev/null
+pub struct Bar(pub u8, u8, u8);
+
+pub fn make_bar() -> Bar {
+ Bar(1, 12, 10)
+}
--- /dev/null
+// Test for for diagnostic improvement issue #75907
+
+mod foo {
+ pub(crate) struct Foo(u8);
+ pub(crate) struct Bar(pub u8, u8, Foo);
+
+ pub(crate) fn make_bar() -> Bar {
+ Bar(1, 12, Foo(10))
+ }
+}
+
+use foo::{make_bar, Bar, Foo};
+
+fn main() {
+ let Bar(x, y, Foo(z)) = make_bar();
+ //~^ ERROR expected tuple struct
+ //~| ERROR expected tuple struct
+}
--- /dev/null
+error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
+ --> $DIR/issue-75907.rs:15:9
+ |
+LL | let Bar(x, y, Foo(z)) = make_bar();
+ | ^^^
+ |
+note: constructor is not visible here due to private fields
+ --> $DIR/issue-75907.rs:15:16
+ |
+LL | let Bar(x, y, Foo(z)) = make_bar();
+ | ^ ^^^^^^ private field
+ | |
+ | private field
+
+error[E0532]: expected tuple struct or tuple variant, found struct `Foo`
+ --> $DIR/issue-75907.rs:15:19
+ |
+LL | let Bar(x, y, Foo(z)) = make_bar();
+ | ^^^
+ |
+note: constructor is not visible here due to private fields
+ --> $DIR/issue-75907.rs:15:23
+ |
+LL | let Bar(x, y, Foo(z)) = make_bar();
+ | ^ private field
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0532`.
--- /dev/null
+// Test for for diagnostic improvement issue #75907, extern crate
+// aux-build:issue-75907.rs
+
+extern crate issue_75907 as a;
+
+use a::{make_bar, Bar};
+
+fn main() {
+ let Bar(x, y, z) = make_bar();
+ //~^ ERROR expected tuple struct
+}
--- /dev/null
+error[E0532]: expected tuple struct or tuple variant, found struct `Bar`
+ --> $DIR/issue-75907_b.rs:9:9
+ |
+LL | let Bar(x, y, z) = make_bar();
+ | ^^^ constructor is not visible here due to private fields
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0532`.