diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index b143e28c5f9cc..d60122fccee21 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -390,7 +390,7 @@ impl<'ll> CodegenCx<'ll, '_> { let val_llty = self.val_ty(v); let g = self.get_static_inner(def_id, val_llty); - let llty = self.val_ty(g); + let llty = llvm::LLVMGlobalGetValueType(g); let g = if val_llty == llty { g diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 138cc3219aa08..3bf4d4964082f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -974,6 +974,7 @@ unsafe extern "C" { pub fn LLVMGetAlignment(Global: &Value) -> c_uint; pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint); pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass); + pub fn LLVMGlobalGetValueType(Global: &Value) -> &Type; // Operations on global variables pub fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>; diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 8824e1dfe50d8..c43c650a9f912 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -1,19 +1,29 @@ -use rustc_data_structures::fx::FxIndexSet; +use std::assert_matches::debug_assert_matches; +use std::cell::LazyCell; + +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::LintDiagnostic; -use rustc_middle::bug; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; +use rustc_middle::ty::relate::{ + structurally_relate_consts, structurally_relate_tys, Relate, RelateResult, TypeRelation, +}; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; +use rustc_middle::{bug, span_bug}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::edition::Edition; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; +use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt; +use rustc_trait_selection::traits::ObligationCtxt; use crate::{fluent_generated as fluent, LateContext, LateLintPass}; @@ -119,20 +129,41 @@ impl<'tcx> LateLintPass<'tcx> for ImplTraitOvercaptures { } } +#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] +enum ParamKind { + // Early-bound var. + Early(Symbol, u32), + // Late-bound var on function, not within a binder. We can capture these. + Free(DefId, Symbol), + // Late-bound var in a binder. We can't capture these yet. + Late, +} + fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { let sig = tcx.fn_sig(parent_def_id).instantiate_identity(); - let mut in_scope_parameters = FxIndexSet::default(); + let mut in_scope_parameters = FxIndexMap::default(); // Populate the in_scope_parameters list first with all of the generics in scope let mut current_def_id = Some(parent_def_id.to_def_id()); while let Some(def_id) = current_def_id { let generics = tcx.generics_of(def_id); for param in &generics.own_params { - in_scope_parameters.insert(param.def_id); + in_scope_parameters.insert(param.def_id, ParamKind::Early(param.name, param.index)); } current_def_id = generics.parent; } + for bound_var in sig.bound_vars() { + let ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, name)) = bound_var + else { + span_bug!(tcx.def_span(parent_def_id), "unexpected non-lifetime binder on fn sig"); + }; + + in_scope_parameters.insert(def_id, ParamKind::Free(def_id, name)); + } + + let sig = tcx.liberate_late_bound_regions(parent_def_id.to_def_id(), sig); + // Then visit the signature to walk through all the binders (incl. the late-bound // vars on the function itself, which we need to count too). sig.visit_with(&mut VisitOpaqueTypes { @@ -140,21 +171,45 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) { parent_def_id, in_scope_parameters, seen: Default::default(), + // Lazily compute these two, since they're likely a bit expensive. + variances: LazyCell::new(|| { + let mut functional_variances = FunctionalVariances { + tcx: tcx, + variances: FxHashMap::default(), + ambient_variance: ty::Covariant, + generics: tcx.generics_of(parent_def_id), + }; + functional_variances.relate(sig, sig).unwrap(); + functional_variances.variances + }), + outlives_env: LazyCell::new(|| { + let param_env = tcx.param_env(parent_def_id); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let assumed_wf_tys = ocx.assumed_wf_types(param_env, parent_def_id).unwrap_or_default(); + let implied_bounds = + infcx.implied_bounds_tys_compat(param_env, parent_def_id, &assumed_wf_tys, false); + OutlivesEnvironment::with_bounds(param_env, implied_bounds) + }), }); } -struct VisitOpaqueTypes<'tcx> { +struct VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> { tcx: TyCtxt<'tcx>, parent_def_id: LocalDefId, - in_scope_parameters: FxIndexSet, + in_scope_parameters: FxIndexMap, + variances: LazyCell, VarFn>, + outlives_env: LazyCell, OutlivesFn>, seen: FxIndexSet, } -impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { - fn visit_binder>>( - &mut self, - t: &ty::Binder<'tcx, T>, - ) -> Self::Result { +impl<'tcx, VarFn, OutlivesFn> TypeVisitor> + for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn> +where + VarFn: FnOnce() -> FxHashMap, + OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>, +{ + fn visit_binder>>(&mut self, t: &ty::Binder<'tcx, T>) { // When we get into a binder, we need to add its own bound vars to the scope. let mut added = vec![]; for arg in t.bound_vars() { @@ -163,8 +218,8 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..)) | ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => { added.push(def_id); - let unique = self.in_scope_parameters.insert(def_id); - assert!(unique); + let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late); + assert_eq!(unique, None); } _ => { self.tcx.dcx().span_delayed_bug( @@ -184,7 +239,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { } } - fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: Ty<'tcx>) { if !t.has_aliases() { return; } @@ -207,89 +262,126 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { && let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin && parent_def_id == self.parent_def_id { - // Compute the set of args that are captured by the opaque... - let mut captured = FxIndexSet::default(); - let variances = self.tcx.variances_of(opaque_def_id); - let mut current_def_id = Some(opaque_def_id.to_def_id()); - while let Some(def_id) = current_def_id { - let generics = self.tcx.generics_of(def_id); - for param in &generics.own_params { - // A param is captured if it's invariant. - if variances[param.index as usize] != ty::Invariant { - continue; - } - // We need to turn all `ty::Param`/`ConstKind::Param` and - // `ReEarlyParam`/`ReBound` into def ids. - captured.insert(extract_def_id_from_arg( - self.tcx, - generics, - opaque_ty.args[param.index as usize], - )); - } - current_def_id = generics.parent; - } - - // Compute the set of in scope params that are not captured. Get their spans, - // since that's all we really care about them for emitting the diagnostic. - let uncaptured_spans: Vec<_> = self - .in_scope_parameters - .iter() - .filter(|def_id| !captured.contains(*def_id)) - .map(|def_id| self.tcx.def_span(def_id)) - .collect(); - let opaque_span = self.tcx.def_span(opaque_def_id); let new_capture_rules = opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024; - - // If we have uncaptured args, and if the opaque doesn't already have - // `use<>` syntax on it, and we're < edition 2024, then warn the user. if !new_capture_rules && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..))) - && !uncaptured_spans.is_empty() { - let suggestion = if let Ok(snippet) = - self.tcx.sess.source_map().span_to_snippet(opaque_span) - && snippet.starts_with("impl ") - { - let (lifetimes, others): (Vec<_>, Vec<_>) = captured - .into_iter() - .partition(|def_id| self.tcx.def_kind(*def_id) == DefKind::LifetimeParam); - // Take all lifetime params first, then all others (ty/ct). - let generics: Vec<_> = lifetimes - .into_iter() - .chain(others) - .map(|def_id| self.tcx.item_name(def_id).to_string()) - .collect(); - // Make sure that we're not trying to name any APITs - if generics.iter().all(|name| !name.starts_with("impl ")) { - Some(( - format!(" + use<{}>", generics.join(", ")), - opaque_span.shrink_to_hi(), - )) + // Compute the set of args that are captured by the opaque... + let mut captured = FxIndexSet::default(); + let mut captured_regions = FxIndexSet::default(); + let variances = self.tcx.variances_of(opaque_def_id); + let mut current_def_id = Some(opaque_def_id.to_def_id()); + while let Some(def_id) = current_def_id { + let generics = self.tcx.generics_of(def_id); + for param in &generics.own_params { + // A param is captured if it's invariant. + if variances[param.index as usize] != ty::Invariant { + continue; + } + + let arg = opaque_ty.args[param.index as usize]; + // We need to turn all `ty::Param`/`ConstKind::Param` and + // `ReEarlyParam`/`ReBound` into def ids. + captured.insert(extract_def_id_from_arg(self.tcx, generics, arg)); + + captured_regions.extend(arg.as_region()); + } + current_def_id = generics.parent; + } + + // Compute the set of in scope params that are not captured. + let mut uncaptured_args: FxIndexSet<_> = self + .in_scope_parameters + .iter() + .filter(|&(def_id, _)| !captured.contains(def_id)) + .collect(); + // Remove the set of lifetimes that are in-scope that outlive some other captured + // lifetime and are contravariant (i.e. covariant in argument position). + uncaptured_args.retain(|&(def_id, kind)| { + let Some(ty::Bivariant | ty::Contravariant) = self.variances.get(def_id) else { + // Keep all covariant/invariant args. Also if variance is `None`, + // then that means it's either not a lifetime, or it didn't show up + // anywhere in the signature. + return true; + }; + // We only computed variance of lifetimes... + debug_assert_matches!(self.tcx.def_kind(def_id), DefKind::LifetimeParam); + let uncaptured = match *kind { + ParamKind::Early(name, index) => ty::Region::new_early_param( + self.tcx, + ty::EarlyParamRegion { name, index }, + ), + ParamKind::Free(def_id, name) => ty::Region::new_late_param( + self.tcx, + self.parent_def_id.to_def_id(), + ty::BoundRegionKind::BrNamed(def_id, name), + ), + // Totally ignore late bound args from binders. + ParamKind::Late => return true, + }; + // Does this region outlive any captured region? + !captured_regions.iter().any(|r| { + self.outlives_env + .free_region_map() + .sub_free_regions(self.tcx, *r, uncaptured) + }) + }); + + // If we have uncaptured args, and if the opaque doesn't already have + // `use<>` syntax on it, and we're < edition 2024, then warn the user. + if !uncaptured_args.is_empty() { + let suggestion = if let Ok(snippet) = + self.tcx.sess.source_map().span_to_snippet(opaque_span) + && snippet.starts_with("impl ") + { + let (lifetimes, others): (Vec<_>, Vec<_>) = + captured.into_iter().partition(|def_id| { + self.tcx.def_kind(*def_id) == DefKind::LifetimeParam + }); + // Take all lifetime params first, then all others (ty/ct). + let generics: Vec<_> = lifetimes + .into_iter() + .chain(others) + .map(|def_id| self.tcx.item_name(def_id).to_string()) + .collect(); + // Make sure that we're not trying to name any APITs + if generics.iter().all(|name| !name.starts_with("impl ")) { + Some(( + format!(" + use<{}>", generics.join(", ")), + opaque_span.shrink_to_hi(), + )) + } else { + None + } } else { None - } - } else { - None - }; - - self.tcx.emit_node_span_lint( - IMPL_TRAIT_OVERCAPTURES, - self.tcx.local_def_id_to_hir_id(opaque_def_id), - opaque_span, - ImplTraitOvercapturesLint { - self_ty: t, - num_captured: uncaptured_spans.len(), - uncaptured_spans, - suggestion, - }, - ); + }; + + let uncaptured_spans: Vec<_> = uncaptured_args + .into_iter() + .map(|(def_id, _)| self.tcx.def_span(def_id)) + .collect(); + + self.tcx.emit_node_span_lint( + IMPL_TRAIT_OVERCAPTURES, + self.tcx.local_def_id_to_hir_id(opaque_def_id), + opaque_span, + ImplTraitOvercapturesLint { + self_ty: t, + num_captured: uncaptured_spans.len(), + uncaptured_spans, + suggestion, + }, + ); + } } + // Otherwise, if we are edition 2024, have `use<>` syntax, and // have no uncaptured args, then we should warn to the user that // it's redundant to capture all args explicitly. - else if new_capture_rules + if new_capture_rules && let Some((captured_args, capturing_span)) = opaque.bounds.iter().find_map(|bound| match *bound { hir::GenericBound::Use(a, s) => Some((a, s)), @@ -327,7 +419,7 @@ impl<'tcx> TypeVisitor> for VisitOpaqueTypes<'tcx> { if self .in_scope_parameters .iter() - .all(|def_id| explicitly_captured.contains(def_id)) + .all(|(def_id, _)| explicitly_captured.contains(def_id)) { self.tcx.emit_node_span_lint( IMPL_TRAIT_REDUNDANT_CAPTURES, @@ -396,7 +488,11 @@ fn extract_def_id_from_arg<'tcx>( ty::ReBound( _, ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. }, - ) => def_id, + ) + | ty::ReLateParam(ty::LateParamRegion { + scope: _, + bound_region: ty::BoundRegionKind::BrNamed(def_id, ..), + }) => def_id, _ => unreachable!(), }, ty::GenericArgKind::Type(ty) => { @@ -413,3 +509,106 @@ fn extract_def_id_from_arg<'tcx>( } } } + +/// Computes the variances of regions that appear in the type, but considering +/// late-bound regions too, which don't have their variance computed usually. +/// +/// Like generalization, this is a unary operation implemented on top of the binary +/// relation infrastructure, mostly because it's much easier to have the relation +/// track the variance for you, rather than having to do it yourself. +struct FunctionalVariances<'tcx> { + tcx: TyCtxt<'tcx>, + variances: FxHashMap, + ambient_variance: ty::Variance, + generics: &'tcx ty::Generics, +} + +impl<'tcx> TypeRelation> for FunctionalVariances<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn relate_with_variance>>( + &mut self, + variance: rustc_type_ir::Variance, + _: ty::VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.relate(a, b).unwrap(); + self.ambient_variance = old_variance; + Ok(a) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + structurally_relate_tys(self, a, b).unwrap(); + Ok(a) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + let def_id = match *a { + ty::ReEarlyParam(ebr) => self.generics.region_param(ebr, self.tcx).def_id, + ty::ReBound( + _, + ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. }, + ) + | ty::ReLateParam(ty::LateParamRegion { + scope: _, + bound_region: ty::BoundRegionKind::BrNamed(def_id, ..), + }) => def_id, + _ => { + return Ok(a); + } + }; + + if let Some(variance) = self.variances.get_mut(&def_id) { + *variance = unify(*variance, self.ambient_variance); + } else { + self.variances.insert(def_id, self.ambient_variance); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + structurally_relate_consts(self, a, b).unwrap(); + Ok(a) + } + + fn binders( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate>, + { + self.relate(a.skip_binder(), b.skip_binder()).unwrap(); + Ok(a) + } +} + +/// What is the variance that satisfies the two variances? +fn unify(a: ty::Variance, b: ty::Variance) -> ty::Variance { + match (a, b) { + // Bivariance is lattice bottom. + (ty::Bivariant, other) | (other, ty::Bivariant) => other, + // Invariant is lattice top. + (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, + // If type is required to be covariant and contravariant, then it's invariant. + (ty::Contravariant, ty::Covariant) | (ty::Covariant, ty::Contravariant) => ty::Invariant, + // Otherwise, co + co = co, contra + contra = contra. + (ty::Contravariant, ty::Contravariant) => ty::Contravariant, + (ty::Covariant, ty::Covariant) => ty::Covariant, + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c5a5c5b30afef..bb7de4739fbdd 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -30,6 +30,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 9906be60e3e95..a98e6943d68ee 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -612,7 +612,9 @@ fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn io::Write) -> io: let def_id = body.source.def_id(); let kind = tcx.def_kind(def_id); let is_function = match kind { - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => { + true + } _ => tcx.is_closure_like(def_id), }; match (kind, body.source.promoted) { diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index ebe8d2eff4ff3..cf39c136b01a3 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -207,11 +207,12 @@ pub fn coroutine_by_move_body_def_id<'tcx>( let mut by_move_body = body.clone(); MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body); - dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - let body_def = tcx.create_def(coroutine_def_id, kw::Empty, DefKind::SyntheticCoroutineBody); + // This will always be `{closure#1}`, since the original coroutine is `{closure#0}`. + let body_def = tcx.create_def(parent_def_id, kw::Empty, DefKind::SyntheticCoroutineBody); by_move_body.source = mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id())); + dump_mir(tcx, false, "built", &"after", &by_move_body, |_, _| Ok(())); // Inherited from the by-ref coroutine. body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone()); diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 8940a21d7fae5..67bee36b8a5e2 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -163,7 +163,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let def_id = body.source.def_id(); - let mut allocations = Allocations::default(); + let mut candidates = Candidates::default(); + let mut write_info = WriteInfo::default(); trace!(func = ?tcx.def_path_str(def_id)); let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body); @@ -191,12 +192,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { loop { // PERF: Can we do something smarter than recalculating the candidates and liveness // results? - let mut candidates = find_candidates( - body, - &borrowed, - &mut allocations.candidates, - &mut allocations.candidates_reverse, - ); + candidates.reset_and_find(body, &borrowed); trace!(?candidates); dest_prop_mir_dump(tcx, body, &points, &live, round_count); @@ -204,7 +200,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { &mut candidates, &points, &live, - &mut allocations.write_info, + &mut write_info, body, ); @@ -253,20 +249,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { } } -/// Container for the various allocations that we need. -/// -/// We store these here and hand out `&mut` access to them, instead of dropping and recreating them -/// frequently. Everything with a `&'alloc` lifetime points into here. -#[derive(Default)] -struct Allocations { - candidates: FxIndexMap>, - candidates_reverse: FxIndexMap>, - write_info: WriteInfo, - // PERF: Do this for `MaybeLiveLocals` allocations too. -} - -#[derive(Debug)] -struct Candidates<'alloc> { +#[derive(Debug, Default)] +struct Candidates { /// The set of candidates we are considering in this optimization. /// /// We will always merge the key into at most one of its values. @@ -281,11 +265,12 @@ struct Candidates<'alloc> { /// /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to /// remove that assignment. - c: &'alloc mut FxIndexMap>, + c: FxIndexMap>, + /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`, /// then this contains `b => a`. // PERF: Possibly these should be `SmallVec`s? - reverse: &'alloc mut FxIndexMap>, + reverse: FxIndexMap>, } ////////////////////////////////////////////////////////// @@ -358,19 +343,40 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> { // // This section enforces bullet point 2 -struct FilterInformation<'a, 'body, 'alloc, 'tcx> { - body: &'body Body<'tcx>, +struct FilterInformation<'a, 'tcx> { + body: &'a Body<'tcx>, points: &'a DenseLocationMap, live: &'a SparseIntervalMatrix, - candidates: &'a mut Candidates<'alloc>, - write_info: &'alloc mut WriteInfo, + candidates: &'a mut Candidates, + write_info: &'a mut WriteInfo, at: Location, } // We first implement some utility functions which we will expose removing candidates according to // different needs. Throughout the liveness filtering, the `candidates` are only ever accessed // through these methods, and not directly. -impl<'alloc> Candidates<'alloc> { +impl Candidates { + /// Collects the candidates for merging. + /// + /// This is responsible for enforcing the first and third bullet point. + fn reset_and_find<'tcx>(&mut self, body: &Body<'tcx>, borrowed: &BitSet) { + self.c.clear(); + self.reverse.clear(); + let mut visitor = FindAssignments { body, candidates: &mut self.c, borrowed }; + visitor.visit_body(body); + // Deduplicate candidates. + for (_, cands) in self.c.iter_mut() { + cands.sort(); + cands.dedup(); + } + // Generate the reverse map. + for (src, cands) in self.c.iter() { + for dest in cands.iter().copied() { + self.reverse.entry(dest).or_default().push(*src); + } + } + } + /// Just `Vec::retain`, but the condition is inverted and we add debugging output fn vec_filter_candidates( src: Local, @@ -445,7 +451,7 @@ enum CandidateFilter { Remove, } -impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { +impl<'a, 'tcx> FilterInformation<'a, 'tcx> { /// Filters the set of candidates to remove those that conflict. /// /// The steps we take are exactly those that are outlined at the top of the file. For each @@ -463,12 +469,12 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { /// before the statement/terminator will correctly report locals that are read in the /// statement/terminator to be live. We are additionally conservative by treating all written to /// locals as also being read from. - fn filter_liveness<'b>( - candidates: &mut Candidates<'alloc>, + fn filter_liveness( + candidates: &mut Candidates, points: &DenseLocationMap, live: &SparseIntervalMatrix, - write_info_alloc: &'alloc mut WriteInfo, - body: &'b Body<'tcx>, + write_info: &mut WriteInfo, + body: &Body<'tcx>, ) { let mut this = FilterInformation { body, @@ -477,7 +483,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> { candidates, // We don't actually store anything at this scope, we just keep things here to be able // to reuse the allocation. - write_info: write_info_alloc, + write_info, // Doesn't matter what we put here, will be overwritten before being used at: Location::START, }; @@ -734,40 +740,13 @@ fn places_to_candidate_pair<'tcx>( Some((a, b)) } -/// Collects the candidates for merging -/// -/// This is responsible for enforcing the first and third bullet point. -fn find_candidates<'alloc, 'tcx>( - body: &Body<'tcx>, - borrowed: &BitSet, - candidates: &'alloc mut FxIndexMap>, - candidates_reverse: &'alloc mut FxIndexMap>, -) -> Candidates<'alloc> { - candidates.clear(); - candidates_reverse.clear(); - let mut visitor = FindAssignments { body, candidates, borrowed }; - visitor.visit_body(body); - // Deduplicate candidates - for (_, cands) in candidates.iter_mut() { - cands.sort(); - cands.dedup(); - } - // Generate the reverse map - for (src, cands) in candidates.iter() { - for dest in cands.iter().copied() { - candidates_reverse.entry(dest).or_default().push(*src); - } - } - Candidates { c: candidates, reverse: candidates_reverse } -} - -struct FindAssignments<'a, 'alloc, 'tcx> { +struct FindAssignments<'a, 'tcx> { body: &'a Body<'tcx>, - candidates: &'alloc mut FxIndexMap>, + candidates: &'a mut FxIndexMap>, borrowed: &'a BitSet, } -impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> { +impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) { if let StatementKind::Assign(box ( lhs, @@ -819,9 +798,9 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool { ///////////////////////////////////////////////////////// // MIR Dump -fn dest_prop_mir_dump<'body, 'tcx>( +fn dest_prop_mir_dump<'tcx>( tcx: TyCtxt<'tcx>, - body: &'body Body<'tcx>, + body: &Body<'tcx>, points: &DenseLocationMap, live: &SparseIntervalMatrix, round: usize, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5654f5aa4b8d2..d9f197d9510ef 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -288,8 +288,19 @@ marker_impls! { /// } /// ``` /// -/// There is a small difference between the two: the `derive` strategy will also place a `Copy` -/// bound on type parameters, which isn't always desired. +/// There is a small difference between the two. The `derive` strategy will also place a `Copy` +/// bound on type parameters: +/// +/// ``` +/// #[derive(Clone)] +/// struct MyStruct(T); +/// +/// impl Copy for MyStruct { } +/// ``` +/// +/// This isn't always desired. For example, shared references (`&T`) can be copied regardless of +/// whether `T` is `Copy`. Likewise, a generic struct containing markers such as [`PhantomData`] +/// could potentially be duplicated with a bit-wise copy. /// /// ## What's the difference between `Copy` and `Clone`? /// diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index dc34c7844bfdd..f67d006116cb6 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1794,13 +1794,64 @@ fn render_impl( let mut default_impl_items = Buffer::empty_from(w); let impl_ = i.inner_impl(); + // Impl items are grouped by kinds: + // + // 1. Constants + // 2. Types + // 3. Functions + // + // This order is because you can have associated constants used in associated types (like array + // length), and both in associcated functions. So with this order, when reading from top to + // bottom, you should see items definitions before they're actually used most of the time. + let mut assoc_types = Vec::new(); + let mut methods = Vec::new(); + if !impl_.is_negative_trait_impl() { for trait_item in &impl_.items { + match *trait_item.kind { + clean::MethodItem(..) | clean::TyMethodItem(_) => methods.push(trait_item), + clean::TyAssocTypeItem(..) | clean::AssocTypeItem(..) => { + assoc_types.push(trait_item) + } + clean::TyAssocConstItem(..) | clean::AssocConstItem(_) => { + // We render it directly since they're supposed to come first. + doc_impl_item( + &mut default_impl_items, + &mut impl_items, + cx, + trait_item, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, + ); + } + _ => {} + } + } + + for assoc_type in assoc_types { doc_impl_item( &mut default_impl_items, &mut impl_items, cx, - trait_item, + assoc_type, + if trait_.is_some() { &i.impl_item } else { parent }, + link, + render_mode, + false, + trait_, + rendering_params, + ); + } + for method in methods { + doc_impl_item( + &mut default_impl_items, + &mut impl_items, + cx, + method, if trait_.is_some() { &i.impl_item } else { parent }, link, render_mode, @@ -2455,28 +2506,6 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES; let locations_encoded = serde_json::to_string(&line_ranges).unwrap(); - write!( - &mut w, - "
\ -
\ - {name} ({title})\ -
\ -
", - expanded_cls = if needs_expansion { "" } else { "expanded" }, - name = call_data.display_name, - url = init_url, - title = init_title, - // The locations are encoded as a data attribute, so they can be read - // later by the JS for interactions. - locations = Escape(&locations_encoded) - ) - .unwrap(); - - if line_ranges.len() > 1 { - w.write_str(r#" "#) - .unwrap(); - } - // Look for the example file in the source map if it exists, otherwise return a dummy span let file_span = (|| { let source_map = tcx.sess.source_map(); @@ -2507,9 +2536,16 @@ fn render_call_locations(mut w: W, cx: &mut Context<'_>, item: &c cx, &cx.root_path(), highlight::DecorationInfo(decoration_info), - sources::SourceContext::Embedded { offset: line_min, needs_expansion }, + sources::SourceContext::Embedded(sources::ScrapedInfo { + needs_prev_next_buttons: line_ranges.len() > 1, + needs_expansion, + offset: line_min, + name: &call_data.display_name, + url: init_url, + title: init_title, + locations: locations_encoded, + }), ); - w.write_str("
").unwrap(); true }; diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index a0a72d59d123f..cda5409a460ee 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -843,55 +843,55 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: } } - if !required_types.is_empty() { + if !required_consts.is_empty() { write_section_heading( w, - "Required Associated Types", - "required-associated-types", + "Required Associated Constants", + "required-associated-consts", None, "
", ); - for t in required_types { + for t in required_consts { trait_item(w, cx, t, it); } w.write_str("
"); } - if !provided_types.is_empty() { + if !provided_consts.is_empty() { write_section_heading( w, - "Provided Associated Types", - "provided-associated-types", + "Provided Associated Constants", + "provided-associated-consts", None, "
", ); - for t in provided_types { + for t in provided_consts { trait_item(w, cx, t, it); } w.write_str("
"); } - if !required_consts.is_empty() { + if !required_types.is_empty() { write_section_heading( w, - "Required Associated Constants", - "required-associated-consts", + "Required Associated Types", + "required-associated-types", None, "
", ); - for t in required_consts { + for t in required_types { trait_item(w, cx, t, it); } w.write_str("
"); } - if !provided_consts.is_empty() { + if !provided_types.is_empty() { write_section_heading( w, - "Provided Associated Constants", - "provided-associated-consts", + "Provided Associated Types", + "provided-associated-types", None, "
", ); - for t in provided_consts { + for t in provided_types { trait_item(w, cx, t, it); } w.write_str("
"); diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index c75c28ef0a3b4..06cc57b2a5536 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -302,10 +302,10 @@ fn sidebar_trait<'a>( blocks.extend( [ - ("required-associated-types", "Required Associated Types", req_assoc), - ("provided-associated-types", "Provided Associated Types", prov_assoc), ("required-associated-consts", "Required Associated Constants", req_assoc_const), ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const), + ("required-associated-types", "Required Associated Types", req_assoc), + ("provided-associated-types", "Provided Associated Types", prov_assoc), ("required-methods", "Required Methods", req_method), ("provided-methods", "Provided Methods", prov_method), ("foreign-impls", "Implementations on Foreign Types", foreign_impls), @@ -394,6 +394,7 @@ fn sidebar_assoc_items<'a>( let cache = cx.cache(); let mut assoc_consts = Vec::new(); + let mut assoc_types = Vec::new(); let mut methods = Vec::new(); if let Some(v) = cache.impls.get(&did) { let mut used_links = FxHashSet::default(); @@ -401,22 +402,14 @@ fn sidebar_assoc_items<'a>( { let used_links_bor = &mut used_links; - assoc_consts.extend( - v.iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)), - ); + for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) { + assoc_consts.extend(get_associated_constants(impl_, used_links_bor)); + assoc_types.extend(get_associated_types(impl_, used_links_bor)); + methods.extend(get_methods(impl_, false, used_links_bor, false, cx.tcx())); + } // We want links' order to be reproducible so we don't use unstable sort. assoc_consts.sort(); - - #[rustfmt::skip] // rustfmt makes the pipeline less readable - methods.extend( - v.iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())), - ); - - // We want links' order to be reproducible so we don't use unstable sort. + assoc_types.sort(); methods.sort(); } @@ -426,6 +419,11 @@ fn sidebar_assoc_items<'a>( "associatedconstant", assoc_consts, ), + LinkBlock::new( + Link::new("implementations", "Associated Types"), + "associatedtype", + assoc_types, + ), LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), ]; @@ -452,6 +450,7 @@ fn sidebar_assoc_items<'a>( &mut blocks, ); } + links.append(&mut blocks); } } @@ -715,3 +714,19 @@ fn get_associated_constants<'a>( }) .collect::>() } + +fn get_associated_types<'a>( + i: &'a clean::Impl, + used_links: &mut FxHashSet, +) -> Vec> { + i.items + .iter() + .filter_map(|item| match item.name { + Some(ref name) if !name.is_empty() && item.is_associated_type() => Some(Link::new( + get_next_url(used_links, format!("{typ}.{name}", typ = ItemType::AssocType)), + name.as_str(), + )), + _ => None, + }) + .collect::>() +} diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index ae65dd76b5fb6..551bb56685c99 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -290,9 +290,34 @@ where } } -pub(crate) enum SourceContext { +pub(crate) struct ScrapedInfo<'a> { + pub(crate) offset: usize, + pub(crate) needs_prev_next_buttons: bool, + pub(crate) name: &'a str, + pub(crate) url: &'a str, + pub(crate) title: &'a str, + pub(crate) locations: String, + pub(crate) needs_expansion: bool, +} + +#[derive(Template)] +#[template(path = "scraped_source.html")] +struct ScrapedSource<'a, Code: std::fmt::Display> { + info: ScrapedInfo<'a>, + lines: RangeInclusive, + code_html: Code, +} + +#[derive(Template)] +#[template(path = "source.html")] +struct Source { + lines: RangeInclusive, + code_html: Code, +} + +pub(crate) enum SourceContext<'a> { Standalone, - Embedded { offset: usize, needs_expansion: bool }, + Embedded(ScrapedInfo<'a>), } /// Wrapper struct to render the source code of a file. This will do things like @@ -304,23 +329,8 @@ pub(crate) fn print_src( context: &Context<'_>, root_path: &str, decoration_info: highlight::DecorationInfo, - source_context: SourceContext, + source_context: SourceContext<'_>, ) { - #[derive(Template)] - #[template(path = "source.html")] - struct Source { - embedded: bool, - needs_expansion: bool, - lines: RangeInclusive, - code_html: Code, - } - let lines = s.lines().count(); - let (embedded, needs_expansion, lines) = match source_context { - SourceContext::Standalone => (false, false, 1..=lines), - SourceContext::Embedded { offset, needs_expansion } => { - (true, needs_expansion, (1 + offset)..=(lines + offset)) - } - }; let current_href = context .href_from_span(clean::Span::new(file_span), false) .expect("only local crates should have sources emitted"); @@ -333,5 +343,14 @@ pub(crate) fn print_src( ); Ok(()) }); - Source { embedded, needs_expansion, lines, code_html: code }.render_into(&mut writer).unwrap(); + let lines = s.lines().count(); + match source_context { + SourceContext::Standalone => { + Source { lines: (1..=lines), code_html: code }.render_into(&mut writer).unwrap() + } + SourceContext::Embedded(info) => { + let lines = (1 + info.offset)..=(lines + info.offset); + ScrapedSource { info, lines, code_html: code }.render_into(&mut writer).unwrap(); + } + }; } diff --git a/src/librustdoc/html/static/css/noscript.css b/src/librustdoc/html/static/css/noscript.css index 86e8edad7032a..e62b16267f102 100644 --- a/src/librustdoc/html/static/css/noscript.css +++ b/src/librustdoc/html/static/css/noscript.css @@ -59,6 +59,8 @@ nav.sub { --copy-path-button-color: #999; --copy-path-img-filter: invert(50%); --copy-path-img-hover-filter: invert(35%); + --code-example-button-color: #7f7f7f; + --code-example-button-hover-color: #595959; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -162,6 +164,8 @@ nav.sub { --copy-path-button-color: #999; --copy-path-img-filter: invert(50%); --copy-path-img-hover-filter: invert(65%); + --code-example-button-color: #7f7f7f; + --code-example-button-hover-color: #a5a5a5; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 271de1b1ccf77..38154dee3e287 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -378,7 +378,7 @@ pre.item-decl { .src .content pre { padding: 20px; } -.rustdoc.src .example-wrap pre.src-line-numbers { +.rustdoc.src .example-wrap .src-line-numbers { padding: 20px 0 20px 4px; } @@ -757,10 +757,32 @@ ul.block, .block li, .block ul { margin-bottom: 10px; } -.rustdoc .example-wrap > pre { +.rustdoc .example-wrap > pre, +.rustdoc .scraped-example .src-line-numbers, +.rustdoc .scraped-example .src-line-numbers > pre { border-radius: 6px; } +/* +If the code example line numbers are displayed, there will be a weird radius in the middle from +both the code example and the line numbers, so we need to remove the radius in this case. +*/ +.rustdoc .example-wrap > .example-line-numbers, +.rustdoc .scraped-example .src-line-numbers, +.rustdoc .scraped-example .src-line-numbers > pre { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rustdoc .example-wrap > .example-line-numbers + pre, +.rustdoc .scraped-example .rust { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.rustdoc .scraped-example { + position: relative; +} + /* For the last child of a div, the margin will be taken care of by the margin-top of the next item. */ .rustdoc .example-wrap:last-child { @@ -772,15 +794,36 @@ ul.block, .block li, .block ul { flex-grow: 1; } -.rustdoc:not(.src) .example-wrap pre { +.scraped-example:not(.expanded) .example-wrap { + /* scrape-examples.js has a constant DEFAULT_MAX_LINES (call it N) for the number + * of lines shown in the un-expanded example code viewer. This pre needs to have + * a max-height equal to line-height * N. The line-height is currently 1.5em, + * and we include additional 10px for padding. */ + max-height: calc(1.5em * 5 + 10px); +} + +.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers, +.rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers > pre, +.rustdoc:not(.src) .scraped-example:not(.expanded) pre.rust { + padding-bottom: 0; + /* See above comment, should be the same max-height. */ overflow: auto hidden; } +.rustdoc:not(.src) .scraped-example .src-line-numbers { + padding-top: 0; +} +.rustdoc:not(.src) .scraped-example.expanded .src-line-numbers { + padding-bottom: 0; +} + +.rustdoc:not(.src) .example-wrap pre { + overflow: auto; +} .rustdoc .example-wrap pre.example-line-numbers, -.rustdoc .example-wrap pre.src-line-numbers { - flex-grow: 0; +.rustdoc .example-wrap .src-line-numbers { min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */ - overflow: initial; + flex-grow: 0; text-align: right; -webkit-user-select: none; user-select: none; @@ -788,7 +831,7 @@ ul.block, .block li, .block ul { color: var(--src-line-numbers-span-color); } -.rustdoc .example-wrap pre.src-line-numbers { +.rustdoc .scraped-example .src-line-numbers { padding: 14px 0; } .src-line-numbers a, .src-line-numbers span { @@ -1500,17 +1543,23 @@ instead, we check that it's not a "finger" cursor. .example-wrap .button-holder.keep-visible { visibility: visible; } -.example-wrap .button-holder .copy-button, .example-wrap .test-arrow { +.example-wrap .button-holder > * { background: var(--main-background-color); cursor: pointer; border-radius: var(--button-border-radius); height: var(--copy-path-height); width: var(--copy-path-width); + border: 0; + color: var(--code-example-button-color); } -.example-wrap .button-holder .copy-button { +.example-wrap .button-holder > *:hover { + color: var(--code-example-button-hover-color); +} +.example-wrap .button-holder > *:not(:first-child) { margin-left: var(--button-left-margin); +} +.example-wrap .button-holder .copy-button { padding: 2px 0 0 4px; - border: 0; } .example-wrap .button-holder .copy-button::before, .example-wrap .test-arrow::before { @@ -2254,6 +2303,7 @@ in src-script.js and main.js } } + /* Should have min-width: (N + 1)px where N is the mobile breakpoint above. */ @media (min-width: 701px) { /* Places file-link for a scraped example on top of the example to save space. @@ -2356,99 +2406,41 @@ in src-script.js and main.js color: var(--scrape-example-help-hover-color); } -.scraped-example { - /* So .scraped-example-title can be positioned absolutely */ - position: relative; -} - -.scraped-example .code-wrapper { - position: relative; - display: flex; - flex-direction: row; - flex-wrap: wrap; - width: 100%; -} - -.scraped-example:not(.expanded) .code-wrapper { - /* scrape-examples.js has a constant DEFAULT_MAX_LINES (call it N) for the number - * of lines shown in the un-expanded example code viewer. This pre needs to have - * a max-height equal to line-height * N. The line-height is currently 1.5em, - * and we include additional 10px for padding. */ - max-height: calc(1.5em * 5 + 10px); -} - -.scraped-example:not(.expanded) .code-wrapper pre { - overflow-y: hidden; - padding-bottom: 0; - /* See above comment, should be the same max-height. */ - max-height: calc(1.5em * 5 + 10px); -} - -.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper, -.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre { - /* See above comment, except this height is based on HIDDEN_MAX_LINES. */ - max-height: calc(1.5em * 10 + 10px); -} - -.scraped-example .code-wrapper .next, -.scraped-example .code-wrapper .prev, -.scraped-example .code-wrapper .expand { - color: var(--main-color); - position: absolute; - top: 0.25em; - z-index: 1; - padding: 0; - background: none; - border: none; - /* iOS button gradient: https://stackoverflow.com/q/5438567 */ - -webkit-appearance: none; - opacity: 1; -} -.scraped-example .code-wrapper .prev { - right: 2.25em; -} -.scraped-example .code-wrapper .next { - right: 1.25em; -} -.scraped-example .code-wrapper .expand { - right: 0.25em; -} - -.scraped-example:not(.expanded) .code-wrapper::before, -.scraped-example:not(.expanded) .code-wrapper::after { +.scraped-example:not(.expanded) .example-wrap::before, +.scraped-example:not(.expanded) .example-wrap::after { content: " "; width: 100%; height: 5px; position: absolute; z-index: 1; } -.scraped-example:not(.expanded) .code-wrapper::before { +.scraped-example:not(.expanded) .example-wrap::before { top: 0; background: linear-gradient(to bottom, var(--scrape-example-code-wrapper-background-start), var(--scrape-example-code-wrapper-background-end)); } -.scraped-example:not(.expanded) .code-wrapper::after { +.scraped-example:not(.expanded) .example-wrap::after { bottom: 0; background: linear-gradient(to top, var(--scrape-example-code-wrapper-background-start), var(--scrape-example-code-wrapper-background-end)); } -.scraped-example .code-wrapper .example-wrap { +.scraped-example:not(.expanded) { width: 100%; overflow-y: hidden; margin-bottom: 0; } -.scraped-example:not(.expanded) .code-wrapper .example-wrap { +.scraped-example:not(.expanded) { overflow-x: hidden; } -.scraped-example .example-wrap .rust span.highlight { +.scraped-example .rust span.highlight { background: var(--scrape-example-code-line-highlight); } -.scraped-example .example-wrap .rust span.highlight.focus { +.scraped-example .rust span.highlight.focus { background: var(--scrape-example-code-line-highlight-focus); } @@ -2542,6 +2534,8 @@ by default. --copy-path-button-color: #999; --copy-path-img-filter: invert(50%); --copy-path-img-hover-filter: invert(35%); + --code-example-button-color: #7f7f7f; + --code-example-button-hover-color: #595959; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -2644,6 +2638,8 @@ by default. --copy-path-button-color: #999; --copy-path-img-filter: invert(50%); --copy-path-img-hover-filter: invert(65%); + --code-example-button-color: #7f7f7f; + --code-example-button-hover-color: #a5a5a5; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); @@ -2753,6 +2749,8 @@ Original by Dempfi (https://github.com/dempfi/ayu) --copy-path-button-color: #fff; --copy-path-img-filter: invert(70%); --copy-path-img-hover-filter: invert(100%); + --code-example-button-color: #b2b2b2; + --code-example-button-hover-color: #fff; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 0291b6ed6f581..858753a1917d8 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1855,8 +1855,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Since the button will be added, no need to keep this listener around. elem.removeEventListener("mouseover", addCopyButton); - const parent = document.createElement("div"); - parent.className = "button-holder"; + // If this is a scrapped example, there will already be a "button-holder" element. + let parent = elem.querySelector(".button-holder"); + if (!parent) { + parent = document.createElement("div"); + parent.className = "button-holder"; + } + const runButton = elem.querySelector(".test-arrow"); if (runButton !== null) { // If there is a run button, we move it into the same div. diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 7a3a9c5f34001..06e42814d33b0 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -13,7 +13,7 @@ // Scroll code block to the given code location function scrollToLoc(elt, loc, isHidden) { - const lines = elt.querySelector(".src-line-numbers"); + const lines = elt.querySelector(".src-line-numbers > pre"); let scrollOffset; // If the block is greater than the size of the viewer, @@ -24,8 +24,7 @@ const line = Math.max(0, loc[0] - 1); scrollOffset = lines.children[line].offsetTop; } else { - const wrapper = elt.querySelector(".code-wrapper"); - const halfHeight = wrapper.offsetHeight / 2; + const halfHeight = elt.offsetHeight / 2; const offsetTop = lines.children[loc[0]].offsetTop; const lastLine = lines.children[loc[1]]; const offsetBot = lastLine.offsetTop + lastLine.offsetHeight; @@ -33,7 +32,7 @@ scrollOffset = offsetMid - halfHeight; } - lines.scrollTo(0, scrollOffset); + lines.parentElement.scrollTo(0, scrollOffset); elt.querySelector(".rust").scrollTo(0, scrollOffset); } diff --git a/src/librustdoc/html/templates/scraped_source.html b/src/librustdoc/html/templates/scraped_source.html new file mode 100644 index 0000000000000..e1fc2e6937820 --- /dev/null +++ b/src/librustdoc/html/templates/scraped_source.html @@ -0,0 +1,33 @@ +
{# #} +
+ {{info.name +}} ({{info.title}}) {# #} +
+
{# #} + {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr + Do not show "1 2 3 4 5 ..." in web search results. #} +
{# #} +
+                {% for line in lines.clone() %}
+                    {# ~#}
+                    {{line|safe}}
+                {% endfor %}
+            
{# #} +
{# #} +
 {# #}
+            
+                {{code_html|safe}}
+             {# #}
+        
{# #} + {% if info.needs_prev_next_buttons || info.needs_expansion %} +
+ {% if info.needs_prev_next_buttons %} + {# #} + + {% endif %} + {% if info.needs_expansion %} + + {% endif %} +
+ {% endif %} +
{# #} +
{# #} diff --git a/src/librustdoc/html/templates/source.html b/src/librustdoc/html/templates/source.html index 42d01277db2c2..60a47f1b5de7d 100644 --- a/src/librustdoc/html/templates/source.html +++ b/src/librustdoc/html/templates/source.html @@ -1,21 +1,15 @@ -
{# #} +
{# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr Do not show "1 2 3 4 5 ..." in web search results. #}
         {% for line in lines.clone() %}
-            {% if embedded %}
-                {{line|safe}}
-            {%~ else %}
-                {{line|safe}}
-            {%~ endif %}
+            {# ~#}
+            {{line|safe}}
         {% endfor %}
     
{# #}
 {# #}
         
-            {% if needs_expansion %}
-                
-            {% endif %}
             {{code_html|safe}}
          {# #}
     
{# #} -
+
{# #} diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index a0b96e25a0cba..6b58173b34304 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -15,7 +15,7 @@ use crate::{ /// This is a custom command wrapper that simplifies working with commands and makes it easier to /// ensure that we check the exit status of executed processes. /// -/// # A [`Command`] must be executed +/// # A [`Command`] must be executed exactly once /// /// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If /// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test @@ -23,26 +23,73 @@ use crate::{ /// containing constructed but never executed commands is dangerous because it can give a false /// sense of confidence. /// +/// Each [`Command`] invocation can also only be executed once, because we want to enforce +/// `std{in,out,err}` config via [`std::process::Stdio`] but [`std::process::Stdio`] is not +/// cloneable. +/// +/// In this sense, [`Command`] exhibits linear type semantics but enforced at run-time. +/// /// [`run`]: Self::run /// [`run_fail`]: Self::run_fail /// [`run_unchecked`]: Self::run_unchecked #[derive(Debug)] pub struct Command { cmd: StdCommand, - stdin: Option>, + // Convience for providing a quick stdin buffer. + stdin_buf: Option>, + + // Configurations for child process's std{in,out,err} handles. + stdin: Option, + stdout: Option, + stderr: Option, + + // Emulate linear type semantics. drop_bomb: DropBomb, + already_executed: bool, } impl Command { #[track_caller] pub fn new>(program: P) -> Self { let program = program.as_ref(); - Self { cmd: StdCommand::new(program), stdin: None, drop_bomb: DropBomb::arm(program) } + Self { + cmd: StdCommand::new(program), + stdin_buf: None, + drop_bomb: DropBomb::arm(program), + stdin: None, + stdout: None, + stderr: None, + already_executed: false, + } } - /// Specify a stdin input - pub fn stdin>(&mut self, input: I) -> &mut Self { - self.stdin = Some(input.as_ref().to_vec().into_boxed_slice()); + /// Specify a stdin input buffer. This is a convenience helper, + pub fn stdin_buf>(&mut self, input: I) -> &mut Self { + self.stdin_buf = Some(input.as_ref().to_vec().into_boxed_slice()); + self + } + + /// Configuration for the child process’s standard input (stdin) handle. + /// + /// See [`std::process::Command::stdin`]. + pub fn stdin>(&mut self, cfg: T) -> &mut Self { + self.stdin = Some(cfg.into()); + self + } + + /// Configuration for the child process’s standard output (stdout) handle. + /// + /// See [`std::process::Command::stdout`]. + pub fn stdout>(&mut self, cfg: T) -> &mut Self { + self.stdout = Some(cfg.into()); + self + } + + /// Configuration for the child process’s standard error (stderr) handle. + /// + /// See [`std::process::Command::stderr`]. + pub fn stderr>(&mut self, cfg: T) -> &mut Self { + self.stderr = Some(cfg.into()); self } @@ -105,6 +152,8 @@ impl Command { } /// Run the constructed command and assert that it is successfully run. + /// + /// By default, std{in,out,err} are [`Stdio::piped()`]. #[track_caller] pub fn run(&mut self) -> CompletedProcess { let output = self.command_output(); @@ -115,6 +164,8 @@ impl Command { } /// Run the constructed command and assert that it does not successfully run. + /// + /// By default, std{in,out,err} are [`Stdio::piped()`]. #[track_caller] pub fn run_fail(&mut self) -> CompletedProcess { let output = self.command_output(); @@ -124,10 +175,10 @@ impl Command { output } - /// Run the command but do not check its exit status. - /// Only use if you explicitly don't care about the exit status. - /// Prefer to use [`Self::run`] and [`Self::run_fail`] - /// whenever possible. + /// Run the command but do not check its exit status. Only use if you explicitly don't care + /// about the exit status. + /// + /// Prefer to use [`Self::run`] and [`Self::run_fail`] whenever possible. #[track_caller] pub fn run_unchecked(&mut self) -> CompletedProcess { self.command_output() @@ -135,13 +186,19 @@ impl Command { #[track_caller] fn command_output(&mut self) -> CompletedProcess { + if self.already_executed { + panic!("command was already executed"); + } else { + self.already_executed = true; + } + self.drop_bomb.defuse(); // let's make sure we piped all the input and outputs - self.cmd.stdin(Stdio::piped()); - self.cmd.stdout(Stdio::piped()); - self.cmd.stderr(Stdio::piped()); + self.cmd.stdin(self.stdin.take().unwrap_or(Stdio::piped())); + self.cmd.stdout(self.stdout.take().unwrap_or(Stdio::piped())); + self.cmd.stderr(self.stderr.take().unwrap_or(Stdio::piped())); - let output = if let Some(input) = &self.stdin { + let output = if let Some(input) = &self.stdin_buf { let mut child = self.cmd.spawn().unwrap(); { diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index e9315856cd775..2522c4aeb9365 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -227,9 +227,10 @@ impl LlvmFilecheck { Self { cmd } } - /// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call. - pub fn stdin>(&mut self, input: I) -> &mut Self { - self.cmd.stdin(input); + /// Provide a buffer representing standard input containing patterns that will be matched + /// against the `.patterns(path)` call. + pub fn stdin_buf>(&mut self, input: I) -> &mut Self { + self.cmd.stdin_buf(input); self } diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index cece58d29566c..f60ea972839c1 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -291,9 +291,9 @@ impl Rustc { self } - /// Specify a stdin input - pub fn stdin>(&mut self, input: I) -> &mut Self { - self.cmd.stdin(input); + /// Specify a stdin input buffer. + pub fn stdin_buf>(&mut self, input: I) -> &mut Self { + self.cmd.stdin_buf(input); self } diff --git a/src/tools/run-make-support/src/external_deps/rustdoc.rs b/src/tools/run-make-support/src/external_deps/rustdoc.rs index 96b1c719e2e3e..df5e5d8a2e84a 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs @@ -85,9 +85,9 @@ impl Rustdoc { self } - /// Specify a stdin input - pub fn stdin>(&mut self, input: I) -> &mut Self { - self.cmd.stdin(input); + /// Specify a stdin input buffer. + pub fn stdin_buf>(&mut self, input: I) -> &mut Self { + self.cmd.stdin_buf(input); self } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 989d00d4c2f97..fefafb95b33b1 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -34,6 +34,7 @@ pub mod rfs { } // Re-exports of third-party library crates. +// tidy-alphabetical-start pub use bstr; pub use gimli; pub use libc; @@ -41,6 +42,7 @@ pub use object; pub use regex; pub use serde_json; pub use wasmparser; +// tidy-alphabetical-end // Re-exports of external dependencies. pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc}; diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-abort.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir similarity index 81% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-abort.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir index 1f5bb551b8e01..7da33b8a094b3 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-abort.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir @@ -1,11 +1,11 @@ -// MIR for `main::{closure#0}::{closure#0}::{closure#0}` 0 coroutine_by_move +// MIR for `main::{closure#0}::{closure#0}::{closure#0}` after built -fn main::{closure#0}::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}, _2: ResumeTy) -> () +fn main::{closure#0}::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:54:53: 57:10}, _2: ResumeTy) -> () yields () { debug _task_context => _2; debug a => (_1.0: i32); - debug b => (_1.1: i32); + debug b => (*(_1.1: &i32)); let mut _0: (); let _3: i32; scope 1 { @@ -28,7 +28,7 @@ yields () _4 = &_3; FakeRead(ForLet(None), _4); StorageLive(_5); - _5 = &(_1.1: i32); + _5 = &(*(_1.1: &i32)); FakeRead(ForLet(None), _5); _0 = const (); StorageDead(_5); diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-unwind.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#1}.built.after.mir similarity index 80% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-unwind.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#1}.built.after.mir index 1f5bb551b8e01..a21e82ef5b633 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.panic-unwind.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#1}.built.after.mir @@ -1,6 +1,6 @@ -// MIR for `main::{closure#0}::{closure#0}::{closure#0}` 0 coroutine_by_move +// MIR for `main::{closure#0}::{closure#0}::{closure#1}` after built -fn main::{closure#0}::{closure#0}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}, _2: ResumeTy) -> () +fn main::{closure#0}::{closure#0}::{closure#1}(_1: {async closure body@$DIR/async_closure_shims.rs:54:53: 57:10}, _2: ResumeTy) -> () yields () { debug _task_context => _2; diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-abort.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir similarity index 60% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-abort.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir index a984845fd2c11..c156636099519 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-abort.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir @@ -1,10 +1,10 @@ // MIR for `main::{closure#0}::{closure#0}` 0 coroutine_closure_by_move -fn main::{closure#0}::{closure#0}(_1: {async closure@$DIR/async_closure_shims.rs:53:33: 53:52}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}; +fn main::{closure#0}::{closure#0}(_1: {async closure@$DIR/async_closure_shims.rs:54:33: 54:52}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:54:53: 57:10} { + let mut _0: {async closure body@$DIR/async_closure_shims.rs:54:53: 57:10}; bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:53:53: 56:10 (#0)} { a: move _2, b: move (_1.0: i32) }; + _0 = {coroutine@$DIR/async_closure_shims.rs:54:53: 57:10 (#0)} { a: move _2, b: move (_1.0: i32) }; return; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-unwind.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-unwind.mir deleted file mode 100644 index a984845fd2c11..0000000000000 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.panic-unwind.mir +++ /dev/null @@ -1,10 +0,0 @@ -// MIR for `main::{closure#0}::{closure#0}` 0 coroutine_closure_by_move - -fn main::{closure#0}::{closure#0}(_1: {async closure@$DIR/async_closure_shims.rs:53:33: 53:52}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:53:53: 56:10}; - - bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:53:53: 56:10 (#0)} { a: move _2, b: move (_1.0: i32) }; - return; - } -} diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-abort.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir similarity index 86% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-abort.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir index 17fa93148064f..a4a6a535a2313 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-abort.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir @@ -1,6 +1,6 @@ -// MIR for `main::{closure#0}::{closure#1}::{closure#0}` 0 coroutine_by_move +// MIR for `main::{closure#0}::{closure#1}::{closure#0}` after built -fn main::{closure#0}::{closure#1}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}, _2: ResumeTy) -> () +fn main::{closure#0}::{closure#1}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10}, _2: ResumeTy) -> () yields () { debug _task_context => _2; diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-unwind.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#1}.built.after.mir similarity index 80% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-unwind.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#1}.built.after.mir index 17fa93148064f..69bba6f519414 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.panic-unwind.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#1}.built.after.mir @@ -1,6 +1,6 @@ -// MIR for `main::{closure#0}::{closure#1}::{closure#0}` 0 coroutine_by_move +// MIR for `main::{closure#0}::{closure#1}::{closure#1}` after built -fn main::{closure#0}::{closure#1}::{closure#0}(_1: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}, _2: ResumeTy) -> () +fn main::{closure#0}::{closure#1}::{closure#1}(_1: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10}, _2: ResumeTy) -> () yields () { debug _task_context => _2; diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-abort.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir similarity index 60% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-abort.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir index aab9f7b03b9ab..134fe145baeee 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-abort.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir @@ -1,10 +1,10 @@ // MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_move -fn main::{closure#0}::{closure#1}(_1: {async closure@$DIR/async_closure_shims.rs:62:33: 62:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}; +fn main::{closure#0}::{closure#1}(_1: {async closure@$DIR/async_closure_shims.rs:63:33: 63:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10} { + let mut _0: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10}; bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:62:48: 65:10 (#0)} { a: move _2, b: move (_1.0: &i32) }; + _0 = {coroutine@$DIR/async_closure_shims.rs:63:48: 66:10 (#0)} { a: move _2, b: move (_1.0: &i32) }; return; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-unwind.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-unwind.mir deleted file mode 100644 index aab9f7b03b9ab..0000000000000 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.panic-unwind.mir +++ /dev/null @@ -1,10 +0,0 @@ -// MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_move - -fn main::{closure#0}::{closure#1}(_1: {async closure@$DIR/async_closure_shims.rs:62:33: 62:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}; - - bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:62:48: 65:10 (#0)} { a: move _2, b: move (_1.0: &i32) }; - return; - } -} diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-abort.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir similarity index 60% rename from tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-abort.mir rename to tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir index 3fdc81791deef..f267d93bd606c 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-abort.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir @@ -1,10 +1,10 @@ // MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_ref -fn main::{closure#0}::{closure#1}(_1: &{async closure@$DIR/async_closure_shims.rs:62:33: 62:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}; +fn main::{closure#0}::{closure#1}(_1: &{async closure@$DIR/async_closure_shims.rs:63:33: 63:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10} { + let mut _0: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10}; bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:62:48: 65:10 (#0)} { a: move _2, b: copy ((*_1).0: &i32) }; + _0 = {coroutine@$DIR/async_closure_shims.rs:63:48: 66:10 (#0)} { a: move _2, b: copy ((*_1).0: &i32) }; return; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-unwind.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-unwind.mir deleted file mode 100644 index 3fdc81791deef..0000000000000 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.panic-unwind.mir +++ /dev/null @@ -1,10 +0,0 @@ -// MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_ref - -fn main::{closure#0}::{closure#1}(_1: &{async closure@$DIR/async_closure_shims.rs:62:33: 62:47}, _2: i32) -> {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10} { - let mut _0: {async closure body@$DIR/async_closure_shims.rs:62:48: 65:10}; - - bb0: { - _0 = {coroutine@$DIR/async_closure_shims.rs:62:48: 65:10 (#0)} { a: move _2, b: copy ((*_1).0: &i32) }; - return; - } -} diff --git a/tests/mir-opt/async_closure_shims.rs b/tests/mir-opt/async_closure_shims.rs index 57c55ef055cd7..b2168ba0c465b 100644 --- a/tests/mir-opt/async_closure_shims.rs +++ b/tests/mir-opt/async_closure_shims.rs @@ -1,6 +1,5 @@ //@ edition:2021 // skip-filecheck -// EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![feature(async_closure, noop_waker, async_fn_traits)] #![allow(unused)] @@ -22,7 +21,7 @@ pub fn block_on(fut: impl Future) -> T { } } -async fn call(f: &mut impl AsyncFn(i32)) { +async fn call(f: &impl AsyncFn(i32)) { f(0).await; } @@ -43,10 +42,12 @@ async fn call_normal_mut>(f: &mut impl FnMut(i32) -> F) { } // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}.coroutine_closure_by_move.0.mir -// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.coroutine_by_move.0.mir +// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir +// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#0}-{closure#1}.built.after.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_ref.0.mir // EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}.coroutine_closure_by_move.0.mir -// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.coroutine_by_move.0.mir +// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir +// EMIT_MIR async_closure_shims.main-{closure#0}-{closure#1}-{closure#1}.built.after.mir pub fn main() { block_on(async { let b = 2i32; @@ -54,7 +55,7 @@ pub fn main() { let a = &a; let b = &b; }; - call(&mut async_closure).await; + call(&async_closure).await; call_mut(&mut async_closure).await; call_once(async_closure).await; diff --git a/tests/run-make/clear-error-blank-output/rmake.rs b/tests/run-make/clear-error-blank-output/rmake.rs index e0f042cf805b5..22e030691f289 100644 --- a/tests/run-make/clear-error-blank-output/rmake.rs +++ b/tests/run-make/clear-error-blank-output/rmake.rs @@ -8,6 +8,6 @@ use run_make_support::rustc; fn main() { - let output = rustc().output("").stdin(b"fn main() {}").run_fail(); + let output = rustc().output("").stdin_buf(b"fn main() {}").run_fail(); output.assert_stderr_not_contains("panic"); } diff --git a/tests/run-make/comment-section/rmake.rs b/tests/run-make/comment-section/rmake.rs index 1557f50dbd024..ccfc38e870d88 100644 --- a/tests/run-make/comment-section/rmake.rs +++ b/tests/run-make/comment-section/rmake.rs @@ -14,7 +14,7 @@ fn main() { rustc() .arg("-") - .stdin("fn main() {}") + .stdin_buf("fn main() {}") .emit("link,obj") .arg("-Csave-temps") .target(target) diff --git a/tests/run-make/compile-stdin/rmake.rs b/tests/run-make/compile-stdin/rmake.rs index f93080dfdc4fe..b8244335855c5 100644 --- a/tests/run-make/compile-stdin/rmake.rs +++ b/tests/run-make/compile-stdin/rmake.rs @@ -8,6 +8,6 @@ use run_make_support::{run, rustc}; fn main() { - rustc().arg("-").stdin("fn main() {}").run(); + rustc().arg("-").stdin_buf("fn main() {}").run(); run("rust_out"); } diff --git a/tests/run-make/libtest-junit/rmake.rs b/tests/run-make/libtest-junit/rmake.rs index d631313ed9210..5917660b6c7ff 100644 --- a/tests/run-make/libtest-junit/rmake.rs +++ b/tests/run-make/libtest-junit/rmake.rs @@ -21,7 +21,7 @@ fn run_tests(extra_args: &[&str], expected_file: &str) { .run_fail(); let test_stdout = &cmd_out.stdout_utf8(); - python_command().arg("validate_junit.py").stdin(test_stdout).run(); + python_command().arg("validate_junit.py").stdin_buf(test_stdout).run(); diff() .expected_file(expected_file) diff --git a/tests/run-make/llvm-ident/rmake.rs b/tests/run-make/llvm-ident/rmake.rs index 9699d0579f6ef..47e6fc4de15b3 100644 --- a/tests/run-make/llvm-ident/rmake.rs +++ b/tests/run-make/llvm-ident/rmake.rs @@ -17,7 +17,7 @@ fn main() { .codegen_units(16) .opt_level("2") .target(&env_var("TARGET")) - .stdin("fn main(){}") + .stdin_buf("fn main(){}") .run(); // `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR diff --git a/tests/run-make/llvm-outputs/rmake.rs b/tests/run-make/llvm-outputs/rmake.rs index b8ac21c98a010..632e9a09ba503 100644 --- a/tests/run-make/llvm-outputs/rmake.rs +++ b/tests/run-make/llvm-outputs/rmake.rs @@ -11,8 +11,8 @@ fn main() { let p = cwd(); path_bc = p.join("nonexistant_dir_bc"); path_ir = p.join("nonexistant_dir_ir"); - rustc().input("-").stdin("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run(); - rustc().input("-").stdin("fn main() {}").out_dir(&path_ir).emit("llvm-ir").run(); + rustc().input("-").stdin_buf("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run(); + rustc().input("-").stdin_buf("fn main() {}").out_dir(&path_ir).emit("llvm-ir").run(); assert!(path_bc.exists()); assert!(path_ir.exists()); }); diff --git a/tests/run-make/no-builtins-attribute/rmake.rs b/tests/run-make/no-builtins-attribute/rmake.rs index 1e15b0c03f1d9..7182c65a2c59e 100644 --- a/tests/run-make/no-builtins-attribute/rmake.rs +++ b/tests/run-make/no-builtins-attribute/rmake.rs @@ -9,5 +9,5 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { rustc().input("no_builtins.rs").emit("link").run(); rustc().input("main.rs").emit("llvm-ir").run(); - llvm_filecheck().patterns("filecheck.main.txt").stdin(rfs::read("main.ll")).run(); + llvm_filecheck().patterns("filecheck.main.txt").stdin_buf(rfs::read("main.ll")).run(); } diff --git a/tests/run-make/pgo-branch-weights/rmake.rs b/tests/run-make/pgo-branch-weights/rmake.rs index ef33d009dbb20..105c2fafc5a10 100644 --- a/tests/run-make/pgo-branch-weights/rmake.rs +++ b/tests/run-make/pgo-branch-weights/rmake.rs @@ -35,5 +35,8 @@ fn main() { .codegen_units(1) .emit("llvm-ir") .run(); - llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run(); + llvm_filecheck() + .patterns("filecheck-patterns.txt") + .stdin_buf(rfs::read("interesting.ll")) + .run(); } diff --git a/tests/run-make/pgo-indirect-call-promotion/rmake.rs b/tests/run-make/pgo-indirect-call-promotion/rmake.rs index d0ccfd8a4d7e4..28232eb256621 100644 --- a/tests/run-make/pgo-indirect-call-promotion/rmake.rs +++ b/tests/run-make/pgo-indirect-call-promotion/rmake.rs @@ -29,5 +29,8 @@ fn main() { .codegen_units(1) .emit("llvm-ir") .run(); - llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run(); + llvm_filecheck() + .patterns("filecheck-patterns.txt") + .stdin_buf(rfs::read("interesting.ll")) + .run(); } diff --git a/tests/run-make/pgo-use/rmake.rs b/tests/run-make/pgo-use/rmake.rs index cad49372266e8..276af9ea263a7 100644 --- a/tests/run-make/pgo-use/rmake.rs +++ b/tests/run-make/pgo-use/rmake.rs @@ -51,5 +51,5 @@ fn main() { let lines: Vec<_> = ir.lines().rev().collect(); let mut reversed_ir = lines.join("\n"); reversed_ir.push('\n'); - llvm_filecheck().patterns("filecheck-patterns.txt").stdin(reversed_ir.as_bytes()).run(); + llvm_filecheck().patterns("filecheck-patterns.txt").stdin_buf(reversed_ir.as_bytes()).run(); } diff --git a/tests/run-make/separate-link/rmake.rs b/tests/run-make/separate-link/rmake.rs index e91b25489bc59..0ec7334e75de7 100644 --- a/tests/run-make/separate-link/rmake.rs +++ b/tests/run-make/separate-link/rmake.rs @@ -8,7 +8,7 @@ use run_make_support::{run, rustc}; fn main() { - rustc().stdin(b"fn main(){}").arg("-Zno-link").arg("-").run(); + rustc().stdin_buf(b"fn main(){}").arg("-Zno-link").arg("-").run(); rustc().arg("-Zlink-only").input("rust_out.rlink").run(); run("rust_out"); } diff --git a/tests/run-make/static-pie/rmake.rs b/tests/run-make/static-pie/rmake.rs index 74f86bb8163b0..1557c170f56de 100644 --- a/tests/run-make/static-pie/rmake.rs +++ b/tests/run-make/static-pie/rmake.rs @@ -28,7 +28,7 @@ fn ok_compiler_version(compiler: &str) -> bool { } let compiler_output = - cmd(compiler).stdin(trigger).arg("-").arg("-E").arg("-x").arg("c").run().stdout_utf8(); + cmd(compiler).stdin_buf(trigger).arg("-").arg("-E").arg("-x").arg("c").run().stdout_utf8(); let re = Regex::new(r"(?m)^(\d+)").unwrap(); let version: u32 = re.captures(&compiler_output).unwrap().get(1).unwrap().as_str().parse().unwrap(); diff --git a/tests/run-make/stdin-rustc/rmake.rs b/tests/run-make/stdin-rustc/rmake.rs index 68e9cf2fbd803..2d634dd455eb2 100644 --- a/tests/run-make/stdin-rustc/rmake.rs +++ b/tests/run-make/stdin-rustc/rmake.rs @@ -14,7 +14,7 @@ const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff]; fn main() { // echo $HELLO_WORLD | rustc - - rustc().arg("-").stdin(HELLO_WORLD).run(); + rustc().arg("-").stdin_buf(HELLO_WORLD).run(); assert!( PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" }) .try_exists() @@ -22,7 +22,7 @@ fn main() { ); // echo $NOT_UTF8 | rustc - - rustc().arg("-").stdin(NOT_UTF8).run_fail().assert_stderr_contains( + rustc().arg("-").stdin_buf(NOT_UTF8).run_fail().assert_stderr_contains( "error: couldn't read from stdin, as it did not contain valid UTF-8", ); } diff --git a/tests/run-make/stdin-rustdoc/rmake.rs b/tests/run-make/stdin-rustdoc/rmake.rs index de47a24fbdd4f..30f97b8a2cd75 100644 --- a/tests/run-make/stdin-rustdoc/rmake.rs +++ b/tests/run-make/stdin-rustdoc/rmake.rs @@ -15,11 +15,11 @@ fn main() { let out_dir = PathBuf::from("doc"); // rustdoc - - rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run(); + rustdoc().arg("-").out_dir(&out_dir).stdin_buf(INPUT).run(); assert!(out_dir.join("rust_out/struct.F.html").try_exists().unwrap()); // rustdoc --test - - rustdoc().arg("--test").arg("-").stdin(INPUT).run(); + rustdoc().arg("--test").arg("-").stdin_buf(INPUT).run(); // rustdoc file.rs - rustdoc().arg("file.rs").arg("-").run_fail(); diff --git a/tests/run-make/sysroot-crates-are-unstable/rmake.rs b/tests/run-make/sysroot-crates-are-unstable/rmake.rs index 2240d87237b1a..c81c7fafdab0b 100644 --- a/tests/run-make/sysroot-crates-are-unstable/rmake.rs +++ b/tests/run-make/sysroot-crates-are-unstable/rmake.rs @@ -34,7 +34,7 @@ fn check_crate_is_unstable(cr: &Crate) { .target(target()) .extern_(name, path) .input("-") - .stdin(format!("extern crate {name};")) + .stdin_buf(format!("extern crate {name};")) .run_fail(); // Make sure it failed for the intended reason, not some other reason. diff --git a/tests/run-make/unknown-mod-stdin/rmake.rs b/tests/run-make/unknown-mod-stdin/rmake.rs index 0fe5c78ed0ff0..6be3119c0fd15 100644 --- a/tests/run-make/unknown-mod-stdin/rmake.rs +++ b/tests/run-make/unknown-mod-stdin/rmake.rs @@ -12,7 +12,7 @@ use run_make_support::{diff, rustc}; fn main() { - let out = rustc().crate_type("rlib").stdin(b"mod unknown;").arg("-").run_fail(); + let out = rustc().crate_type("rlib").stdin_buf(b"mod unknown;").arg("-").run_fail(); diff() .actual_text("actual-stdout", out.stdout_utf8()) .expected_file("unknown-mod.stdout") diff --git a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs index 130781a429371..e58762aeb6db7 100644 --- a/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs +++ b/tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs @@ -78,19 +78,23 @@ fn check(func_re: &str, mut checks: &str) { // This is because frame pointers are optional, and them being enabled requires // an additional `popq` in the pattern checking file. if func_re == "std::io::stdio::_print::[[:alnum:]]+" { - let output = llvm_filecheck().stdin(&dump).patterns(checks).run_unchecked(); + let output = llvm_filecheck().stdin_buf(&dump).patterns(checks).run_unchecked(); if !output.status().success() { checks = "print.without_frame_pointers.checks"; - llvm_filecheck().stdin(&dump).patterns(checks).run(); + llvm_filecheck().stdin_buf(&dump).patterns(checks).run(); } } else { - llvm_filecheck().stdin(&dump).patterns(checks).run(); + llvm_filecheck().stdin_buf(&dump).patterns(checks).run(); } if !["rust_plus_one_global_asm", "cmake_plus_one_c_global_asm", "cmake_plus_one_cxx_global_asm"] .contains(&func_re) { // The assembler cannot avoid explicit `ret` instructions. Sequences // of `shlq $0x0, (%rsp); lfence; retq` are used instead. - llvm_filecheck().args(&["--implicit-check-not", "ret"]).stdin(dump).patterns(checks).run(); + llvm_filecheck() + .args(&["--implicit-check-not", "ret"]) + .stdin_buf(dump) + .patterns(checks) + .run(); } } diff --git a/tests/rustdoc-gui/code-example-buttons.goml b/tests/rustdoc-gui/code-example-buttons.goml index 4f037ec79f5e2..c62683b45da9e 100644 --- a/tests/rustdoc-gui/code-example-buttons.goml +++ b/tests/rustdoc-gui/code-example-buttons.goml @@ -94,3 +94,24 @@ call-function: ("check-buttons",{ "filter": "invert(0.5)", "filter_hover": "invert(0.35)", }) + +define-function: ( + "check-buttons-position", + [pre_selector], + block { + move-cursor-to: |pre_selector| + " .rust:not(.item-decl)" + store-position: (|pre_selector| + " .rust:not(.item-decl)", {"x": x, "y": y}) + assert-position: (|pre_selector| + " .rust:not(.item-decl) + .button-holder", { + "y": |y| + 4, + }) + } +) + +call-function: ("check-buttons-position", {"pre_selector": ".example-wrap"}) + +go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html" +// We should work as well for scraped examples. +call-function: ("check-buttons-position", {"pre_selector": ".scraped-example .example-wrap"}) +// And also when the scraped example "title" goes above. +set-window-size: (600, 600) +call-function: ("check-buttons-position", {"pre_selector": ".scraped-example .example-wrap"}) diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml index 348ce0c992fd9..03f8f80b10d4d 100644 --- a/tests/rustdoc-gui/docblock-code-block-line-number.goml +++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml @@ -5,6 +5,18 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html" // We check that without this setting, there is no line number displayed. assert-false: "pre.example-line-numbers" +// All corners should be rounded. +assert-css: ( + ".example-wrap .rust", + { + "border-top-left-radius": "6px", + "border-bottom-left-radius": "6px", + "border-top-right-radius": "6px", + "border-bottom-right-radius": "6px", + }, + ALL, +) + // We set the setting to show the line numbers on code examples. set-local-storage: {"rustdoc-line-numbers": "true"} reload: @@ -29,9 +41,21 @@ define-function: ( "margin": "0px", "padding": "14px 8px", "text-align": "right", + // There should not be a radius on the right of the line numbers. + "border-top-left-radius": "6px", + "border-bottom-left-radius": "6px", + "border-top-right-radius": "0px", + "border-bottom-right-radius": "0px", }, ALL, ) + // There should not be a radius on the left of the line numbers. + assert-css: ("pre.example-line-numbers + .rust", { + "border-top-left-radius": "0px", + "border-bottom-left-radius": "0px", + "border-top-right-radius": "6px", + "border-bottom-right-radius": "6px", + }) }, ) call-function: ("check-colors", { @@ -64,7 +88,56 @@ wait-for: 100 // wait-for-false does not exist assert-false: "pre.example-line-numbers" assert-local-storage: {"rustdoc-line-numbers": "false" } +// Check that the rounded corners are back. +assert-css: ( + ".example-wrap .rust", + { + "border-top-left-radius": "6px", + "border-bottom-left-radius": "6px", + "border-top-right-radius": "6px", + "border-bottom-right-radius": "6px", + }, + ALL, +) + // Finally, turn it on again. click: "input#line-numbers" wait-for: "pre.example-line-numbers" assert-local-storage: {"rustdoc-line-numbers": "true" } + +// Same check with scraped examples line numbers. +go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html" + +assert-css: ( + ".scraped-example .src-line-numbers > pre", + { + // There should not be a radius on the right of the line numbers. + "border-top-left-radius": "6px", + "border-bottom-left-radius": "6px", + "border-top-right-radius": "0px", + "border-bottom-right-radius": "0px", + }, + ALL, +) +assert-css: ( + ".scraped-example .src-line-numbers", + { + // There should not be a radius on the right of the line numbers. + "border-top-left-radius": "6px", + "border-bottom-left-radius": "6px", + "border-top-right-radius": "0px", + "border-bottom-right-radius": "0px", + }, + ALL, +) +assert-css: ( + ".scraped-example .rust", + { + // There should not be a radius on the left of the code. + "border-top-left-radius": "0px", + "border-bottom-left-radius": "0px", + "border-top-right-radius": "6px", + "border-bottom-right-radius": "6px", + }, + ALL, +) diff --git a/tests/rustdoc-gui/scrape-examples-button-focus.goml b/tests/rustdoc-gui/scrape-examples-button-focus.goml index af4293dfc0057..83ed6a219b25a 100644 --- a/tests/rustdoc-gui/scrape-examples-button-focus.goml +++ b/tests/rustdoc-gui/scrape-examples-button-focus.goml @@ -3,29 +3,53 @@ go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html" // The next/prev buttons vertically scroll the code viewport between examples -store-property: (".scraped-example-list > .scraped-example pre", {"scrollTop": initialScrollTop}) +move-cursor-to: ".scraped-example-list > .scraped-example" +store-property: (".scraped-example-list > .scraped-example .src-line-numbers", { + "scrollTop": initialScrollTop, +}) +assert-property: (".scraped-example-list > .scraped-example .rust", { + "scrollTop": |initialScrollTop|, +}) focus: ".scraped-example-list > .scraped-example .next" press-key: "Enter" -assert-property-false: (".scraped-example-list > .scraped-example pre", { +assert-property-false: (".scraped-example-list > .scraped-example .src-line-numbers", { + "scrollTop": |initialScrollTop| +}, NEAR) +assert-property-false: (".scraped-example-list > .scraped-example .rust", { "scrollTop": |initialScrollTop| }, NEAR) focus: ".scraped-example-list > .scraped-example .prev" press-key: "Enter" -assert-property: (".scraped-example-list > .scraped-example pre", { +assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", { + "scrollTop": |initialScrollTop| +}, NEAR) +assert-property: (".scraped-example-list > .scraped-example .rust", { "scrollTop": |initialScrollTop| }, NEAR) // The expand button increases the scrollHeight of the minimized code viewport store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": smallOffsetHeight}) -assert-property-false: (".scraped-example-list > .scraped-example pre", { +assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", { + "scrollHeight": |smallOffsetHeight| +}, NEAR) +assert-property: (".scraped-example-list > .scraped-example .rust", { "scrollHeight": |smallOffsetHeight| }, NEAR) focus: ".scraped-example-list > .scraped-example .expand" press-key: "Enter" -assert-property-false: (".scraped-example-list > .scraped-example pre", { +assert-property-false: (".scraped-example-list > .scraped-example .src-line-numbers", { + "offsetHeight": |smallOffsetHeight| +}, NEAR) +assert-property-false: (".scraped-example-list > .scraped-example .rust", { "offsetHeight": |smallOffsetHeight| }, NEAR) -store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": fullOffsetHeight}) -assert-property: (".scraped-example-list > .scraped-example pre", { +store-property: (".scraped-example-list > .scraped-example .src-line-numbers", { + "offsetHeight": fullOffsetHeight, +}) +assert-property: (".scraped-example-list > .scraped-example .rust", { + "offsetHeight": |fullOffsetHeight|, + "scrollHeight": |fullOffsetHeight|, +}) +assert-property: (".scraped-example-list > .scraped-example .src-line-numbers", { "scrollHeight": |fullOffsetHeight| }, NEAR) diff --git a/tests/rustdoc-gui/scrape-examples-color.goml b/tests/rustdoc-gui/scrape-examples-color.goml index 588ba08a60c7a..b0faca190a578 100644 --- a/tests/rustdoc-gui/scrape-examples-color.goml +++ b/tests/rustdoc-gui/scrape-examples-color.goml @@ -10,10 +10,10 @@ define-function: ( block { call-function: ("switch-theme", {"theme": |theme|}) wait-for: ".more-examples-toggle" - assert-css: (".scraped-example .example-wrap .rust span.highlight:not(.focus)", { + assert-css: (".scraped-example .rust span.highlight:not(.focus)", { "background-color": |highlight|, }, ALL) - assert-css: (".scraped-example .example-wrap .rust span.highlight.focus", { + assert-css: (".scraped-example .rust span.highlight.focus", { "background-color": |highlight_focus|, }, ALL) @@ -67,11 +67,11 @@ define-function: ( [theme, background_color_start, background_color_end], block { call-function: ("switch-theme", {"theme": |theme|}) - assert-css: (".scraped-example:not(.expanded) .code-wrapper::before", { + assert-css: (".scraped-example:not(.expanded) .example-wrap::before", { "background-image": "linear-gradient(" + |background_color_start| + ", " + |background_color_end| + ")", }) - assert-css: (".scraped-example:not(.expanded) .code-wrapper::after", { + assert-css: (".scraped-example:not(.expanded) .example-wrap::after", { "background-image": "linear-gradient(to top, " + |background_color_start| + ", " + |background_color_end| + ")", }) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 4fc1c1ac065f4..6bea352bce4bb 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -1,48 +1,115 @@ // Check that the line number column has the correct layout. go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html" +set-window-size: (1000, 1000) + // Check that it's not zero. assert-property-false: ( - ".more-scraped-examples .scraped-example .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example .src-line-numbers", {"clientWidth": "0"} ) // Check that examples with very long lines have the same width as ones that don't. store-property: ( - ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example:nth-child(2) .src-line-numbers", {"clientWidth": clientWidth}, ) assert-property: ( - ".more-scraped-examples .scraped-example:nth-child(3) .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example:nth-child(3) .src-line-numbers", {"clientWidth": |clientWidth|} ) assert-property: ( - ".more-scraped-examples .scraped-example:nth-child(4) .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example:nth-child(4) .src-line-numbers", {"clientWidth": |clientWidth|} ) assert-property: ( - ".more-scraped-examples .scraped-example:nth-child(5) .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example:nth-child(5) .src-line-numbers", {"clientWidth": |clientWidth|} ) assert-property: ( - ".more-scraped-examples .scraped-example:nth-child(6) .code-wrapper .src-line-numbers", + ".more-scraped-examples .scraped-example:nth-child(6) .src-line-numbers", {"clientWidth": |clientWidth|} ) +// The "title" should be located at the right bottom corner of the code example. +store-position: (".scraped-example .example-wrap", {"x": x, "y": y}) +store-size: (".scraped-example .example-wrap", {"width": width, "height": height}) +store-size: (".scraped-example .scraped-example-title", { + "width": title_width, + "height": title_height, +}) +assert-position: (".scraped-example .scraped-example-title", { + "x": |x| + |width| - |title_width| - 5, + "y": |y| + |height| - |title_height| - 8, +}) + +// Check that the expand button works and also that line number aligns with code. +move-cursor-to: ".scraped-example .rust" +click: ".scraped-example .button-holder .expand" +wait-for: ".scraped-example.expanded" +// They should have the same y position. +compare-elements-position: ( + ".scraped-example.expanded .src-line-numbers pre span", + ".scraped-example.expanded .rust code", + ["y"], +) +// And they should have the same height. +compare-elements-size: ( + ".scraped-example.expanded .src-line-numbers", + ".scraped-example.expanded .rust", + ["height"], +) +// Collapse code again. +click: ".scraped-example .button-holder .expand" + // Check that for both mobile and desktop sizes, the buttons in scraped examples are displayed // correctly. store-value: (offset_y, 4) // First with desktop -assert-position: (".scraped-example .code-wrapper", {"y": 226}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 226 + |offset_y|}) +assert-position: (".scraped-example", {"y": 226}) +assert-position: (".scraped-example .prev", {"y": 226 + |offset_y|}) + +// Gradient background should be at the top of the code block. +assert-css: (".scraped-example .example-wrap::before", {"top": "0px"}) +assert-css: (".scraped-example .example-wrap::after", {"bottom": "0px"}) // Then with mobile set-window-size: (600, 600) -assert-position: (".scraped-example .code-wrapper", {"y": 308}) -assert-position: (".scraped-example .code-wrapper .prev", {"y": 308 + |offset_y|}) +store-size: (".scraped-example .scraped-example-title", {"height": title_height}) +assert-position: (".scraped-example", {"y": 284}) +assert-position: (".scraped-example .prev", {"y": 284 + |offset_y| + |title_height|}) + +define-function: ( + "check_title_and_code_position", + [], + block { + // Title should be above the code. + store-position: (".scraped-example .example-wrap .src-line-numbers", {"x": x, "y": y}) + store-size: (".scraped-example .scraped-example-title", { "height": title_height }) + + assert-position: (".scraped-example .scraped-example-title", { + "x": |x|, // same X position. + "y": |y| - |title_height|, + }) + + // Line numbers should be right beside the code. + compare-elements-position: ( + ".scraped-example .example-wrap .src-line-numbers", + ".scraped-example .example-wrap .rust", + ["y"], + ) + } +) + +// Check that the title is now above the code. +call-function: ("check_title_and_code_position", {}) + +// Then with small mobile +set-window-size: (300, 300) +call-function: ("check_title_and_code_position", {}) diff --git a/tests/rustdoc-gui/src/theme_css/custom-theme.css b/tests/rustdoc-gui/src/theme_css/custom-theme.css index a56c31ab9d26f..366f09f22b230 100644 --- a/tests/rustdoc-gui/src/theme_css/custom-theme.css +++ b/tests/rustdoc-gui/src/theme_css/custom-theme.css @@ -23,6 +23,8 @@ --copy-path-button-color: #999; --copy-path-img-filter: invert(50%); --copy-path-img-hover-filter: invert(35%); + --code-example-button-color: #7f7f7f; + --code-example-button-hover-color: #a5a5a5; --codeblock-error-hover-color: rgb(255, 0, 0); --codeblock-error-color: rgba(255, 0, 0, .5); --codeblock-ignore-hover-color: rgb(255, 142, 0); diff --git a/tests/rustdoc/impl-associated-items-order.rs b/tests/rustdoc/impl-associated-items-order.rs new file mode 100644 index 0000000000000..759e0f0b40095 --- /dev/null +++ b/tests/rustdoc/impl-associated-items-order.rs @@ -0,0 +1,42 @@ +// This test ensures that impl associated items always follow this order: +// +// 1. Consts +// 2. Types +// 3. Functions + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] +#![crate_name = "foo"] + +//@ has 'foo/struct.Bar.html' +pub struct Bar; + +impl Bar { + //@ has - '//*[@id="implementations-list"]//*[@class="impl-items"]/section[3]/h4' \ + // 'pub fn foo()' + pub fn foo() {} + //@ has - '//*[@id="implementations-list"]//*[@class="impl-items"]/section[1]/h4' \ + // 'pub const X: u8 = 12u8' + pub const X: u8 = 12; + //@ has - '//*[@id="implementations-list"]//*[@class="impl-items"]/section[2]/h4' \ + // 'pub type Y = u8' + pub type Y = u8; +} + +pub trait Foo { + const W: u32; + fn yeay(); + type Z; +} + +impl Foo for Bar { + //@ has - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]/section[2]/h4' \ + // 'type Z = u8' + type Z = u8; + //@ has - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]/section[1]/h4' \ + // 'const W: u32 = 12u32' + const W: u32 = 12; + //@ has - '//*[@id="trait-implementations-list"]//*[@class="impl-items"]/section[3]/h4' \ + // 'fn yeay()' + fn yeay() {} +} diff --git a/tests/rustdoc/impl-associated-items-sidebar.rs b/tests/rustdoc/impl-associated-items-sidebar.rs new file mode 100644 index 0000000000000..d393a577e5009 --- /dev/null +++ b/tests/rustdoc/impl-associated-items-sidebar.rs @@ -0,0 +1,42 @@ +// This test ensures that impl/trait associated items are listed in the sidebar. + +// ignore-tidy-linelength + +#![feature(inherent_associated_types)] +#![feature(associated_type_defaults)] +#![allow(incomplete_features)] +#![crate_name = "foo"] + +//@ has 'foo/struct.Bar.html' +pub struct Bar; + +impl Bar { + //@ has - '//*[@class="sidebar-elems"]//h3[1]' 'Associated Constants' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block associatedconstant"]/li/a[@href="#associatedconstant.X"]' 'X' + pub const X: u8 = 12; + //@ has - '//*[@class="sidebar-elems"]//h3[2]' 'Associated Types' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block associatedtype"]/li/a[@href="#associatedtype.Y"]' 'Y' + pub type Y = u8; +} + +//@ has 'foo/trait.Foo.html' +pub trait Foo { + //@ has - '//*[@class="sidebar-elems"]//h3[5]' 'Required Methods' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][5]/li/a[@href="#tymethod.yeay"]' 'yeay' + fn yeay(); + //@ has - '//*[@class="sidebar-elems"]//h3[6]' 'Provided Methods' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][6]/li/a[@href="#method.boo"]' 'boo' + fn boo() {} + //@ has - '//*[@class="sidebar-elems"]//h3[1]' 'Required Associated Constants' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][1]/li/a[@href="#associatedconstant.W"]' 'W' + const W: u32; + //@ has - '//*[@class="sidebar-elems"]//h3[2]' 'Provided Associated Constants' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][2]/li/a[@href="#associatedconstant.U"]' 'U' + const U: u32 = 0; + //@ has - '//*[@class="sidebar-elems"]//h3[3]' 'Required Associated Types' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][3]/li/a[@href="#associatedtype.Z"]' 'Z' + type Z; + //@ has - '//*[@class="sidebar-elems"]//h3[4]' 'Provided Associated Types' + //@ has - '//*[@class="sidebar-elems"]//ul[@class="block"][4]/li/a[@href="#associatedtype.T"]' 'T' + type T = u32; +} diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024-but-fine.rs b/tests/ui/impl-trait/precise-capturing/overcaptures-2024-but-fine.rs new file mode 100644 index 0000000000000..e30f785b0ae33 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024-but-fine.rs @@ -0,0 +1,15 @@ +//@ check-pass + +#![deny(impl_trait_overcaptures)] + +struct Ctxt<'tcx>(&'tcx ()); + +// In `compute`, we don't care that we're "overcapturing" `'tcx` +// in edition 2024, because it can be shortened at the call site +// and we know it outlives `'_`. + +impl<'tcx> Ctxt<'tcx> { + fn compute(&self) -> impl Sized + '_ {} +} + +fn main() {}