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:
commit
128148d4cf
64 changed files with 745 additions and 593 deletions
16
Cargo.lock
16
Cargo.lock
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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`].
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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][..];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!?!& -<em>qux</em>-%</a>\
|
||||
<a class=\"doc-anchor\" href=\"#foo-bar-baz--qux-\">§</a>\
|
||||
<strong>Foo</strong> <em>bar</em> baz!?!& -<em>qux</em>-%\
|
||||
</h4>",
|
||||
);
|
||||
t(
|
||||
"#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux",
|
||||
"<h5 id=\"foo--bar--baz--qux\">\
|
||||
<a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> & *bar?!* \
|
||||
<em><code>baz</code></em> ❤ #qux</a>\
|
||||
<a class=\"doc-anchor\" href=\"#foo--bar--baz--qux\">§</a>\
|
||||
<strong>Foo?</strong> & *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]
|
||||
|
|
|
@ -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<'_>,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) } {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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",
|
||||
},
|
||||
)
|
||||
|
|
32
tests/rustdoc-gui/headings-anchor.goml
Normal file
32
tests/rustdoc-gui/headings-anchor.goml
Normal 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"))
|
|
@ -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() {}
|
||||
|
|
14
tests/rustdoc/links-in-headings.rs
Normal file
14
tests/rustdoc/links-in-headings.rs
Normal 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'
|
|
@ -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() {}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 = ()>>`
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(&(_, !)),
|
||||
}
|
||||
}
|
|
@ -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`.
|
66
tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
Normal file
66
tests/ui/rfcs/rfc-0000-never_patterns/typeck.fail.stderr
Normal 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
|
||||
|
125
tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
Normal file
125
tests/ui/rfcs/rfc-0000-never_patterns/typeck.rs
Normal 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(!),
|
||||
}
|
||||
}
|
9
tests/ui/suggestions/suggest-slice-swap.fixed
Normal file
9
tests/ui/suggestions/suggest-slice-swap.fixed
Normal 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() {}
|
9
tests/ui/suggestions/suggest-slice-swap.rs
Normal file
9
tests/ui/suggestions/suggest-slice-swap.rs
Normal 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() {}
|
17
tests/ui/suggestions/suggest-slice-swap.stderr
Normal file
17
tests/ui/suggestions/suggest-slice-swap.stderr
Normal 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`.
|
|
@ -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"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue