1
Fork 0

Auto merge of #120136 - matthiaskrgr:rollup-3zzb0z9, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #117561 (Stabilize `slice_first_last_chunk`)
 - #117662 ([rustdoc] Allows links in headings)
 - #119815 (Format sources into the error message when loading codegen backends)
 - #119835 (Exhaustiveness: simplify empty pattern logic)
 - #119984 (Change return type of unstable `Waker::noop()` from `Waker` to `&Waker`.)
 - #120009 (never_patterns: typecheck never patterns)
 - #120122 (Don't add needs-triage to A-diagnostics)
 - #120126 (Suggest `.swap()` when encountering conflicting borrows from `mem::swap` on a slice)
 - #120134 (Restrict access to the private field of newtype indexes)

Failed merges:

 - #119968 (Remove unused/unnecessary features)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-01-20 02:58:08 +00:00
commit 128148d4cf
64 changed files with 745 additions and 593 deletions

View file

@ -2187,16 +2187,6 @@ dependencies = [
"cc",
]
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libloading"
version = "0.8.1"
@ -2479,7 +2469,7 @@ dependencies = [
"lazy_static",
"libc",
"libffi",
"libloading 0.8.1",
"libloading",
"log",
"measureme",
"rand",
@ -4005,7 +3995,7 @@ dependencies = [
name = "rustc_interface"
version = "0.0.0"
dependencies = [
"libloading 0.7.4",
"libloading",
"rustc-rayon",
"rustc-rayon-core",
"rustc_ast",
@ -4135,7 +4125,7 @@ name = "rustc_metadata"
version = "0.0.0"
dependencies = [
"bitflags 2.4.1",
"libloading 0.7.4",
"libloading",
"odht",
"rustc_ast",
"rustc_attr",

View file

@ -26,6 +26,7 @@ use rustc_span::hygiene::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
use rustc_trait_selection::traits::ObligationCtxt;
use std::iter;
@ -1304,14 +1305,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
place: Place<'tcx>,
borrowed_place: Place<'tcx>,
) {
if let ([ProjectionElem::Index(_)], [ProjectionElem::Index(_)]) =
(&place.projection[..], &borrowed_place.projection[..])
let tcx = self.infcx.tcx;
let hir = tcx.hir();
if let ([ProjectionElem::Index(index1)], [ProjectionElem::Index(index2)])
| (
[ProjectionElem::Deref, ProjectionElem::Index(index1)],
[ProjectionElem::Deref, ProjectionElem::Index(index2)],
) = (&place.projection[..], &borrowed_place.projection[..])
{
err.help(
"consider using `.split_at_mut(position)` or similar method to obtain \
two mutable non-overlapping sub-slices",
)
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
let mut note_default_suggestion = || {
err.help(
"consider using `.split_at_mut(position)` or similar method to obtain \
two mutable non-overlapping sub-slices",
)
.help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
};
let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else {
note_default_suggestion();
return;
};
let mut expr_finder =
FindExprBySpan::new(self.body.local_decls[*index1].source_info.span);
expr_finder.visit_expr(hir.body(body_id).value);
let Some(index1) = expr_finder.result else {
note_default_suggestion();
return;
};
expr_finder = FindExprBySpan::new(self.body.local_decls[*index2].source_info.span);
expr_finder.visit_expr(hir.body(body_id).value);
let Some(index2) = expr_finder.result else {
note_default_suggestion();
return;
};
let sm = tcx.sess.source_map();
let Ok(index1_str) = sm.span_to_snippet(index1.span) else {
note_default_suggestion();
return;
};
let Ok(index2_str) = sm.span_to_snippet(index2.span) else {
note_default_suggestion();
return;
};
let Some(object) = hir.parent_id_iter(index1.hir_id).find_map(|id| {
if let hir::Node::Expr(expr) = tcx.hir_node(id)
&& let hir::ExprKind::Index(obj, ..) = expr.kind
{
Some(obj)
} else {
None
}
}) else {
note_default_suggestion();
return;
};
let Ok(obj_str) = sm.span_to_snippet(object.span) else {
note_default_suggestion();
return;
};
let Some(swap_call) = hir.parent_id_iter(object.hir_id).find_map(|id| {
if let hir::Node::Expr(call) = tcx.hir_node(id)
&& let hir::ExprKind::Call(callee, ..) = call.kind
&& let hir::ExprKind::Path(qpath) = callee.kind
&& let hir::QPath::Resolved(None, res) = qpath
&& let hir::def::Res::Def(_, did) = res.res
&& tcx.is_diagnostic_item(sym::mem_swap, did)
{
Some(call)
} else {
None
}
}) else {
note_default_suggestion();
return;
};
err.span_suggestion(
swap_call.span,
"use `.swap()` to swap elements at the specified indices instead",
format!("{obj_str}.swap({index1_str}, {index2_str})"),
Applicability::MachineApplicable,
);
}
}

View file

@ -246,12 +246,12 @@ checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
[[package]]
name = "libloading"
version = "0.7.4"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"winapi",
"windows-sys",
]
[[package]]

View file

@ -19,7 +19,7 @@ gimli = { version = "0.28", default-features = false, features = ["write"]}
object = { version = "0.32", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
indexmap = "2.0.0"
libloading = { version = "0.7.3", optional = true }
libloading = { version = "0.8.0", optional = true }
smallvec = "1.8.1"
[patch.crates-io]

View file

@ -178,8 +178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = match pat.kind {
PatKind::Wild | PatKind::Err(_) => expected,
// FIXME(never_patterns): check the type is uninhabited. If that is not possible within
// typeck, do that in a later phase.
// We allow any type here; we ensure that the type is uninhabited during match checking.
PatKind::Never => expected,
PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti),
PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti),

View file

@ -104,7 +104,7 @@ impl Parse for Newtype {
#gate_rustc_only
impl<E: ::rustc_serialize::Encoder> ::rustc_serialize::Encodable<E> for #name {
fn encode(&self, e: &mut E) {
e.emit_u32(self.private);
e.emit_u32(self.as_u32());
}
}
}
@ -164,7 +164,7 @@ impl Parse for Newtype {
#[inline]
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
if #max_val < u32::MAX {
l.map(|i| i.private).unwrap_or(#max_val+1) == r.map(|i| i.private).unwrap_or(#max_val+1)
l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1)
} else {
match (l, r) {
(Some(l), Some(r)) => r == l,
@ -188,7 +188,7 @@ impl Parse for Newtype {
#[cfg_attr(#gate_rustc_only_cfg, rustc_layout_scalar_valid_range_end(#max))]
#[cfg_attr(#gate_rustc_only_cfg, rustc_pass_by_value)]
#vis struct #name {
private: u32,
private_use_as_methods_instead: u32,
}
#(#consts)*
@ -238,7 +238,7 @@ impl Parse for Newtype {
/// Prefer using `from_u32`.
#[inline]
#vis const unsafe fn from_u32_unchecked(value: u32) -> Self {
Self { private: value }
Self { private_use_as_methods_instead: value }
}
/// Extracts the value of this index as a `usize`.
@ -250,7 +250,7 @@ impl Parse for Newtype {
/// Extracts the value of this index as a `u32`.
#[inline]
#vis const fn as_u32(self) -> u32 {
self.private
self.private_use_as_methods_instead
}
/// Extracts the value of this index as a `usize`.

View file

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
# tidy-alphabetical-start
libloading = "0.7.1"
libloading = "0.8.0"
rustc-rayon = { version = "0.5.0", optional = true }
rustc-rayon-core = { version = "0.5.0", optional = true }
rustc_ast = { path = "../rustc_ast" }

View file

@ -1,9 +1,10 @@
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(error_iter)]
#![feature(internal_output_capture)]
#![feature(thread_spawn_unchecked)]
#![feature(lazy_cell)]
#![feature(let_chains)]
#![feature(thread_spawn_unchecked)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]

View file

@ -162,15 +162,21 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
}
fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
fn format_err(e: &(dyn std::error::Error + 'static)) -> String {
e.sources().map(|e| format!(": {e}")).collect()
}
let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
let err = format!("couldn't load codegen backend {path:?}: {err}");
let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err));
early_dcx.early_fatal(err);
});
let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
.unwrap_or_else(|e| {
let err = format!("couldn't load codegen backend: {e}");
early_dcx.early_fatal(err);
let e = format!(
"`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}",
format_err(&e)
);
early_dcx.early_fatal(e);
});
// Intentionally leak the dynamic library. We can't ever unload it

View file

@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
# tidy-alphabetical-start
bitflags = "2.4.1"
libloading = "0.7.1"
libloading = "0.8.0"
odht = { version = "0.3.1", features = ["nightly"] }
rustc_ast = { path = "../rustc_ast" }
rustc_attr = { path = "../rustc_attr" }

