diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 92d2d04f23f23..bc740de515065 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -117,9 +117,29 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } - assert!(body.yield_ty().is_some() == universal_regions.yield_ty.is_some()); - if let Some(mir_yield_ty) = body.yield_ty() { - let ur_yield_ty = universal_regions.yield_ty.unwrap(); + debug!( + "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}", + body.yield_ty(), + universal_regions.yield_ty + ); + + // We will not have a universal_regions.yield_ty if we yield (by accident) + // outside of a generator and return an `impl Trait`, so emit a delay_span_bug + // because we don't want to panic in an assert here if we've already got errors. + if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() { + self.tcx().sess.delay_span_bug( + body.span, + &format!( + "Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})", + body.yield_ty(), + universal_regions.yield_ty, + ), + ); + } + + if let (Some(mir_yield_ty), Some(ur_yield_ty)) = + (body.yield_ty(), universal_regions.yield_ty) + { let yield_span = body.local_decls[RETURN_PLACE].source_info.span; self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty, yield_span); } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index f8491654f39e3..537a10e98e59d 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -1027,6 +1027,24 @@ fn check_matcher_core( ), ); err.span_label(sp, format!("not allowed after `{}` fragments", kind)); + + if kind == NonterminalKind::PatWithOr + && sess.edition == Edition::Edition2021 + && next_token.is_token(&BinOp(token::BinOpToken::Or)) + { + let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( + span, + name, + Some(NonterminalKind::PatParam { inferred: false }), + )); + err.span_suggestion( + span, + &format!("try a `pat_param` fragment specifier instead"), + suggestion, + Applicability::MaybeIncorrect, + ); + } + let msg = "allowed there are: "; match possible { &[] => {} diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index d8883b0e66dba..4a24f803e8463 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -79,9 +79,8 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { let receiver_ty = cx.typeck_results().expr_ty(receiver_arg); let adjustments = cx.typeck_results().expr_adjustments(receiver_arg); - let target = match adjustments.last() { - Some(Adjustment { kind: Adjust::Borrow(_), target }) => target, - _ => return, + let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else { + return }; let types = diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 077d3e1c82058..5dbcc1655c906 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -609,14 +609,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { // If the trait is private, add the impl items to `private_traits` so they don't get // reported for missing docs. let real_trait = trait_ref.path.res.def_id(); - if let Some(def_id) = real_trait.as_local() { - let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); - if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) { - if let hir::VisibilityKind::Inherited = item.vis.node { - for impl_item_ref in items { - self.private_traits.insert(impl_item_ref.id.hir_id()); - } - } + let Some(def_id) = real_trait.as_local() else { return }; + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id); + let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) else { return }; + if let hir::VisibilityKind::Inherited = item.vis.node { + for impl_item_ref in items { + self.private_traits.insert(impl_item_ref.id.hir_id()); } } return; @@ -829,9 +827,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { _ => return, } - let debug = match cx.tcx.get_diagnostic_item(sym::Debug) { - Some(debug) => debug, - None => return, + let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { + return }; if self.impling_types.is_none() { @@ -1509,9 +1506,8 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let (ty, type_alias_generics) = match item.kind { - hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics), - _ => return, + let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else { + return }; if let hir::TyKind::OpaqueDef(..) = ty.kind { // Bounds are respected for `type X = impl Trait` @@ -2266,16 +2262,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // and should check for them here. match predicate.bounded_ty.kind { hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { - if let Res::Def(DefKind::TyParam, def_id) = path.res { - let index = ty_generics.param_def_id_to_index[&def_id]; - ( - Self::lifetimes_outliving_type(inferred_outlives, index), - &predicate.bounds, - predicate.span, - ) - } else { - continue; - } + let Res::Def(DefKind::TyParam, def_id) = path.res else { + continue + }; + let index = ty_generics.param_def_id_to_index[&def_id]; + ( + Self::lifetimes_outliving_type(inferred_outlives, index), + &predicate.bounds, + predicate.span, + ) } _ => { continue; @@ -3216,18 +3211,17 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { for (idx, _) in statement.match_indices(':') { let possible_label = statement[start_idx..idx].trim(); let mut chars = possible_label.chars(); - if let Some(c) = chars.next() { - // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $ - if (c.is_alphabetic() || matches!(c, '.' | '_')) - && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$')) - { - found_labels.push(possible_label); - } else { - // If we encounter a non-label, there cannot be any further labels, so stop checking - break; - } - } else { + let Some(c) = chars.next() else { // Empty string means a leading ':' in this section, which is not a label + break + }; + // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $ + if (c.is_alphabetic() || matches!(c, '.' | '_')) + && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$')) + { + found_labels.push(possible_label); + } else { + // If we encounter a non-label, there cannot be any further labels, so stop checking break; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index b6d66eb12d083..485728cbfd39d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -227,14 +227,12 @@ impl<'s> LintLevelsBuilder<'s> { let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); for attr in attrs { - let level = match Level::from_symbol(attr.name_or_empty()) { - None => continue, - Some(lvl) => lvl, + let Some(level) = Level::from_symbol(attr.name_or_empty()) else { + continue }; - let mut metas = match attr.meta_item_list() { - Some(x) => x, - None => continue, + let Some(mut metas) = attr.meta_item_list() else { + continue }; if metas.is_empty() { @@ -481,9 +479,8 @@ impl<'s> LintLevelsBuilder<'s> { continue; } - let (lint_attr_name, lint_attr_span) = match *src { - LintLevelSource::Node(name, span, _) => (name, span), - _ => continue, + let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else { + continue }; let lint = builtin::UNUSED_ATTRIBUTES; diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index a3a3cd0077dbe..600504f7c1280 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -40,9 +40,8 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]); impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // We only care about method calls. - let (call, elements) = match expr.kind { - ExprKind::MethodCall(call, _, elements, _) => (call, elements), - _ => return, + let ExprKind::MethodCall(call, _, elements, _) = &expr.kind else { + return }; // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow` // traits and ignore any other method call. @@ -70,9 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { } let param_env = cx.tcx.param_env(trait_id); // Resolve the trait method instance. - let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) { - Ok(Some(i)) => i, - _ => return, + let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else { + return }; // (Re)check that it implements the noop diagnostic. let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return }; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 5435ff1396de8..dafff640b36ef 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -91,9 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { let predicates = cx.tcx.explicit_predicates_of(item.def_id); for &(predicate, span) in predicates.predicates { - let trait_predicate = match predicate.kind().skip_binder() { - Trait(trait_predicate) => trait_predicate, - _ => continue, + let Trait(trait_predicate) = predicate.kind().skip_binder() else { + continue }; if trait_predicate.constness == ty::BoundConstness::ConstIfConst { // `~const Drop` definitely have meanings so avoid linting here. @@ -106,9 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { continue; } cx.struct_span_lint(DROP_BOUNDS, span, |lint| { - let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) { - Some(needs_drop) => needs_drop, - None => return, + let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { + return }; let msg = format!( "bounds on `{}` are most likely incorrect, consider instead \ @@ -123,17 +121,15 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { } fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { - let bounds = match &ty.kind { - hir::TyKind::TraitObject(bounds, _lifetime, _syntax) => bounds, - _ => return, + let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { + return }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); if cx.tcx.lang_items().drop_trait() == def_id { cx.struct_span_lint(DYN_DROP, bound.span, |lint| { - let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) { - Some(needs_drop) => needs_drop, - None => return, + let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { + return }; let msg = format!( "types that do not implement `Drop` can still have drop glue, consider \ diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b20f7357b35b8..32ed6dad7f8c3 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1342,11 +1342,10 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { | ty::layout::LayoutError::NormalizationFailure(_, _), ) => return, }; - let (variants, tag) = match layout.variants { - Variants::Multiple { + let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, ref variants, .. - } => (variants, tag), - _ => return, + } = &layout.variants else { + return }; let tag_size = tag.value.size(&cx.tcx).bytes(); diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 69eb73b42552c..771ce2eb884af 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -64,6 +64,30 @@ bitflags! { /// Moreover, Rust only allows recursive data types through indirection. /// /// [adt]: https://en.wikipedia.org/wiki/Algebraic_data_type +/// +/// # Recursive types +/// +/// It may seem impossible to represent recursive types using [`Ty`], +/// since [`TyKind::Adt`] includes [`AdtDef`], which includes its fields, +/// creating a cycle. However, `AdtDef` does not actually include the *types* +/// of its fields; it includes just their [`DefId`]s. +/// +/// [`TyKind::Adt`]: ty::TyKind::Adt +/// +/// For example, the following type: +/// +/// ``` +/// struct S { x: Box } +/// ``` +/// +/// is essentially represented with [`Ty`] as the following pseudocode: +/// +/// ``` +/// struct S { x } +/// ``` +/// +/// where `x` here represents the `DefId` of `S.x`. Then, the `DefId` +/// can be used with [`TyCtxt::type_of()`] to get the type of the field. pub struct AdtDef { /// The `DefId` of the struct, enum or union item. pub did: DefId, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5070e1565b3d4..7e1804673df2c 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1711,7 +1711,7 @@ impl ReprOptions { impl<'tcx> FieldDef { /// Returns the type of this field. The resulting type is not normalized. The `subst` is - /// typically obtained via the second field of `TyKind::AdtDef`. + /// typically obtained via the second field of [`TyKind::Adt`]. pub fn ty(&self, tcx: TyCtxt<'tcx>, subst: SubstsRef<'tcx>) -> Ty<'tcx> { tcx.type_of(self.did).subst(tcx, subst) } diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 12cd7ad184892..b210c78cae007 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -1223,7 +1223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Missing try_into implementation for `{integer}` to `{float}` err.multipart_suggestion_verbose( &format!( - "{}, producing the floating point representation of the integer, + "{}, producing the floating point representation of the integer, \ rounded if necessary", cast_msg, ), diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 0b72b3f0ee7b0..ab41f5646e5e5 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1518,8 +1518,6 @@ impl TryFrom> for Box<[T; N]> { } impl Box { - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. /// /// # Examples @@ -1537,21 +1535,48 @@ impl Box { /// print_if_string(Box::new(my_string)); /// print_if_string(Box::new(0i8)); /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn downcast(self) -> Result, Self> { - if self.is::() { - unsafe { - let (raw, alloc): (*mut dyn Any, _) = Box::into_raw_with_allocator(self); - Ok(Box::from_raw_in(raw as *mut T, alloc)) - } - } else { - Err(self) + if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } + } + + /// Downcasts the box to a concrete type. + /// + /// For a safe alternative see [`downcast`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + /// + /// [`downcast`]: Self::downcast + #[inline] + #[unstable(feature = "downcast_unchecked", issue = "90850")] + pub unsafe fn downcast_unchecked(self) -> Box { + debug_assert!(self.is::()); + unsafe { + let (raw, alloc): (*mut dyn Any, _) = Box::into_raw_with_allocator(self); + Box::from_raw_in(raw as *mut T, alloc) } } } impl Box { - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. /// /// # Examples @@ -1569,21 +1594,48 @@ impl Box { /// print_if_string(Box::new(my_string)); /// print_if_string(Box::new(0i8)); /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn downcast(self) -> Result, Self> { - if self.is::() { - unsafe { - let (raw, alloc): (*mut (dyn Any + Send), _) = Box::into_raw_with_allocator(self); - Ok(Box::from_raw_in(raw as *mut T, alloc)) - } - } else { - Err(self) + if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } + } + + /// Downcasts the box to a concrete type. + /// + /// For a safe alternative see [`downcast`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + /// + /// [`downcast`]: Self::downcast + #[inline] + #[unstable(feature = "downcast_unchecked", issue = "90850")] + pub unsafe fn downcast_unchecked(self) -> Box { + debug_assert!(self.is::()); + unsafe { + let (raw, alloc): (*mut (dyn Any + Send), _) = Box::into_raw_with_allocator(self); + Box::from_raw_in(raw as *mut T, alloc) } } } impl Box { - #[inline] - #[stable(feature = "box_send_sync_any_downcast", since = "1.51.0")] /// Attempt to downcast the box to a concrete type. /// /// # Examples @@ -1601,15 +1653,44 @@ impl Box { /// print_if_string(Box::new(my_string)); /// print_if_string(Box::new(0i8)); /// ``` + #[inline] + #[stable(feature = "box_send_sync_any_downcast", since = "1.51.0")] pub fn downcast(self) -> Result, Self> { - if self.is::() { - unsafe { - let (raw, alloc): (*mut (dyn Any + Send + Sync), _) = - Box::into_raw_with_allocator(self); - Ok(Box::from_raw_in(raw as *mut T, alloc)) - } - } else { - Err(self) + if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } + } + + /// Downcasts the box to a concrete type. + /// + /// For a safe alternative see [`downcast`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + /// + /// [`downcast`]: Self::downcast + #[inline] + #[unstable(feature = "downcast_unchecked", issue = "90850")] + pub unsafe fn downcast_unchecked(self) -> Box { + debug_assert!(self.is::()); + unsafe { + let (raw, alloc): (*mut (dyn Any + Send + Sync), _) = + Box::into_raw_with_allocator(self); + Box::from_raw_in(raw as *mut T, alloc) } } } diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 1fd5aa27fce46..72528185707a6 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -164,7 +164,7 @@ impl fmt::Debug for dyn Any + Send + Sync { } impl dyn Any { - /// Returns `true` if the boxed type is the same as `T`. + /// Returns `true` if the inner type is the same as `T`. /// /// # Examples /// @@ -195,7 +195,7 @@ impl dyn Any { t == concrete } - /// Returns some reference to the boxed value if it is of type `T`, or + /// Returns some reference to the inner value if it is of type `T`, or /// `None` if it isn't. /// /// # Examples @@ -221,13 +221,13 @@ impl dyn Any { // SAFETY: just checked whether we are pointing to the correct type, and we can rely on // that check for memory safety because we have implemented Any for all types; no other // impls can exist as they would conflict with our impl. - unsafe { Some(&*(self as *const dyn Any as *const T)) } + unsafe { Some(self.downcast_ref_unchecked()) } } else { None } } - /// Returns some mutable reference to the boxed value if it is of type `T`, or + /// Returns some mutable reference to the inner value if it is of type `T`, or /// `None` if it isn't. /// /// # Examples @@ -257,15 +257,73 @@ impl dyn Any { // SAFETY: just checked whether we are pointing to the correct type, and we can rely on // that check for memory safety because we have implemented Any for all types; no other // impls can exist as they would conflict with our impl. - unsafe { Some(&mut *(self as *mut dyn Any as *mut T)) } + unsafe { Some(self.downcast_mut_unchecked()) } } else { None } } + + /// Returns a reference to the inner value as type `dyn T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_ref_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_ref_unchecked(&self) -> &T { + debug_assert!(self.is::()); + // SAFETY: caller guarantees that T is the correct type + unsafe { &*(self as *const dyn Any as *const T) } + } + + /// Returns a mutable reference to the inner value as type `dyn T`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let mut x: Box = Box::new(1_usize); + /// + /// unsafe { + /// *x.downcast_mut_unchecked::() += 1; + /// } + /// + /// assert_eq!(*x.downcast_ref::().unwrap(), 2); + /// ``` + /// + /// # Safety + /// + /// The contained value must be of type `T`. Calling this method + /// with the incorrect type is *undefined behavior*. + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + debug_assert!(self.is::()); + // SAFETY: caller guarantees that T is the correct type + unsafe { &mut *(self as *mut dyn Any as *mut T) } + } } impl dyn Any + Send { - /// Forwards to the method defined on the type `Any`. + /// Forwards to the method defined on the type `dyn Any`. /// /// # Examples /// @@ -289,7 +347,7 @@ impl dyn Any + Send { ::is::(self) } - /// Forwards to the method defined on the type `Any`. + /// Forwards to the method defined on the type `dyn Any`. /// /// # Examples /// @@ -313,7 +371,7 @@ impl dyn Any + Send { ::downcast_ref::(self) } - /// Forwards to the method defined on the type `Any`. + /// Forwards to the method defined on the type `dyn Any`. /// /// # Examples /// @@ -340,6 +398,60 @@ impl dyn Any + Send { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } + + /// Forwards to the method defined on the type `dyn Any`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_ref_unchecked::(), 1); + /// } + /// ``` + /// + /// # Safety + /// + /// Same as the method on the type `dyn Any`. + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_ref_unchecked(&self) -> &T { + // SAFETY: guaranteed by caller + unsafe { ::downcast_ref_unchecked::(self) } + } + + /// Forwards to the method defined on the type `dyn Any`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let mut x: Box = Box::new(1_usize); + /// + /// unsafe { + /// *x.downcast_mut_unchecked::() += 1; + /// } + /// + /// assert_eq!(*x.downcast_ref::().unwrap(), 2); + /// ``` + /// + /// # Safety + /// + /// Same as the method on the type `dyn Any`. + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + // SAFETY: guaranteed by caller + unsafe { ::downcast_mut_unchecked::(self) } + } } impl dyn Any + Send + Sync { @@ -418,6 +530,52 @@ impl dyn Any + Send + Sync { pub fn downcast_mut(&mut self) -> Option<&mut T> { ::downcast_mut::(self) } + + /// Forwards to the method defined on the type `Any`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let x: Box = Box::new(1_usize); + /// + /// unsafe { + /// assert_eq!(*x.downcast_ref_unchecked::(), 1); + /// } + /// ``` + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_ref_unchecked(&self) -> &T { + // SAFETY: guaranteed by caller + unsafe { ::downcast_ref_unchecked::(self) } + } + + /// Forwards to the method defined on the type `Any`. + /// + /// # Examples + /// + /// ``` + /// #![feature(downcast_unchecked)] + /// + /// use std::any::Any; + /// + /// let mut x: Box = Box::new(1_usize); + /// + /// unsafe { + /// *x.downcast_mut_unchecked::() += 1; + /// } + /// + /// assert_eq!(*x.downcast_ref::().unwrap(), 2); + /// ``` + #[unstable(feature = "downcast_unchecked", issue = "90850")] + #[inline] + pub unsafe fn downcast_mut_unchecked(&mut self) -> &mut T { + // SAFETY: guaranteed by caller + unsafe { ::downcast_mut_unchecked::(self) } + } } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py index 8647db5a45dc8..48a341ffe0837 100644 --- a/src/etc/htmldocck.py +++ b/src/etc/htmldocck.py @@ -90,10 +90,20 @@ highlights for example. If you want to simply check for the presence of a given node or attribute, use an empty string (`""`) as a `PATTERN`. -* `@count PATH XPATH COUNT' checks for the occurrence of the given XPath +* `@count PATH XPATH COUNT` checks for the occurrence of the given XPath in the specified file. The number of occurrences must match the given count. +* `@snapshot NAME PATH XPATH` creates a snapshot test named NAME. + A snapshot test captures a subtree of the DOM, at the location + determined by the XPath, and compares it to a pre-recorded value + in a file. The file's name is the test's name with the `.rs` extension + replaced with `.NAME.html`, where NAME is the snapshot's name. + + htmldocck supports the `--bless` option to accept the current subtree + as expected, saving it to the file determined by the snapshot's name. + compiletest's `--bless` flag is forwarded to htmldocck. + * `@has-dir PATH` checks for the existence of the given directory. All conditions can be negated with `!`. `@!has foo/type.NoSuch.html` @@ -137,6 +147,10 @@ channel = os.environ["DOC_RUST_LANG_ORG_CHANNEL"] +# Initialized in main +rust_test_path = None +bless = None + class CustomHTMLParser(HTMLParser): """simplified HTML parser. @@ -387,6 +401,32 @@ def get_tree_count(tree, path): return len(tree.findall(path)) +def check_snapshot(snapshot_name, tree): + assert rust_test_path.endswith('.rs') + snapshot_path = '{}.{}.{}'.format(rust_test_path[:-3], snapshot_name, 'html') + try: + with open(snapshot_path, 'r') as snapshot_file: + expected_str = snapshot_file.read() + except FileNotFoundError: + if bless: + expected_str = None + else: + raise FailedCheck('No saved snapshot value') + + actual_str = ET.tostring(tree).decode('utf-8') + + if expected_str != actual_str: + if bless: + with open(snapshot_path, 'w') as snapshot_file: + snapshot_file.write(actual_str) + else: + print('--- expected ---\n') + print(expected_str) + print('\n\n--- actual ---\n') + print(actual_str) + print() + raise FailedCheck('Actual snapshot value is different than expected') + def stderr(*args): if sys.version_info.major < 3: file = codecs.getwriter('utf-8')(sys.stderr) @@ -448,6 +488,28 @@ def check_command(c, cache): ret = expected == found else: raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) + + elif c.cmd == 'snapshot': # snapshot test + if len(c.args) == 3: # @snapshot + [snapshot_name, html_path, pattern] = c.args + tree = cache.get_tree(html_path) + xpath = normalize_xpath(pattern) + subtrees = tree.findall(xpath) + if len(subtrees) == 1: + [subtree] = subtrees + try: + check_snapshot(snapshot_name, subtree) + ret = True + except FailedCheck as err: + cerr = str(err) + ret = False + elif len(subtrees) == 0: + raise FailedCheck('XPATH did not match') + else: + raise FailedCheck('Expected 1 match, but found {}'.format(len(subtrees))) + else: + raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) + elif c.cmd == 'has-dir': # has-dir test if len(c.args) == 1: # @has-dir = has-dir test try: @@ -458,11 +520,13 @@ def check_command(c, cache): ret = False else: raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd)) + elif c.cmd == 'valid-html': raise InvalidCheck('Unimplemented @valid-html') elif c.cmd == 'valid-links': raise InvalidCheck('Unimplemented @valid-links') + else: raise InvalidCheck('Unrecognized @{}'.format(c.cmd)) @@ -483,11 +547,19 @@ def check(target, commands): if __name__ == '__main__': - if len(sys.argv) != 3: - stderr('Usage: {}