View file

@ -234,6 +234,11 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
mir_build_non_const_path = runtime values cannot be referenced in patterns
mir_build_non_empty_never_pattern =
mismatched types
.label = a never pattern must be used on an uninhabited type
.note = the matched value is of type `{$ty}`
mir_build_non_exhaustive_match_all_arms_guarded =
match arms with guards don't count towards exhaustivity

View file

@ -788,6 +788,16 @@ pub struct FloatPattern;
#[diag(mir_build_pointer_pattern)]
pub struct PointerPattern;
#[derive(Diagnostic)]
#[diag(mir_build_non_empty_never_pattern)]
#[note]
pub struct NonEmptyNeverPattern<'tcx> {
#[primary_span]
#[label]
pub span: Span,
pub ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_indirect_structural_match)]
#[note(mir_build_type_not_structural_tip)]

View file

@ -276,10 +276,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
} else {
// Check the pattern for some things unrelated to exhaustiveness.
let refutable = if cx.refutable { Refutable } else { Irrefutable };
let mut err = Ok(());
pat.walk_always(|pat| {
check_borrow_conflicts_in_at_patterns(self, pat);
check_for_bindings_named_same_as_variants(self, pat, refutable);
err = err.and(check_never_pattern(cx, pat));
});
err?;
Ok(cx.pattern_arena.alloc(cx.lower_pat(pat)))
}
}
@ -289,7 +292,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
fn is_known_valid_scrutinee(&self, scrutinee: &Expr<'tcx>) -> bool {
use ExprKind::*;
match &scrutinee.kind {
// Both pointers and references can validly point to a place with invalid data.
// Pointers can validly point to a place with invalid data. It is undecided whether
// references can too, so we conservatively assume they can.
Deref { .. } => false,
// Inherit validity of the parent place, unless the parent is an union.
Field { lhs, .. } => {
@ -811,6 +815,19 @@ fn check_for_bindings_named_same_as_variants(
}
}
/// Check that never patterns are only used on inhabited types.
fn check_never_pattern<'tcx>(
cx: &MatchCheckCtxt<'_, 'tcx>,
pat: &Pat<'tcx>,
) -> Result<(), ErrorGuaranteed> {
if let PatKind::Never = pat.kind {
if !cx.is_uninhabited(pat.ty) {
return Err(cx.tcx.dcx().emit_err(NonEmptyNeverPattern { span: pat.span, ty: pat.ty }));
}
}
Ok(())
}
fn report_irrefutable_let_patterns(
tcx: TyCtxt<'_>,
id: HirId,

View file

@ -861,12 +861,14 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
/// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
/// and its invariants.
#[instrument(level = "debug", skip(self, pcx, ctors), ret)]
#[instrument(level = "debug", skip(self, ctors), ret)]
pub(crate) fn split<'a>(
&self,
pcx: &PlaceCtxt<'a, Cx>,
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
) -> SplitConstructorSet<Cx> {
) -> SplitConstructorSet<Cx>
where
Cx: 'a,
{
let mut present: SmallVec<[_; 1]> = SmallVec::new();
// Empty constructors found missing.
let mut missing_empty = Vec::new();
@ -1006,17 +1008,6 @@ impl<Cx: TypeCx> ConstructorSet<Cx> {
}
}
// We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
// In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
// types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on()
&& !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
{
// Treat all missing constructors as nonempty.
// This clears `missing_empty`.
missing.append(&mut missing_empty);
}
SplitConstructorSet { present, missing, missing_empty }
}
}

View file

@ -56,7 +56,7 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
let column_ctors = self.patterns.iter().map(|p| p.ctor());
let ctors_for_ty = &pcx.ctors_for_ty()?;
Ok(ctors_for_ty.split(pcx, column_ctors))
Ok(ctors_for_ty.split(column_ctors))
}
/// Does specialization: given a constructor, this takes the patterns from the column that match

View file

@ -737,15 +737,13 @@ pub(crate) struct PlaceCtxt<'a, Cx: TypeCx> {
pub(crate) mcx: MatchCtxt<'a, Cx>,
/// Type of the place under investigation.
pub(crate) ty: Cx::Ty,
/// Whether the place is the original scrutinee place, as opposed to a subplace of it.
pub(crate) is_scrutinee: bool,
}
impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
/// A `PlaceCtxt` when code other than `is_useful` needs one.
#[cfg_attr(not(feature = "rustc"), allow(dead_code))]
pub(crate) fn new_dummy(mcx: MatchCtxt<'a, Cx>, ty: Cx::Ty) -> Self {
PlaceCtxt { mcx, ty, is_scrutinee: false }
PlaceCtxt { mcx, ty }
}
pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
@ -768,9 +766,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
pub enum ValidityConstraint {
ValidOnly,
MaybeInvalid,
/// Option for backwards compatibility: the place is not known to be valid but we allow omitting
/// `useful && !reachable` arms anyway.
MaybeInvalidButAllowOmittingArms,
}
impl ValidityConstraint {
@ -778,20 +773,9 @@ impl ValidityConstraint {
if is_valid_only { ValidOnly } else { MaybeInvalid }
}
fn allow_omitting_side_effecting_arms(self) -> Self {
match self {
MaybeInvalid | MaybeInvalidButAllowOmittingArms => MaybeInvalidButAllowOmittingArms,
// There are no side-effecting empty arms here, nothing to do.
ValidOnly => ValidOnly,
}
}
fn is_known_valid(self) -> bool {
matches!(self, ValidOnly)
}
fn allows_omitting_empty_arms(self) -> bool {
matches!(self, ValidOnly | MaybeInvalidButAllowOmittingArms)
}
/// If the place has validity given by `self` and we read that the value at the place has
/// constructor `ctor`, this computes what we can assume about the validity of the constructor
@ -814,7 +798,7 @@ impl fmt::Display for ValidityConstraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
ValidOnly => "",
MaybeInvalid | MaybeInvalidButAllowOmittingArms => "?",
MaybeInvalid => "?",
};
write!(f, "{s}")
}
@ -1447,41 +1431,44 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
};
debug!("ty: {ty:?}");
let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level };
let pcx = &PlaceCtxt { mcx, ty };
let ctors_for_ty = pcx.ctors_for_ty()?;
// Whether the place/column we are inspecting is known to contain valid data.
let place_validity = matrix.place_validity[0];
// For backwards compability we allow omitting some empty arms that we ideally shouldn't.
let place_validity = place_validity.allow_omitting_side_effecting_arms();
// We treat match scrutinees of type `!` or `EmptyEnum` differently.
let is_toplevel_exception =
is_top_level && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
// Whether empty patterns can be omitted for exhaustiveness.
let can_omit_empty_arms = is_toplevel_exception || mcx.tycx.is_exhaustive_patterns_feature_on();
// Whether empty patterns are counted as useful or not.
let empty_arms_are_unreachable = place_validity.is_known_valid() && can_omit_empty_arms;
// Analyze the constructors present in this column.
let ctors = matrix.heads().map(|p| p.ctor());
let ctors_for_ty = pcx.ctors_for_ty()?;
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
let split_set = ctors_for_ty.split(pcx, ctors);
let mut split_set = ctors_for_ty.split(ctors);
let all_missing = split_set.present.is_empty();
// Build the set of constructors we will specialize with. It must cover the whole type.
// We need to iterate over a full set of constructors, so we add `Missing` to represent the
// missing ones. This is explained under "Constructor Splitting" at the top of this file.
let mut split_ctors = split_set.present;
if !split_set.missing.is_empty() {
// We need to iterate over a full set of constructors, so we add `Missing` to represent the
// missing ones. This is explained under "Constructor Splitting" at the top of this file.
split_ctors.push(Constructor::Missing);
} else if !split_set.missing_empty.is_empty() && !place_validity.is_known_valid() {
// The missing empty constructors are reachable if the place can contain invalid data.
if !(split_set.missing.is_empty()
&& (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
{
split_ctors.push(Constructor::Missing);
}
// Decide what constructors to report.
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. });
let always_report_all = is_top_level && !is_integers;
// Whether we should report "Enum::A and Enum::C are missing" or "_ is missing".
let report_individual_missing_ctors = always_report_all || !all_missing;
// Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
// split_ctors.contains(Missing)`. The converse usually holds except in the
// `MaybeInvalidButAllowOmittingArms` backwards-compatibility case.
// split_ctors.contains(Missing)`. The converse usually holds except when
// `!place_validity.is_known_valid()`.
let mut missing_ctors = split_set.missing;
if !place_validity.allows_omitting_empty_arms() {
missing_ctors.extend(split_set.missing_empty);
if !can_omit_empty_arms {
missing_ctors.append(&mut split_set.missing_empty);
}
let mut ret = WitnessMatrix::empty();

View file

@ -16,7 +16,6 @@
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(ptr_sub_ptr)]
#![feature(slice_first_last_chunk)]
#![cfg_attr(test, feature(test))]
#![allow(rustc::internal)]
#![deny(rustc::untranslatable_diagnostic)]

View file

@ -327,21 +327,21 @@ impl UniverseIndex {
/// name the region `'a`, but that region was not nameable from
/// `U` because it was not in scope there.
pub fn next_universe(self) -> UniverseIndex {
UniverseIndex::from_u32(self.private.checked_add(1).unwrap())
UniverseIndex::from_u32(self.as_u32().checked_add(1).unwrap())
}
/// Returns `true` if `self` can name a name from `other` -- in other words,
/// if the set of names in `self` is a superset of those in
/// `other` (`self >= other`).
pub fn can_name(self, other: UniverseIndex) -> bool {
self.private >= other.private
self >= other
}
/// Returns `true` if `self` cannot name some names from `other` -- in other
/// words, if the set of names in `self` is a strict subset of
/// those in `other` (`self < other`).
pub fn cannot_name(self, other: UniverseIndex) -> bool {
self.private < other.private
self < other
}
}

View file

@ -647,7 +647,7 @@ impl<T, const N: usize> [T; N] {
)]
#[inline]
pub fn split_array_ref<const M: usize>(&self) -> (&[T; M], &[T]) {
(&self[..]).split_array_ref::<M>()
(&self[..]).split_first_chunk::<M>().unwrap()
}
/// Divides one mutable array reference into two at an index.
@ -680,7 +680,7 @@ impl<T, const N: usize> [T; N] {
)]
#[inline]
pub fn split_array_mut<const M: usize>(&mut self) -> (&mut [T; M], &mut [T]) {
(&mut self[..]).split_array_mut::<M>()
(&mut self[..]).split_first_chunk_mut::<M>().unwrap()
}
/// Divides one array reference into two at an index from the end.
@ -725,7 +725,7 @@ impl<T, const N: usize> [T; N] {
)]
#[inline]
pub fn rsplit_array_ref<const M: usize>(&self) -> (&[T], &[T; M]) {
(&self[..]).rsplit_array_ref::<M>()
(&self[..]).split_last_chunk::<M>().unwrap()
}
/// Divides one mutable array reference into two at an index from the end.
@ -758,7 +758,7 @@ impl<T, const N: usize> [T; N] {
)]
#[inline]
pub fn rsplit_array_mut<const M: usize>(&mut self) -> (&mut [T], &mut [T; M]) {
(&mut self[..]).rsplit_array_mut::<M>()
(&mut self[..]).split_last_chunk_mut::<M>().unwrap()
}
}

View file

@ -296,7 +296,7 @@ impl<T> [T] {
if let [.., last] = self { Some(last) } else { None }
}
/// Returns a mutable pointer to the last item in the slice.
/// Returns a mutable reference to the last item in the slice.
///
/// # Examples
///
@ -316,13 +316,13 @@ impl<T> [T] {
if let [.., last] = self { Some(last) } else { None }
}
/// Returns the first `N` elements of the slice, or `None` if it has fewer than `N` elements.
/// Return an array reference to the first `N` items in the slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let u = [10, 40, 30];
/// assert_eq!(Some(&[10, 40]), u.first_chunk::<2>());
///
@ -332,27 +332,26 @@ impl<T> [T] {
/// let w: &[i32] = &[];
/// assert_eq!(Some(&[]), w.first_chunk::<0>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
pub const fn first_chunk<const N: usize>(&self) -> Option<&[T; N]> {
if self.len() < N {
None
} else {
// SAFETY: We explicitly check for the correct number of elements,
// and do not let the reference outlive the slice.
Some(unsafe { &*(self.as_ptr() as *const [T; N]) })
Some(unsafe { &*(self.as_ptr().cast::<[T; N]>()) })
}
}
/// Returns a mutable reference to the first `N` elements of the slice,
/// or `None` if it has fewer than `N` elements.
/// Return a mutable array reference to the first `N` items in the slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &mut [0, 1, 2];
///
/// if let Some(first) = x.first_chunk_mut::<2>() {
@ -360,10 +359,12 @@ impl<T> [T] {
/// first[1] = 4;
/// }
/// assert_eq!(x, &[5, 4, 2]);
///
/// assert_eq!(None, x.first_chunk_mut::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
pub const fn first_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
if self.len() < N {
None
@ -371,28 +372,29 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and require exclusive access to the entire slice to mutate the chunk.
Some(unsafe { &mut *(self.as_mut_ptr() as *mut [T; N]) })
Some(unsafe { &mut *(self.as_mut_ptr().cast::<[T; N]>()) })
}
}
/// Returns the first `N` elements of the slice and the remainder,
/// or `None` if it has fewer than `N` elements.
/// Return an array reference to the first `N` items in the slice and the remaining slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &[0, 1, 2];
///
/// if let Some((first, elements)) = x.split_first_chunk::<2>() {
/// assert_eq!(first, &[0, 1]);
/// assert_eq!(elements, &[2]);
/// }
///
/// assert_eq!(None, x.split_first_chunk::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
pub const fn split_first_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
if self.len() < N {
None
@ -402,18 +404,18 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some((unsafe { &*(first.as_ptr() as *const [T; N]) }, tail))
Some((unsafe { &*(first.as_ptr().cast::<[T; N]>()) }, tail))
}
}
/// Returns a mutable reference to the first `N` elements of the slice and the remainder,
/// or `None` if it has fewer than `N` elements.
/// Return a mutable array reference to the first `N` items in the slice and the remaining
/// slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &mut [0, 1, 2];
///
/// if let Some((first, elements)) = x.split_first_chunk_mut::<2>() {
@ -422,10 +424,12 @@ impl<T> [T] {
/// elements[0] = 5;
/// }
/// assert_eq!(x, &[3, 4, 5]);
///
/// assert_eq!(None, x.split_first_chunk_mut::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
pub const fn split_first_chunk_mut<const N: usize>(
&mut self,
) -> Option<(&mut [T; N], &mut [T])> {
@ -438,29 +442,30 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and enforce exclusive mutability of the chunk by the split.
Some((unsafe { &mut *(first.as_mut_ptr() as *mut [T; N]) }, tail))
Some((unsafe { &mut *(first.as_mut_ptr().cast::<[T; N]>()) }, tail))
}
}
/// Returns the last `N` elements of the slice and the remainder,
/// or `None` if it has fewer than `N` elements.
/// Return an array reference to the last `N` items in the slice and the remaining slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &[0, 1, 2];
///
/// if let Some((last, elements)) = x.split_last_chunk::<2>() {
/// assert_eq!(last, &[1, 2]);
/// if let Some((elements, last)) = x.split_last_chunk::<2>() {
/// assert_eq!(elements, &[0]);
/// assert_eq!(last, &[1, 2]);
/// }
///
/// assert_eq!(None, x.split_last_chunk::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T; N], &[T])> {
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
pub const fn split_last_chunk<const N: usize>(&self) -> Option<(&[T], &[T; N])> {
if self.len() < N {
None
} else {
@ -469,32 +474,35 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some((unsafe { &*(last.as_ptr() as *const [T; N]) }, init))
Some((init, unsafe { &*(last.as_ptr().cast::<[T; N]>()) }))
}
}
/// Returns the last and all the rest of the elements of the slice, or `None` if it is empty.
/// Return a mutable array reference to the last `N` items in the slice and the remaining
/// slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &mut [0, 1, 2];
///
/// if let Some((last, elements)) = x.split_last_chunk_mut::<2>() {
/// if let Some((elements, last)) = x.split_last_chunk_mut::<2>() {
/// last[0] = 3;
/// last[1] = 4;
/// elements[0] = 5;
/// }
/// assert_eq!(x, &[5, 3, 4]);
///
/// assert_eq!(None, x.split_last_chunk_mut::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
pub const fn split_last_chunk_mut<const N: usize>(
&mut self,
) -> Option<(&mut [T; N], &mut [T])> {
) -> Option<(&mut [T], &mut [T; N])> {
if self.len() < N {
None
} else {
@ -504,17 +512,17 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and enforce exclusive mutability of the chunk by the split.
Some((unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) }, init))
Some((init, unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) }))
}
}
/// Returns the last element of the slice, or `None` if it is empty.
/// Return an array reference to the last `N` items in the slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let u = [10, 40, 30];
/// assert_eq!(Some(&[40, 30]), u.last_chunk::<2>());
///
@ -524,9 +532,9 @@ impl<T> [T] {
/// let w: &[i32] = &[];
/// assert_eq!(Some(&[]), w.last_chunk::<0>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
pub const fn last_chunk<const N: usize>(&self) -> Option<&[T; N]> {
if self.len() < N {
None
@ -537,17 +545,17 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// and do not let the references outlive the slice.
Some(unsafe { &*(last.as_ptr() as *const [T; N]) })
Some(unsafe { &*(last.as_ptr().cast::<[T; N]>()) })
}
}
/// Returns a mutable pointer to the last item in the slice.
/// Return a mutable array reference to the last `N` items in the slice.
///
/// If the slice is not at least `N` in length, this will return `None`.
///
/// # Examples
///
/// ```
/// #![feature(slice_first_last_chunk)]
///
/// let x = &mut [0, 1, 2];
///
/// if let Some(last) = x.last_chunk_mut::<2>() {
@ -555,10 +563,12 @@ impl<T> [T] {
/// last[1] = 20;
/// }
/// assert_eq!(x, &[0, 10, 20]);
///
/// assert_eq!(None, x.last_chunk_mut::<4>());
/// ```
#[unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[rustc_const_unstable(feature = "slice_first_last_chunk", issue = "111774")]
#[inline]
#[stable(feature = "slice_first_last_chunk", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_unstable(feature = "const_slice_first_last_chunk", issue = "111774")]
pub const fn last_chunk_mut<const N: usize>(&mut self) -> Option<&mut [T; N]> {
if self.len() < N {
None
@ -570,7 +580,7 @@ impl<T> [T] {
// SAFETY: We explicitly check for the correct number of elements,
// do not let the reference outlive the slice,
// and require exclusive access to the entire slice to mutate the chunk.
Some(unsafe { &mut *(last.as_mut_ptr() as *mut [T; N]) })
Some(unsafe { &mut *(last.as_mut_ptr().cast::<[T; N]>()) })
}
}
@ -1859,7 +1869,6 @@ impl<T> [T] {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")]
#[rustc_allow_const_fn_unstable(slice_split_at_unchecked)]
#[inline]
#[track_caller]
#[must_use]
@ -1946,7 +1955,10 @@ impl<T> [T] {
/// }
/// ```
#[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")]
#[rustc_const_unstable(feature = "slice_split_at_unchecked", issue = "76014")]
#[rustc_const_stable(
feature = "const_slice_split_at_unchecked",
since = "CURRENT_RUSTC_VERSION"
)]
#[inline]
#[must_use]
pub const unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) {
@ -2019,164 +2031,6 @@ impl<T> [T] {
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
}
/// Divides one slice into an array and a remainder slice at an index.
///
/// The array will contain all indices from `[0, N)` (excluding
/// the index `N` itself) and the slice will contain all
/// indices from `[N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let v = &[1, 2, 3, 4, 5, 6][..];
///
/// {
/// let (left, right) = v.split_array_ref::<0>();
/// assert_eq!(left, &[]);
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<2>();
/// assert_eq!(left, &[1, 2]);
/// assert_eq!(right, [3, 4, 5, 6]);
/// }
///
/// {
/// let (left, right) = v.split_array_ref::<6>();
/// assert_eq!(left, &[1, 2, 3, 4, 5, 6]);
/// assert_eq!(right, []);
/// }
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
#[track_caller]
#[must_use]
pub fn split_array_ref<const N: usize>(&self) -> (&[T; N], &[T]) {
let (a, b) = self.split_at(N);
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at)
unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
}
/// Divides one mutable slice into an array and a remainder slice at an index.
///
/// The array will contain all indices from `[0, N)` (excluding
/// the index `N` itself) and the slice will contain all
/// indices from `[N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let mut v = &mut [1, 0, 3, 0, 5, 6][..];
/// let (left, right) = v.split_array_mut::<2>();
/// assert_eq!(left, &mut [1, 0]);
/// assert_eq!(right, [3, 0, 5, 6]);
/// left[1] = 2;
/// right[1] = 4;
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
#[track_caller]
#[must_use]
pub fn split_array_mut<const N: usize>(&mut self) -> (&mut [T; N], &mut [T]) {
let (a, b) = self.split_at_mut(N);
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at_mut)
unsafe { (&mut *(a.as_mut_ptr() as *mut [T; N]), b) }
}
/// Divides one slice into an array and a remainder slice at an index from
/// the end.
///
/// The slice will contain all indices from `[0, len - N)` (excluding
/// the index `len - N` itself) and the array will contain all
/// indices from `[len - N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let v = &[1, 2, 3, 4, 5, 6][..];
///
/// {
/// let (left, right) = v.rsplit_array_ref::<0>();
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
/// assert_eq!(right, &[]);
/// }
///
/// {
/// let (left, right) = v.rsplit_array_ref::<2>();
/// assert_eq!(left, [1, 2, 3, 4]);
/// assert_eq!(right, &[5, 6]);
/// }
///
/// {
/// let (left, right) = v.rsplit_array_ref::<6>();
/// assert_eq!(left, []);
/// assert_eq!(right, &[1, 2, 3, 4, 5, 6]);
/// }
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
#[must_use]
pub fn rsplit_array_ref<const N: usize>(&self) -> (&[T], &[T; N]) {
assert!(N <= self.len());
let (a, b) = self.split_at(self.len() - N);
// SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at)
unsafe { (a, &*(b.as_ptr() as *const [T; N])) }
}
/// Divides one mutable slice into an array and a remainder slice at an
/// index from the end.
///
/// The slice will contain all indices from `[0, len - N)` (excluding
/// the index `N` itself) and the array will contain all
/// indices from `[len - N, len)` (excluding the index `len` itself).
///
/// # Panics
///
/// Panics if `N > len`.
///
/// # Examples
///
/// ```
/// #![feature(split_array)]
///
/// let mut v = &mut [1, 0, 3, 0, 5, 6][..];
/// let (left, right) = v.rsplit_array_mut::<4>();
/// assert_eq!(left, [1, 0]);
/// assert_eq!(right, &mut [3, 0, 5, 6]);
/// left[1] = 2;
/// right[1] = 4;
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
/// ```
#[unstable(feature = "split_array", reason = "new API", issue = "90091")]
#[inline]
#[must_use]
pub fn rsplit_array_mut<const N: usize>(&mut self) -> (&mut [T], &mut [T; N]) {
assert!(N <= self.len());
let (a, b) = self.split_at_mut(self.len() - N);
// SAFETY: b points to [T; N]? Yes it's [T] of length N (checked by split_at_mut)
unsafe { (a, &mut *(b.as_mut_ptr() as *mut [T; N])) }
}
/// Returns an iterator over subslices separated by elements that match
/// `pred`. The matched element is not contained in the subslices.
///

View file

@ -329,12 +329,14 @@ impl Waker {
Waker { waker }
}
/// Creates a new `Waker` that does nothing when `wake` is called.
/// Returns a reference to a `Waker` that does nothing when used.
///
/// This is mostly useful for writing tests that need a [`Context`] to poll
/// some futures, but are not expecting those futures to wake the waker or
/// do not need to do anything specific if it happens.
///
/// If an owned `Waker` is needed, `clone()` this one.
///
/// # Examples
///
/// ```
@ -343,8 +345,7 @@ impl Waker {
/// use std::future::Future;
/// use std::task;
///
/// let waker = task::Waker::noop();
/// let mut cx = task::Context::from_waker(&waker);
/// let mut cx = task::Context::from_waker(task::Waker::noop());
///
/// let mut future = Box::pin(async { 10 });
/// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10));
@ -352,7 +353,12 @@ impl Waker {
#[inline]
#[must_use]
#[unstable(feature = "noop_waker", issue = "98286")]
pub const fn noop() -> Waker {
pub const fn noop() -> &'static Waker {
// Ideally all this data would be explicitly `static` because it is used by reference and
// only ever needs one copy. But `const fn`s (and `const` items) cannot refer to statics,
// even though their values can be promoted to static. (That might change; see #119618.)
// An alternative would be a `pub static NOOP: &Waker`, but associated static items are not
// currently allowed either, and making it non-associated would be unergonomic.
const VTABLE: RawWakerVTable = RawWakerVTable::new(
// Cloning just returns a new no-op raw waker
|_| RAW,
@ -364,8 +370,9 @@ impl Waker {
|_| {},
);
const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE);
const WAKER_REF: &Waker = &Waker { waker: RAW };
Waker { waker: RAW }
WAKER_REF
}
/// Get a reference to the underlying [`RawWaker`].

View file

@ -7,8 +7,7 @@ fn into_async_iter() {
let async_iter = async_iter::from_iter(0..3);
let mut async_iter = pin!(async_iter.into_async_iter());
let waker = core::task::Waker::noop();
let mut cx = &mut core::task::Context::from_waker(&waker);
let mut cx = &mut core::task::Context::from_waker(core::task::Waker::noop());
assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(0)));
assert_eq!(async_iter.as_mut().poll_next(&mut cx), Poll::Ready(Some(1)));

View file

@ -2398,37 +2398,45 @@ mod swap_panics {
}
#[test]
fn slice_split_array_mut() {
fn slice_split_first_chunk_mut() {
let v = &mut [1, 2, 3, 4, 5, 6][..];
{
let (left, right) = v.split_array_mut::<0>();
let (left, right) = v.split_first_chunk_mut::<0>().unwrap();
assert_eq!(left, &mut []);
assert_eq!(right, [1, 2, 3, 4, 5, 6]);
}
{
let (left, right) = v.split_array_mut::<6>();
let (left, right) = v.split_first_chunk_mut::<6>().unwrap();
assert_eq!(left, &mut [1, 2, 3, 4, 5, 6]);
assert_eq!(right, []);
}
{
assert!(v.split_first_chunk_mut::<7>().is_none());
}
}
#[test]
fn slice_rsplit_array_mut() {
fn slice_split_last_chunk_mut() {
let v = &mut [1, 2, 3, 4, 5, 6][..];
{
let (left, right) = v.rsplit_array_mut::<0>();
let (left, right) = v.split_last_chunk_mut::<0>().unwrap();
assert_eq!(left, [1, 2, 3, 4, 5, 6]);
assert_eq!(right, &mut []);
}
{
let (left, right) = v.rsplit_array_mut::<6>();
let (left, right) = v.split_last_chunk_mut::<6>().unwrap();
assert_eq!(left, []);
assert_eq!(right, &mut [1, 2, 3, 4, 5, 6]);
}
{
assert!(v.split_last_chunk_mut::<7>().is_none());
}
}
#[test]
@ -2443,38 +2451,6 @@ fn split_as_slice() {
assert_eq!(split.as_slice(), &[]);
}
#[should_panic]
#[test]
fn slice_split_array_ref_out_of_bounds() {
let v = &[1, 2, 3, 4, 5, 6][..];
let _ = v.split_array_ref::<7>();
}
#[should_panic]
#[test]
fn slice_split_array_mut_out_of_bounds() {
let v = &mut [1, 2, 3, 4, 5, 6][..];
let _ = v.split_array_mut::<7>();
}
#[should_panic]
#[test]
fn slice_rsplit_array_ref_out_of_bounds() {
let v = &[1, 2, 3, 4, 5, 6][..];
let _ = v.rsplit_array_ref::<7>();
}
#[should_panic]
#[test]
fn slice_rsplit_array_mut_out_of_bounds() {
let v = &mut [1, 2, 3, 4, 5, 6][..];
let _ = v.rsplit_array_mut::<7>();
}
#[test]
fn slice_split_once() {
let v = &[1, 2, 3, 2, 4][..];

View file

@ -530,7 +530,6 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
for event in &mut self.inner {
match &event.0 {
Event::End(Tag::Heading(..)) => break,
Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
Event::Text(text) | Event::Code(text) => {
id.extend(text.chars().filter_map(slugify));
self.buf.push_back(event);
@ -549,12 +548,10 @@ impl<'a, 'b, 'ids, I: Iterator<Item = SpannedEvent<'a>>> Iterator
let level =
std::cmp::min(level as u32 + (self.heading_offset as u32), MAX_HEADER_LEVEL);
self.buf.push_back((Event::Html(format!("</a></h{level}>").into()), 0..0));
self.buf.push_back((Event::Html(format!("</h{level}>").into()), 0..0));
let start_tags = format!(
"<h{level} id=\"{id}\">\
<a href=\"#{id}\">",
);
let start_tags =
format!("<h{level} id=\"{id}\"><a class=\"doc-anchor\" href=\"#{id}\">§</a>");
return Some((Event::Html(start_tags.into()), 0..0));
}
event

View file

@ -311,26 +311,38 @@ fn test_header() {
assert_eq!(output, expect, "original: {}", input);
}
t("# Foo bar", "<h2 id=\"foo-bar\"><a href=\"#foo-bar\">Foo bar</a></h2>");
t(
"# Foo bar",
"<h2 id=\"foo-bar\"><a class=\"doc-anchor\" href=\"#foo-bar\">§</a>Foo bar</h2>",
);
t(
"## Foo-bar_baz qux",
"<h3 id=\"foo-bar_baz-qux\">\
<a href=\"#foo-bar_baz-qux\">Foo-bar_baz qux</a></h3>",
<a class=\"doc-anchor\" href=\"#foo-bar_baz-qux\">§</a>\
Foo-bar_baz qux\
</h3>",
);
t(
"### **Foo** *bar* baz!?!& -_qux_-%",
"<h4 id=\"foo-bar-baz--qux-\">\
<a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \
<em>bar</em> baz!?!&amp; -<em>qux</em>-%</a>\
<a class=\"doc-anchor\" href=\"#foo-bar-baz--qux-\">§</a>\
<strong>Foo</strong> <em>bar</em> baz!?!&amp; -<em>qux</em>-%\
</h4>",
);
t(
"#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux",
"<h5 id=\"foo--bar--baz--qux\">\
<a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> &amp; *bar?!* \
<em><code>baz</code></em> #qux</a>\
<a class=\"doc-anchor\" href=\"#foo--bar--baz--qux\">§</a>\
<strong>Foo?</strong> &amp; *bar?!* <em><code>baz</code></em> #qux\
</h5>",
);
t(
"# Foo [bar](https://hello.yo)",
"<h2 id=\"foo-bar\">\
<a class=\"doc-anchor\" href=\"#foo-bar\">§</a>\
Foo <a href=\"https://hello.yo\">bar</a>\
</h2>",
);
}
#[test]
@ -351,12 +363,36 @@ fn test_header_ids_multiple_blocks() {
assert_eq!(output, expect, "original: {}", input);
}
t(&mut map, "# Example", "<h2 id=\"example\"><a href=\"#example\">Example</a></h2>");
t(&mut map, "# Panics", "<h2 id=\"panics\"><a href=\"#panics\">Panics</a></h2>");
t(&mut map, "# Example", "<h2 id=\"example-1\"><a href=\"#example-1\">Example</a></h2>");
t(&mut map, "# Search", "<h2 id=\"search-1\"><a href=\"#search-1\">Search</a></h2>");
t(&mut map, "# Example", "<h2 id=\"example-2\"><a href=\"#example-2\">Example</a></h2>");
t(&mut map, "# Panics", "<h2 id=\"panics-1\"><a href=\"#panics-1\">Panics</a></h2>");
t(
&mut map,
"# Example",
"<h2 id=\"example\"><a class=\"doc-anchor\" href=\"#example\">§</a>Example</h2>",
);
t(
&mut map,
"# Panics",
"<h2 id=\"panics\"><a class=\"doc-anchor\" href=\"#panics\">§</a>Panics</h2>",
);
t(
&mut map,
"# Example",
"<h2 id=\"example-1\"><a class=\"doc-anchor\" href=\"#example-1\">§</a>Example</h2>",
);
t(
&mut map,
"# Search",
"<h2 id=\"search-1\"><a class=\"doc-anchor\" href=\"#search-1\">§</a>Search</h2>",
);
t(
&mut map,
"# Example",
"<h2 id=\"example-2\"><a class=\"doc-anchor\" href=\"#example-2\">§</a>Example</h2>",
);
t(
&mut map,
"# Panics",
"<h2 id=\"panics-1\"><a class=\"doc-anchor\" href=\"#panics-1\">§</a>Panics</h2>",
);
}
#[test]

View file

@ -1207,17 +1207,31 @@ impl<'a> AssocItemLink<'a> {
}
}
fn write_impl_section_heading(mut w: impl fmt::Write, title: &str, id: &str) {
pub fn write_section_heading(
w: &mut impl fmt::Write,
title: &str,
id: &str,
extra_class: Option<&str>,
extra: impl fmt::Display,
) {
let (extra_class, whitespace) = match extra_class {
Some(extra) => (extra, " "),
None => ("", ""),
};
write!(
w,
"<h2 id=\"{id}\" class=\"section-header\">\
"<h2 id=\"{id}\" class=\"{extra_class}{whitespace}section-header\">\
{title}\
<a href=\"#{id}\" class=\"anchor\">§</a>\
</h2>"
</h2>{extra}",
)
.unwrap();
}
fn write_impl_section_heading(w: &mut impl fmt::Write, title: &str, id: &str) {
write_section_heading(w, title, id, None, "")
}
pub(crate) fn render_all_impls(
mut w: impl Write,
cx: &mut Context<'_>,

View file

@ -19,8 +19,8 @@ use super::{
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
render_impl, render_rightside, render_stability_since_raw,
render_stability_since_raw_with_extra, AssocItemLink, AssocItemRender, Context,
ImplRenderingParameters, RenderMode,
render_stability_since_raw_with_extra, write_section_heading, AssocItemLink, AssocItemRender,
Context, ImplRenderingParameters, RenderMode,
};
use crate::clean;
use crate::config::ModuleSorting;
@ -425,13 +425,12 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
w.write_str(ITEM_TABLE_CLOSE);
}
last_section = Some(my_section);
write!(
write_section_heading(
w,
"<h2 id=\"{id}\" class=\"section-header\">\
<a href=\"#{id}\">{name}</a>\
</h2>{ITEM_TABLE_OPEN}",
id = cx.derive_id(my_section.id()),
name = my_section.name(),
my_section.name(),
&cx.derive_id(my_section.id()),
None,
ITEM_TABLE_OPEN,
);
}
@ -814,16 +813,6 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// Trait documentation
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) {
write!(
w,
"<h2 id=\"{0}\" class=\"section-header\">\
{1}<a href=\"#{0}\" class=\"anchor\">§</a>\
</h2>{2}",
id, title, extra_content
)
}
fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) {
let name = m.name.unwrap();
info!("Documenting {name} on {ty_name:?}", ty_name = t.name);
@ -857,10 +846,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
if !required_types.is_empty() {
write_small_section_header(
write_section_heading(
w,
"required-associated-types",
"Required Associated Types",
"required-associated-types",
None,
"<div class=\"methods\">",
);
for t in required_types {
@ -869,10 +859,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w.write_str("</div>");
}
if !provided_types.is_empty() {
write_small_section_header(
write_section_heading(
w,
"provided-associated-types",
"Provided Associated Types",
"provided-associated-types",
None,
"<div class=\"methods\">",
);
for t in provided_types {
@ -882,10 +873,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
if !required_consts.is_empty() {
write_small_section_header(
write_section_heading(
w,
"required-associated-consts",
"Required Associated Constants",
"required-associated-consts",
None,
"<div class=\"methods\">",
);
for t in required_consts {
@ -894,10 +886,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w.write_str("</div>");
}
if !provided_consts.is_empty() {
write_small_section_header(
write_section_heading(
w,
"provided-associated-consts",
"Provided Associated Constants",
"provided-associated-consts",
None,
"<div class=\"methods\">",
);
for t in provided_consts {
@ -908,10 +901,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
// Output the documentation for each function individually
if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
write_small_section_header(
write_section_heading(
w,
"required-methods",
"Required Methods",
"required-methods",
None,
"<div class=\"methods\">",
);
@ -929,10 +923,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w.write_str("</div>");
}
if !provided_methods.is_empty() {
write_small_section_header(
write_section_heading(
w,
"provided-methods",
"Provided Methods",
"provided-methods",
None,
"<div class=\"methods\">",
);
for m in provided_methods {
@ -949,10 +944,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
let mut extern_crates = FxHashSet::default();
if !t.is_object_safe(cx.tcx()) {
write_small_section_header(
write_section_heading(
w,
"object-safety",
"Object Safety",
"object-safety",
None,
&format!(
"<div class=\"object-safety-info\">This trait is <b>not</b> \
<a href=\"{base}/reference/items/traits.html#object-safety\">\
@ -996,7 +992,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
foreign.sort_by_cached_key(|i| ImplString::new(i, cx));
if !foreign.is_empty() {
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "");
write_section_heading(w, "Implementations on Foreign Types", "foreign-impls", None, "");
for implementor in foreign {
let provided_methods = implementor.inner_impl().provided_trait_methods(tcx);
@ -1021,10 +1017,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
}
}
write_small_section_header(
write_section_heading(
w,
"implementors",
"Implementors",
"implementors",
None,
"<div id=\"implementors-list\">",
);
for implementor in concrete {
@ -1033,10 +1030,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
w.write_str("</div>");
if t.is_auto(tcx) {
write_small_section_header(
write_section_heading(
w,
"synthetic-implementors",
"Auto implementors",
"synthetic-implementors",
None,
"<div id=\"synthetic-implementors-list\">",
);
for implementor in synthetic {
@ -1054,18 +1052,20 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
} else {
// even without any implementations to write in, we still want the heading and list, so the
// implementors javascript file pulled in below has somewhere to write the impls into
write_small_section_header(
write_section_heading(
w,
"implementors",
"Implementors",
"implementors",
None,
"<div id=\"implementors-list\"></div>",
);
if t.is_auto(tcx) {
write_small_section_header(
write_section_heading(
w,
"synthetic-implementors",
"Auto implementors",
"synthetic-implementors",
None,
"<div id=\"synthetic-implementors-list\"></div>",
);
}
@ -1248,11 +1248,7 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
if let Some(inner_type) = &t.inner_type {
write!(
w,
"<h2 id=\"aliased-type\" class=\"section-header\">\
Aliased Type<a href=\"#aliased-type\" class=\"anchor\">§</a></h2>"
);
write_section_heading(w, "Aliased Type", "aliased-type", None, "");
match inner_type {
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
@ -1673,16 +1669,14 @@ fn item_variants(
enum_def_id: DefId,
) {
let tcx = cx.tcx();
write!(
write_section_heading(
w,
"<h2 id=\"variants\" class=\"variants section-header\">\
Variants{}<a href=\"#variants\" class=\"anchor\">§</a>\
</h2>\
{}\
<div class=\"variants\">",
document_non_exhaustive_header(it),
document_non_exhaustive(it)
&format!("Variants{}", document_non_exhaustive_header(it)),
"variants",
Some("variants"),
format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
);
let should_show_enum_discriminant = should_show_enum_discriminant(cx, enum_def_id, variants);
for (index, variant) in variants.iter_enumerated() {
if variant.is_stripped() {
@ -1930,16 +1924,12 @@ fn item_fields(
.peekable();
if let None | Some(CtorKind::Fn) = ctor_kind {
if fields.peek().is_some() {
write!(
w,
"<h2 id=\"fields\" class=\"fields section-header\">\
{}{}<a href=\"#fields\" class=\"anchor\">§</a>\
</h2>\
{}",
let title = format!(
"{}{}",
if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
write_section_heading(w, &title, "fields", Some("fields"), document_non_exhaustive(it));
for (index, (field, ty)) in fields.enumerate() {
let field_name =
field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());

View file

@ -849,11 +849,30 @@ nav.sub {
h2.section-header > .anchor {
padding-right: 6px;
}
a.doc-anchor {
color: var(--main-color);
display: none;
position: absolute;
left: -17px;
/* We add this padding so that when the cursor moves from the heading's text to the anchor,
the anchor doesn't disappear. */
padding-right: 5px;
/* And this padding is used to make the anchor larger and easier to click on. */
padding-left: 3px;
}
*:hover > .doc-anchor {
display: block;
}
/* If the first element of the top doc block is a heading, we don't want to ever display its anchor
because of the `[-]` element which would overlap with it. */
.top-doc > .docblock > *:first-child > .doc-anchor {
display: none !important;
}
.main-heading a:hover,
.example-wrap .rust a:hover,
.all-items a:hover,
.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
.docblock a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover:not(.doc-anchor),
.docblock-short a:not(.test-arrow):not(.scrape-help):not(.tooltip):hover,
.item-info a {
text-decoration: underline;

View file

@ -76,8 +76,7 @@ async fn uninhabited_variant() {
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
use std::task::{Context, Poll, Waker};
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
let mut pinned = Box::pin(fut);
loop {

View file

@ -93,8 +93,7 @@ fn dispatch_on_pin_mut() {
let mut fut = async_main();
// Poll loop, just to test the future...
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {

View file

@ -77,8 +77,7 @@ impl Future for DoStuff {
}
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
let mut pinned = pin!(fut);
loop {
@ -90,8 +89,7 @@ fn run_fut<T>(fut: impl Future<Output = T>) -> T {
}
fn self_referential_box() {
let waker = Waker::noop();
let cx = &mut Context::from_waker(&waker);
let cx = &mut Context::from_waker(Waker::noop());
async fn my_fut() -> i32 {
let val = 10;

View file

@ -6,8 +6,7 @@ use std::task::{Context, Poll, Waker};
pub fn fuzzing_block_on<O, F: Future<Output = O>>(fut: F) -> O {
let mut fut = std::pin::pin!(fut);
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(&mut context) {
Poll::Ready(v) => return v,

View file

@ -56,8 +56,7 @@ fn data_moved() {
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
use std::task::{Context, Poll, Waker};
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
let mut pinned = Box::pin(fut);
loop {

View file

@ -117,8 +117,7 @@
LL| | #[coverage(off)]
LL| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
LL| | let mut future = pin!(future);
LL| | let waker = Waker::noop();
LL| | let mut context = Context::from_waker(&waker);
LL| | let mut context = Context::from_waker(Waker::noop());
LL| |
LL| | loop {
LL| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -110,8 +110,7 @@ mod executor {
#[coverage(off)]
pub fn block_on<F: Future>(mut future: F) -> F::Output {
let mut future = pin!(future);
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
loop {
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -41,8 +41,7 @@
LL| | #[coverage(off)]
LL| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
LL| | let mut future = pin!(future);
LL| | let waker = Waker::noop();
LL| | let mut context = Context::from_waker(&waker);
LL| | let mut context = Context::from_waker(Waker::noop());
LL| |
LL| | loop {
LL| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -39,8 +39,7 @@ mod executor {
#[coverage(off)]
pub fn block_on<F: Future>(mut future: F) -> F::Output {
let mut future = pin!(future);
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
loop {
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -24,8 +24,7 @@
LL| | #[coverage(off)]
LL| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
LL| | let mut future = pin!(future);
LL| | let waker = Waker::noop();
LL| | let mut context = Context::from_waker(&waker);
LL| | let mut context = Context::from_waker(Waker::noop());
LL| |
LL| | loop {
LL| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -23,8 +23,7 @@ mod executor {
#[coverage(off)]
pub fn block_on<F: Future>(mut future: F) -> F::Output {
let mut future = pin!(future);
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
loop {
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -54,8 +54,7 @@
LL| | #[coverage(off)]
LL| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
LL| | let mut future = pin!(future);
LL| | let waker = Waker::noop();
LL| | let mut context = Context::from_waker(&waker);
LL| | let mut context = Context::from_waker(Waker::noop());
LL| |
LL| | loop {
LL| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -53,8 +53,7 @@ mod executor {
#[coverage(off)]
pub fn block_on<F: Future>(mut future: F) -> F::Output {
let mut future = pin!(future);
let waker = Waker::noop();
let mut context = Context::from_waker(&waker);
let mut context = Context::from_waker(Waker::noop());
loop {
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {

View file

@ -6,7 +6,7 @@ reload:
// We first check that the headers in the `.top-doc` doc block still have their
// bottom border.
assert-text: (".top-doc .docblock > h3", "Hello")
assert-text: (".top-doc .docblock > h3", "§Hello")
assert-css: (
".top-doc .docblock > h3",
{"border-bottom": "1px solid #d2d2d2"},

View file

@ -1,4 +1,4 @@
// This test check for headers text and background colors for the different themes.
// This test check for headings text and background colors for the different themes.
define-function: (
"check-colors",
@ -45,7 +45,7 @@ call-function: (
"color": "#c5c5c5",
"code_header_color": "#e6e1cf",
"focus_background_color": "rgba(255, 236, 164, 0.06)",
"headings_color": "#39afd7",
"headings_color": "#c5c5c5",
},
)
call-function: (
@ -55,7 +55,7 @@ call-function: (
"color": "#ddd",
"code_header_color": "#ddd",
"focus_background_color": "#494a3d",
"headings_color": "#d2991d",
"headings_color": "#ddd",
},
)
call-function: (
@ -65,6 +65,6 @@ call-function: (
"color": "black",
"code_header_color": "black",
"focus_background_color": "#fdffd3",
"headings_color": "#3873ad",
"headings_color": "black",
},
)

View file

@ -0,0 +1,32 @@
// Test to ensure that the headings anchor behave as expected.
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
show-text: true
define-function: (
"check-heading-anchor",
(heading_id),
block {
// The anchor should not be displayed by default.
assert-css: ("#" + |heading_id| + " .doc-anchor", { "display": "none" })
// We ensure that hovering the heading makes the anchor visible.
move-cursor-to: "#" + |heading_id|
assert-css: ("#" + |heading_id| + ":hover .doc-anchor", { "display": "block" })
// We then ensure that moving from the heading to the anchor doesn't make the anchor
// disappear.
move-cursor-to: "#" + |heading_id| + " .doc-anchor"
assert-css: ("#" + |heading_id| + " .doc-anchor:hover", {
"display": "block",
// We also ensure that there is no underline decoration.
"text-decoration-line": "none",
})
}
)
move-cursor-to: "#top-doc-prose-title"
// If the top documentation block first element is a heading, we should never display its anchor
// to prevent it from overlapping with the `[-]` element.
assert-css: ("#top-doc-prose-title:hover .doc-anchor", { "display": "none" })
call-function: ("check-heading-anchor", ("top-doc-prose-sub-heading"))
call-function: ("check-heading-anchor", ("top-doc-prose-sub-sub-heading"))
call-function: ("check-heading-anchor", ("you-know-the-drill"))

View file

@ -5,18 +5,23 @@
pub struct Foo;
impl Foo {
// @has - '//*[@id="examples"]//a' 'Examples'
// @has - '//*[@id="panics"]//a' 'Panics'
// @has - '//*[@id="examples"]' 'Examples'
// @has - '//*[@id="examples"]/a[@href="#examples"]' '§'
// @has - '//*[@id="panics"]' 'Panics'
// @has - '//*[@id="panics"]/a[@href="#panics"]' '§'
/// # Examples
/// # Panics
pub fn bar() {}
// @has - '//*[@id="examples-1"]//a' 'Examples'
// @has - '//*[@id="examples-1"]' 'Examples'
// @has - '//*[@id="examples-1"]/a[@href="#examples-1"]' '§'
/// # Examples
pub fn bar_1() {}
// @has - '//*[@id="examples-2"]//a' 'Examples'
// @has - '//*[@id="panics-1"]//a' 'Panics'
// @has - '//*[@id="examples-2"]' 'Examples'
// @has - '//*[@id="examples-2"]/a[@href="#examples-2"]' '§'
// @has - '//*[@id="panics-1"]' 'Panics'
// @has - '//*[@id="panics-1"]/a[@href="#panics-1"]' '§'
/// # Examples
/// # Panics
pub fn bar_2() {}

View file

@ -0,0 +1,14 @@
#![crate_name = "foo"]
//! # Heading with [a link](https://a.com) inside
//!
//! And even with
//!
//! ## [multiple](https://b.com) [links](https://c.com)
//!
//! !
// @has 'foo/index.html'
// @has - '//h2/a[@href="https://a.com"]' 'a link'
// @has - '//h3/a[@href="https://b.com"]' 'multiple'
// @has - '//h3/a[@href="https://c.com"]' 'links'

View file

@ -1,9 +1,12 @@
// It actually checks that the link is kept in the headings as expected now.
#![crate_name = "foo"]
// @has foo/fn.foo.html
// @!has - '//a[@href="http://a.a"]' ''
// @has - '//a[@href="#implementing-stuff-somewhere"]' 'Implementing stuff somewhere'
// @has - '//a[@href="#another-one-urg"]' 'Another one urg'
// @has - '//a[@href="http://a.a"]' 'stuff'
// @has - '//*[@id="implementing-stuff-somewhere"]' 'Implementing stuff somewhere'
// @has - '//a[@href="http://b.b"]' 'one'
// @has - '//*[@id="another-one-urg"]' 'Another one urg'
/// fooo
///
@ -13,5 +16,5 @@
///
/// # Another [one][two] urg
///
/// [two]: http://a.a
/// [two]: http://b.b
pub fn foo() {}

View file

@ -2,8 +2,9 @@
// @has foo/index.html '//*[@class="desc docblock-short"]' 'fooo'
// @!has foo/index.html '//*[@class="desc docblock-short"]/h1' 'fooo'
// @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' 'fooo'
// @has foo/fn.foo.html '//h2[@id="fooo"]' 'fooo'
// @has foo/fn.foo.html '//h2[@id="fooo"]/a[@href="#fooo"]' '§'
/// # fooo
///
/// foo
@ -11,8 +12,9 @@ pub fn foo() {}
// @has foo/index.html '//*[@class="desc docblock-short"]' 'mooood'
// @!has foo/index.html '//*[@class="desc docblock-short"]/h2' 'mooood'
// @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' 'mooood'
// @has foo/foo/index.html '//h3[@id="mooood"]' 'mooood'
// @has foo/foo/index.html '//h3[@id="mooood"]/a[@href="#mooood"]' '§'
/// ## mooood
///
/// foo mod

View file

@ -25,8 +25,7 @@ async fn real_main() {
fn main() {
let future = real_main();
let waker = std::task::Waker::noop();
let mut cx = &mut core::task::Context::from_waker(&waker);
let mut cx = &mut core::task::Context::from_waker(std::task::Waker::noop());
let mut future = core::pin::pin!(future);
while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {}
}

View file

@ -17,8 +17,7 @@ async fn real_main() {
fn main() {
let future = real_main();
let waker = std::task::Waker::noop();
let mut cx = &mut core::task::Context::from_waker(&waker);
let mut cx = &mut core::task::Context::from_waker(std::task::Waker::noop());
let mut future = core::pin::pin!(future);
while let core::task::Poll::Pending = future.as_mut().poll(&mut cx) {}
}

View file

@ -40,8 +40,7 @@ fn main() {
let mut fut = pin!(async_main());
// Poll loop, just to test the future...
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(ctx) {

View file

@ -43,8 +43,7 @@ fn main() {
let mut fut = pin!(async_main());
// Poll loop, just to test the future...
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(ctx) {

View file

@ -21,7 +21,7 @@ LL | default async fn foo(_: T) -> &'static str {
= note: specialization behaves in inconsistent and surprising ways with async functions in traits, and for now is disallowed
error[E0599]: no method named `poll` found for struct `Pin<&mut impl Future<Output = ()>>` in the current scope
--> $DIR/dont-project-to-specializable-projection.rs:50:28
--> $DIR/dont-project-to-specializable-projection.rs:49:28
|
LL | match fut.as_mut().poll(ctx) {
| ^^^^ method not found in `Pin<&mut impl Future<Output = ()>>`

View file

@ -11,7 +11,6 @@ async gen fn gen_fn() -> &'static str {
pub fn main() {
let async_iterator = pin!(gen_fn());
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
async_iterator.poll_next(ctx);
}

View file

@ -74,8 +74,7 @@ fn main() {
let mut fut = pin!(async_main());
// Poll loop, just to test the future...
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(ctx) {

View file

@ -19,15 +19,14 @@ async fn async_main() {
// ------------------------------------------------------------------------- //
// Implementation Details Below...
use std::task::*;
use std::pin::pin;
use std::task::*;
fn main() {
let mut fut = pin!(async_main());
// Poll loop, just to test the future...
let waker = Waker::noop();
let ctx = &mut Context::from_waker(&waker);
let ctx = &mut Context::from_waker(Waker::noop());
loop {
match fut.as_mut().poll(ctx) {

View file

@ -1,73 +0,0 @@
#![feature(never_patterns)]
#![allow(incomplete_features)]
enum Void {}
fn main() {}
// The classic use for empty types.
fn safe_unwrap_result<T>(res: Result<T, Void>) {
let Ok(_x) = res; //~ ERROR refutable pattern in local binding
let (Ok(_x) | Err(!)) = &res;
let (Ok(_x) | Err(&!)) = res.as_ref();
}
// Check we only accept `!` where we want to.
fn never_pattern_location(void: Void) {
// FIXME(never_patterns): Don't accept on a non-empty type.
match Some(0) {
None => {}
Some(!),
}
// FIXME(never_patterns): Don't accept on an arbitrary type, even if there are no more branches.
match () {
() => {}
!,
}
// FIXME(never_patterns): Don't accept even on an empty branch.
match None::<Void> {
None => {}
!,
}
// FIXME(never_patterns): Let alone if the emptiness is behind a reference.
match None::<&Void> {
None => {}
!,
}
// Participate in match ergonomics.
match &void {
!
}
match &&void {
!
}
match &&void {
&!
}
match &None::<Void> {
None => {}
Some(!)
}
match None::<&Void> {
None => {}
Some(!),
}
// Accept on a composite empty type.
match None::<&(u32, Void)> {
None => {}
Some(&!),
}
// Accept on an simple empty type.
match None::<Void> {
None => {}
Some(!),
}
match None::<&Void> {
None => {}
Some(&!),
}
match None::<&(u32, Void)> {
None => {}
Some(&(_, !)),
}
}

View file

@ -1,17 +0,0 @@
error[E0005]: refutable pattern in local binding
--> $DIR/never_patterns.rs:10:9
|
LL | let Ok(_x) = res;
| ^^^^^^ pattern `Err(_)` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Result<T, Void>`
help: you might want to use `let else` to handle the variant that isn't matched
|
LL | let Ok(_x) = res else { todo!() };
| ++++++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0005`.

View file

@ -0,0 +1,66 @@
error: mismatched types
--> $DIR/typeck.rs:25:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `()`
error: mismatched types
--> $DIR/typeck.rs:29:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `(i32, bool)`
error: mismatched types
--> $DIR/typeck.rs:33:13
|
LL | (_, !),
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `bool`
error: mismatched types
--> $DIR/typeck.rs:38:14
|
LL | Some(!),
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `i32`
error: mismatched types
--> $DIR/typeck.rs:45:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `()`
error: mismatched types
--> $DIR/typeck.rs:52:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `Option<Void>`
error: mismatched types
--> $DIR/typeck.rs:57:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `[Void]`
error: mismatched types
--> $DIR/typeck.rs:63:9
|
LL | !,
| ^ a never pattern must be used on an uninhabited type
|
= note: the matched value is of type `Option<&Void>`
error: aborting due to 8 previous errors

View file

@ -0,0 +1,125 @@
// revisions: pass fail
//[pass] check-pass
//[fail] check-fail
#![feature(never_patterns)]
#![feature(exhaustive_patterns)]
#![allow(incomplete_features)]
#[derive(Copy, Clone)]
enum Void {}
fn main() {}
// The classic use for empty types.
fn safe_unwrap_result<T: Copy>(res: Result<T, Void>) {
let Ok(_x) = res;
let (Ok(_x) | Err(!)) = &res;
let (Ok(_x) | Err(!)) = res.as_ref();
}
// Check we only accept `!` where we want to.
#[cfg(fail)]
fn never_pattern_typeck_fail(void: Void) {
// Don't accept on a non-empty type.
match () {
!,
//[fail]~^ ERROR: mismatched types
}
match (0, false) {
!,
//[fail]~^ ERROR: mismatched types
}
match (0, false) {
(_, !),
//[fail]~^ ERROR: mismatched types
}
match Some(0) {
None => {}
Some(!),
//[fail]~^ ERROR: mismatched types
}
// Don't accept on an arbitrary type, even if there are no more branches.
match () {
() => {}
!,
//[fail]~^ ERROR: mismatched types
}
// Don't accept even on an empty branch.
match None::<Void> {
None => {}
!,
//[fail]~^ ERROR: mismatched types
}
match (&[] as &[Void]) {
[] => {}
!,
//[fail]~^ ERROR: mismatched types
}
// Let alone if the emptiness is behind a reference.
match None::<&Void> {
None => {}
!,
//[fail]~^ ERROR: mismatched types
}
}
#[cfg(pass)]
fn never_pattern_typeck_pass(void: Void) {
// Participate in match ergonomics.
match &void {
!,
}
match &&void {
!,
}
match &&void {
&!,
}
match &None::<Void> {
None => {}
Some(!),
}
match None::<&Void> {
None => {}
Some(!),
}
// Accept on a directly empty type.
match void {
!,
}
match &void {
&!,
}
match None::<Void> {
None => {}
Some(!),
}
match None::<&Void> {
None => {}
Some(&!),
}
match None::<&(u32, Void)> {
None => {}
Some(&(_, !)),
}
match (&[] as &[Void]) {
[] => {}
[!],
}
// Accept on a composite empty type.
match None::<&(u32, Void)> {
None => {}
Some(&!),
}
match None::<&(u32, Void)> {
None => {}
Some(!),
}
match None::<&Result<Void, Void>> {
None => {}
Some(!),
}
}

View file

@ -0,0 +1,9 @@
// run-rustfix
#![allow(dead_code)]
fn swap(arr: &mut [u32; 2]) {
arr.swap(1, 0);
//~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
}
fn main() {}

View file

@ -0,0 +1,9 @@
// run-rustfix
#![allow(dead_code)]
fn swap(arr: &mut [u32; 2]) {
std::mem::swap(&mut arr[0], &mut arr[1]);
//~^ ERROR cannot borrow `arr[_]` as mutable more than once at a time
}
fn main() {}

View file

@ -0,0 +1,17 @@
error[E0499]: cannot borrow `arr[_]` as mutable more than once at a time
--> $DIR/suggest-slice-swap.rs:5:33
|
LL | std::mem::swap(&mut arr[0], &mut arr[1]);
| -------------- ----------- ^^^^^^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
|
help: use `.swap()` to swap elements at the specified indices instead
|
LL | arr.swap(1, 0);
| ~~~~~~~~~~~~~~
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0499`.

View file

@ -364,7 +364,8 @@ new_pr = true
[autolabel."needs-triage"]
new_issue = true
exclude_labels = [
"C-tracking-issue"
"C-tracking-issue",
"A-diagnostics",
]
[autolabel."WG-trait-system-refactor"]