mirror of https://github.com/rust-lang/rust.git
Auto merge of #129994 - matthiaskrgr:rollup-zkj4ekl, r=matthiaskrgr
Rollup of 8 pull requests Successful merges: - #128820 (fix: get llvm type of global val) - #129028 (`impl_trait_overcaptures`: Don't worry about uncaptured contravariant lifetimes if they outlive a captured lifetime) - #129471 ([rustdoc] Sort impl associated items by kinds and then by appearance) - #129706 (Rename dump of coroutine by-move-body to be more consistent, fix ICE in dump_mir) - #129720 (Simplify DestProp memory management) - #129796 (Unify scraped examples with other code examples) - #129938 (Elaborate on deriving vs implementing `Copy`) - #129973 (run_make_support: rename `Command::stdin` to `stdin_buf` and add `std{in,out,err}` config helpers) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
9c01301c52
|
@ -390,7 +390,7 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
let val_llty = self.val_ty(v);
|
let val_llty = self.val_ty(v);
|
||||||
|
|
||||||
let g = self.get_static_inner(def_id, val_llty);
|
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 {
|
let g = if val_llty == llty {
|
||||||
g
|
g
|
||||||
|
|
|
@ -974,6 +974,7 @@ unsafe extern "C" {
|
||||||
pub fn LLVMGetAlignment(Global: &Value) -> c_uint;
|
pub fn LLVMGetAlignment(Global: &Value) -> c_uint;
|
||||||
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
pub fn LLVMSetAlignment(Global: &Value, Bytes: c_uint);
|
||||||
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
pub fn LLVMSetDLLStorageClass(V: &Value, C: DLLStorageClass);
|
||||||
|
pub fn LLVMGlobalGetValueType(Global: &Value) -> &Type;
|
||||||
|
|
||||||
// Operations on global variables
|
// Operations on global variables
|
||||||
pub fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>;
|
pub fn LLVMIsAGlobalVariable(GlobalVar: &Value) -> Option<&Value>;
|
||||||
|
|
|
@ -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_data_structures::unord::UnordSet;
|
||||||
use rustc_errors::{Applicability, LintDiagnostic};
|
use rustc_errors::{Applicability, LintDiagnostic};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
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_macros::LintDiagnostic;
|
||||||
use rustc_middle::bug;
|
|
||||||
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
|
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::{
|
use rustc_middle::ty::{
|
||||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||||
};
|
};
|
||||||
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_session::lint::FutureIncompatibilityReason;
|
use rustc_session::lint::FutureIncompatibilityReason;
|
||||||
use rustc_session::{declare_lint, declare_lint_pass};
|
use rustc_session::{declare_lint, declare_lint_pass};
|
||||||
use rustc_span::edition::Edition;
|
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};
|
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) {
|
fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
||||||
let sig = tcx.fn_sig(parent_def_id).instantiate_identity();
|
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
|
// 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());
|
let mut current_def_id = Some(parent_def_id.to_def_id());
|
||||||
while let Some(def_id) = current_def_id {
|
while let Some(def_id) = current_def_id {
|
||||||
let generics = tcx.generics_of(def_id);
|
let generics = tcx.generics_of(def_id);
|
||||||
for param in &generics.own_params {
|
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;
|
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
|
// 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).
|
// vars on the function itself, which we need to count too).
|
||||||
sig.visit_with(&mut VisitOpaqueTypes {
|
sig.visit_with(&mut VisitOpaqueTypes {
|
||||||
|
@ -140,21 +171,45 @@ fn check_fn(tcx: TyCtxt<'_>, parent_def_id: LocalDefId) {
|
||||||
parent_def_id,
|
parent_def_id,
|
||||||
in_scope_parameters,
|
in_scope_parameters,
|
||||||
seen: Default::default(),
|
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>,
|
tcx: TyCtxt<'tcx>,
|
||||||
parent_def_id: LocalDefId,
|
parent_def_id: LocalDefId,
|
||||||
in_scope_parameters: FxIndexSet<DefId>,
|
in_scope_parameters: FxIndexMap<DefId, ParamKind>,
|
||||||
|
variances: LazyCell<FxHashMap<DefId, ty::Variance>, VarFn>,
|
||||||
|
outlives_env: LazyCell<OutlivesEnvironment<'tcx>, OutlivesFn>,
|
||||||
seen: FxIndexSet<LocalDefId>,
|
seen: FxIndexSet<LocalDefId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
impl<'tcx, VarFn, OutlivesFn> TypeVisitor<TyCtxt<'tcx>>
|
||||||
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
|
for VisitOpaqueTypes<'tcx, VarFn, OutlivesFn>
|
||||||
&mut self,
|
where
|
||||||
t: &ty::Binder<'tcx, T>,
|
VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
|
||||||
) -> Self::Result {
|
OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
|
||||||
|
{
|
||||||
|
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
|
||||||
// When we get into a binder, we need to add its own bound vars to the scope.
|
// When we get into a binder, we need to add its own bound vars to the scope.
|
||||||
let mut added = vec![];
|
let mut added = vec![];
|
||||||
for arg in t.bound_vars() {
|
for arg in t.bound_vars() {
|
||||||
|
@ -163,8 +218,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
||||||
ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..))
|
ty::BoundVariableKind::Region(ty::BoundRegionKind::BrNamed(def_id, ..))
|
||||||
| ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => {
|
| ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, _)) => {
|
||||||
added.push(def_id);
|
added.push(def_id);
|
||||||
let unique = self.in_scope_parameters.insert(def_id);
|
let unique = self.in_scope_parameters.insert(def_id, ParamKind::Late);
|
||||||
assert!(unique);
|
assert_eq!(unique, None);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx.dcx().span_delayed_bug(
|
self.tcx.dcx().span_delayed_bug(
|
||||||
|
@ -184,7 +239,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> 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() {
|
if !t.has_aliases() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -207,89 +262,126 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
||||||
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
|
&& let hir::OpaqueTyOrigin::FnReturn(parent_def_id) = opaque.origin
|
||||||
&& parent_def_id == self.parent_def_id
|
&& 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 opaque_span = self.tcx.def_span(opaque_def_id);
|
||||||
let new_capture_rules =
|
let new_capture_rules =
|
||||||
opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024;
|
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
|
if !new_capture_rules
|
||||||
&& !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
|
&& !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
|
||||||
&& !uncaptured_spans.is_empty()
|
|
||||||
{
|
{
|
||||||
let suggestion = if let Ok(snippet) =
|
// Compute the set of args that are captured by the opaque...
|
||||||
self.tcx.sess.source_map().span_to_snippet(opaque_span)
|
let mut captured = FxIndexSet::default();
|
||||||
&& snippet.starts_with("impl ")
|
let mut captured_regions = FxIndexSet::default();
|
||||||
{
|
let variances = self.tcx.variances_of(opaque_def_id);
|
||||||
let (lifetimes, others): (Vec<_>, Vec<_>) = captured
|
let mut current_def_id = Some(opaque_def_id.to_def_id());
|
||||||
.into_iter()
|
while let Some(def_id) = current_def_id {
|
||||||
.partition(|def_id| self.tcx.def_kind(*def_id) == DefKind::LifetimeParam);
|
let generics = self.tcx.generics_of(def_id);
|
||||||
// Take all lifetime params first, then all others (ty/ct).
|
for param in &generics.own_params {
|
||||||
let generics: Vec<_> = lifetimes
|
// A param is captured if it's invariant.
|
||||||
.into_iter()
|
if variances[param.index as usize] != ty::Invariant {
|
||||||
.chain(others)
|
continue;
|
||||||
.map(|def_id| self.tcx.item_name(def_id).to_string())
|
}
|
||||||
.collect();
|
|
||||||
// Make sure that we're not trying to name any APITs
|
let arg = opaque_ty.args[param.index as usize];
|
||||||
if generics.iter().all(|name| !name.starts_with("impl ")) {
|
// We need to turn all `ty::Param`/`ConstKind::Param` and
|
||||||
Some((
|
// `ReEarlyParam`/`ReBound` into def ids.
|
||||||
format!(" + use<{}>", generics.join(", ")),
|
captured.insert(extract_def_id_from_arg(self.tcx, generics, arg));
|
||||||
opaque_span.shrink_to_hi(),
|
|
||||||
))
|
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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
};
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tcx.emit_node_span_lint(
|
let uncaptured_spans: Vec<_> = uncaptured_args
|
||||||
IMPL_TRAIT_OVERCAPTURES,
|
.into_iter()
|
||||||
self.tcx.local_def_id_to_hir_id(opaque_def_id),
|
.map(|(def_id, _)| self.tcx.def_span(def_id))
|
||||||
opaque_span,
|
.collect();
|
||||||
ImplTraitOvercapturesLint {
|
|
||||||
self_ty: t,
|
self.tcx.emit_node_span_lint(
|
||||||
num_captured: uncaptured_spans.len(),
|
IMPL_TRAIT_OVERCAPTURES,
|
||||||
uncaptured_spans,
|
self.tcx.local_def_id_to_hir_id(opaque_def_id),
|
||||||
suggestion,
|
opaque_span,
|
||||||
},
|
ImplTraitOvercapturesLint {
|
||||||
);
|
self_ty: t,
|
||||||
|
num_captured: uncaptured_spans.len(),
|
||||||
|
uncaptured_spans,
|
||||||
|
suggestion,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, if we are edition 2024, have `use<>` syntax, and
|
// Otherwise, if we are edition 2024, have `use<>` syntax, and
|
||||||
// have no uncaptured args, then we should warn to the user that
|
// have no uncaptured args, then we should warn to the user that
|
||||||
// it's redundant to capture all args explicitly.
|
// it's redundant to capture all args explicitly.
|
||||||
else if new_capture_rules
|
if new_capture_rules
|
||||||
&& let Some((captured_args, capturing_span)) =
|
&& let Some((captured_args, capturing_span)) =
|
||||||
opaque.bounds.iter().find_map(|bound| match *bound {
|
opaque.bounds.iter().find_map(|bound| match *bound {
|
||||||
hir::GenericBound::Use(a, s) => Some((a, s)),
|
hir::GenericBound::Use(a, s) => Some((a, s)),
|
||||||
|
@ -327,7 +419,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
|
||||||
if self
|
if self
|
||||||
.in_scope_parameters
|
.in_scope_parameters
|
||||||
.iter()
|
.iter()
|
||||||
.all(|def_id| explicitly_captured.contains(def_id))
|
.all(|(def_id, _)| explicitly_captured.contains(def_id))
|
||||||
{
|
{
|
||||||
self.tcx.emit_node_span_lint(
|
self.tcx.emit_node_span_lint(
|
||||||
IMPL_TRAIT_REDUNDANT_CAPTURES,
|
IMPL_TRAIT_REDUNDANT_CAPTURES,
|
||||||
|
@ -396,7 +488,11 @@ fn extract_def_id_from_arg<'tcx>(
|
||||||
ty::ReBound(
|
ty::ReBound(
|
||||||
_,
|
_,
|
||||||
ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, ..), .. },
|
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!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
ty::GenericArgKind::Type(ty) => {
|
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<DefId, ty::Variance>,
|
||||||
|
ambient_variance: ty::Variance,
|
||||||
|
generics: &'tcx ty::Generics,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for FunctionalVariances<'tcx> {
|
||||||
|
fn cx(&self) -> TyCtxt<'tcx> {
|
||||||
|
self.tcx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relate_with_variance<T: ty::relate::Relate<TyCtxt<'tcx>>>(
|
||||||
|
&mut self,
|
||||||
|
variance: rustc_type_ir::Variance,
|
||||||
|
_: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
|
||||||
|
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<T>(
|
||||||
|
&mut self,
|
||||||
|
a: ty::Binder<'tcx, T>,
|
||||||
|
b: ty::Binder<'tcx, T>,
|
||||||
|
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
|
||||||
|
where
|
||||||
|
T: Relate<TyCtxt<'tcx>>,
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||||
#![doc(rust_logo)]
|
#![doc(rust_logo)]
|
||||||
#![feature(array_windows)]
|
#![feature(array_windows)]
|
||||||
|
#![feature(assert_matches)]
|
||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(extract_if)]
|
#![feature(extract_if)]
|
||||||
|
|
|
@ -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 def_id = body.source.def_id();
|
||||||
let kind = tcx.def_kind(def_id);
|
let kind = tcx.def_kind(def_id);
|
||||||
let is_function = match kind {
|
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),
|
_ => tcx.is_closure_like(def_id),
|
||||||
};
|
};
|
||||||
match (kind, body.source.promoted) {
|
match (kind, body.source.promoted) {
|
||||||
|
|
|
@ -207,11 +207,12 @@ pub fn coroutine_by_move_body_def_id<'tcx>(
|
||||||
|
|
||||||
let mut by_move_body = body.clone();
|
let mut by_move_body = body.clone();
|
||||||
MakeByMoveBody { tcx, field_remapping, by_move_coroutine_ty }.visit_body(&mut by_move_body);
|
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 =
|
by_move_body.source =
|
||||||
mir::MirSource::from_instance(InstanceKind::Item(body_def.def_id().to_def_id()));
|
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.
|
// Inherited from the by-ref coroutine.
|
||||||
body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone());
|
body_def.codegen_fn_attrs(tcx.codegen_fn_attrs(coroutine_def_id).clone());
|
||||||
|
|
|
@ -163,7 +163,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
let def_id = body.source.def_id();
|
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));
|
trace!(func = ?tcx.def_path_str(def_id));
|
||||||
|
|
||||||
let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body);
|
let borrowed = rustc_mir_dataflow::impls::borrowed_locals(body);
|
||||||
|
@ -191,12 +192,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
|
||||||
loop {
|
loop {
|
||||||
// PERF: Can we do something smarter than recalculating the candidates and liveness
|
// PERF: Can we do something smarter than recalculating the candidates and liveness
|
||||||
// results?
|
// results?
|
||||||
let mut candidates = find_candidates(
|
candidates.reset_and_find(body, &borrowed);
|
||||||
body,
|
|
||||||
&borrowed,
|
|
||||||
&mut allocations.candidates,
|
|
||||||
&mut allocations.candidates_reverse,
|
|
||||||
);
|
|
||||||
trace!(?candidates);
|
trace!(?candidates);
|
||||||
dest_prop_mir_dump(tcx, body, &points, &live, round_count);
|
dest_prop_mir_dump(tcx, body, &points, &live, round_count);
|
||||||
|
|
||||||
|
@ -204,7 +200,7 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
|
||||||
&mut candidates,
|
&mut candidates,
|
||||||
&points,
|
&points,
|
||||||
&live,
|
&live,
|
||||||
&mut allocations.write_info,
|
&mut write_info,
|
||||||
body,
|
body,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -253,20 +249,8 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Container for the various allocations that we need.
|
#[derive(Debug, Default)]
|
||||||
///
|
struct Candidates {
|
||||||
/// 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<Local, Vec<Local>>,
|
|
||||||
candidates_reverse: FxIndexMap<Local, Vec<Local>>,
|
|
||||||
write_info: WriteInfo,
|
|
||||||
// PERF: Do this for `MaybeLiveLocals` allocations too.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Candidates<'alloc> {
|
|
||||||
/// The set of candidates we are considering in this optimization.
|
/// The set of candidates we are considering in this optimization.
|
||||||
///
|
///
|
||||||
/// We will always merge the key into at most one of its values.
|
/// 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
|
/// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
|
||||||
/// remove that assignment.
|
/// remove that assignment.
|
||||||
c: &'alloc mut FxIndexMap<Local, Vec<Local>>,
|
c: FxIndexMap<Local, Vec<Local>>,
|
||||||
|
|
||||||
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
|
/// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
|
||||||
/// then this contains `b => a`.
|
/// then this contains `b => a`.
|
||||||
// PERF: Possibly these should be `SmallVec`s?
|
// PERF: Possibly these should be `SmallVec`s?
|
||||||
reverse: &'alloc mut FxIndexMap<Local, Vec<Local>>,
|
reverse: FxIndexMap<Local, Vec<Local>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////
|
||||||
|
@ -358,19 +343,40 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Merger<'a, 'tcx> {
|
||||||
//
|
//
|
||||||
// This section enforces bullet point 2
|
// This section enforces bullet point 2
|
||||||
|
|
||||||
struct FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
struct FilterInformation<'a, 'tcx> {
|
||||||
body: &'body Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
points: &'a DenseLocationMap,
|
points: &'a DenseLocationMap,
|
||||||
live: &'a SparseIntervalMatrix<Local, PointIndex>,
|
live: &'a SparseIntervalMatrix<Local, PointIndex>,
|
||||||
candidates: &'a mut Candidates<'alloc>,
|
candidates: &'a mut Candidates,
|
||||||
write_info: &'alloc mut WriteInfo,
|
write_info: &'a mut WriteInfo,
|
||||||
at: Location,
|
at: Location,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We first implement some utility functions which we will expose removing candidates according to
|
// 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
|
// different needs. Throughout the liveness filtering, the `candidates` are only ever accessed
|
||||||
// through these methods, and not directly.
|
// 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<Local>) {
|
||||||
|
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
|
/// Just `Vec::retain`, but the condition is inverted and we add debugging output
|
||||||
fn vec_filter_candidates(
|
fn vec_filter_candidates(
|
||||||
src: Local,
|
src: Local,
|
||||||
|
@ -445,7 +451,7 @@ enum CandidateFilter {
|
||||||
Remove,
|
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.
|
/// 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
|
/// 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
|
/// 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
|
/// statement/terminator to be live. We are additionally conservative by treating all written to
|
||||||
/// locals as also being read from.
|
/// locals as also being read from.
|
||||||
fn filter_liveness<'b>(
|
fn filter_liveness(
|
||||||
candidates: &mut Candidates<'alloc>,
|
candidates: &mut Candidates,
|
||||||
points: &DenseLocationMap,
|
points: &DenseLocationMap,
|
||||||
live: &SparseIntervalMatrix<Local, PointIndex>,
|
live: &SparseIntervalMatrix<Local, PointIndex>,
|
||||||
write_info_alloc: &'alloc mut WriteInfo,
|
write_info: &mut WriteInfo,
|
||||||
body: &'b Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
) {
|
) {
|
||||||
let mut this = FilterInformation {
|
let mut this = FilterInformation {
|
||||||
body,
|
body,
|
||||||
|
@ -477,7 +483,7 @@ impl<'a, 'body, 'alloc, 'tcx> FilterInformation<'a, 'body, 'alloc, 'tcx> {
|
||||||
candidates,
|
candidates,
|
||||||
// We don't actually store anything at this scope, we just keep things here to be able
|
// We don't actually store anything at this scope, we just keep things here to be able
|
||||||
// to reuse the allocation.
|
// to reuse the allocation.
|
||||||
write_info: write_info_alloc,
|
write_info,
|
||||||
// Doesn't matter what we put here, will be overwritten before being used
|
// Doesn't matter what we put here, will be overwritten before being used
|
||||||
at: Location::START,
|
at: Location::START,
|
||||||
};
|
};
|
||||||
|
@ -734,40 +740,13 @@ fn places_to_candidate_pair<'tcx>(
|
||||||
Some((a, b))
|
Some((a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collects the candidates for merging
|
struct FindAssignments<'a, 'tcx> {
|
||||||
///
|
|
||||||
/// This is responsible for enforcing the first and third bullet point.
|
|
||||||
fn find_candidates<'alloc, 'tcx>(
|
|
||||||
body: &Body<'tcx>,
|
|
||||||
borrowed: &BitSet<Local>,
|
|
||||||
candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
|
|
||||||
candidates_reverse: &'alloc mut FxIndexMap<Local, Vec<Local>>,
|
|
||||||
) -> 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> {
|
|
||||||
body: &'a Body<'tcx>,
|
body: &'a Body<'tcx>,
|
||||||
candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
|
candidates: &'a mut FxIndexMap<Local, Vec<Local>>,
|
||||||
borrowed: &'a BitSet<Local>,
|
borrowed: &'a BitSet<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
|
impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
|
||||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
||||||
if let StatementKind::Assign(box (
|
if let StatementKind::Assign(box (
|
||||||
lhs,
|
lhs,
|
||||||
|
@ -819,9 +798,9 @@ fn is_local_required(local: Local, body: &Body<'_>) -> bool {
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// MIR Dump
|
// MIR Dump
|
||||||
|
|
||||||
fn dest_prop_mir_dump<'body, 'tcx>(
|
fn dest_prop_mir_dump<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &'body Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
points: &DenseLocationMap,
|
points: &DenseLocationMap,
|
||||||
live: &SparseIntervalMatrix<Local, PointIndex>,
|
live: &SparseIntervalMatrix<Local, PointIndex>,
|
||||||
round: usize,
|
round: usize,
|
||||||
|
|
|
@ -288,8 +288,19 @@ marker_impls! {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// There is a small difference between the two: the `derive` strategy will also place a `Copy`
|
/// 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.
|
/// bound on type parameters:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #[derive(Clone)]
|
||||||
|
/// struct MyStruct<T>(T);
|
||||||
|
///
|
||||||
|
/// impl<T: Copy> Copy for MyStruct<T> { }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// 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`?
|
/// ## What's the difference between `Copy` and `Clone`?
|
||||||
///
|
///
|
||||||
|
|
|
@ -1794,13 +1794,64 @@ fn render_impl(
|
||||||
let mut default_impl_items = Buffer::empty_from(w);
|
let mut default_impl_items = Buffer::empty_from(w);
|
||||||
let impl_ = i.inner_impl();
|
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() {
|
if !impl_.is_negative_trait_impl() {
|
||||||
for trait_item in &impl_.items {
|
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(
|
doc_impl_item(
|
||||||
&mut default_impl_items,
|
&mut default_impl_items,
|
||||||
&mut impl_items,
|
&mut impl_items,
|
||||||
cx,
|
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 },
|
if trait_.is_some() { &i.impl_item } else { parent },
|
||||||
link,
|
link,
|
||||||
render_mode,
|
render_mode,
|
||||||
|
@ -2455,28 +2506,6 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c
|
||||||
let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
|
let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
|
||||||
let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
|
let locations_encoded = serde_json::to_string(&line_ranges).unwrap();
|
||||||
|
|
||||||
write!(
|
|
||||||
&mut w,
|
|
||||||
"<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\
|
|
||||||
<div class=\"scraped-example-title\">\
|
|
||||||
{name} (<a href=\"{url}\">{title}</a>)\
|
|
||||||
</div>\
|
|
||||||
<div class=\"code-wrapper\">",
|
|
||||||
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#"<button class="prev">≺</button> <button class="next">≻</button>"#)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for the example file in the source map if it exists, otherwise return a dummy span
|
// Look for the example file in the source map if it exists, otherwise return a dummy span
|
||||||
let file_span = (|| {
|
let file_span = (|| {
|
||||||
let source_map = tcx.sess.source_map();
|
let source_map = tcx.sess.source_map();
|
||||||
|
@ -2507,9 +2536,16 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c
|
||||||
cx,
|
cx,
|
||||||
&cx.root_path(),
|
&cx.root_path(),
|
||||||
highlight::DecorationInfo(decoration_info),
|
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("</div></div>").unwrap();
|
|
||||||
|
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
|
@ -843,33 +843,6 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !required_types.is_empty() {
|
|
||||||
write_section_heading(
|
|
||||||
w,
|
|
||||||
"Required Associated Types",
|
|
||||||
"required-associated-types",
|
|
||||||
None,
|
|
||||||
"<div class=\"methods\">",
|
|
||||||
);
|
|
||||||
for t in required_types {
|
|
||||||
trait_item(w, cx, t, it);
|
|
||||||
}
|
|
||||||
w.write_str("</div>");
|
|
||||||
}
|
|
||||||
if !provided_types.is_empty() {
|
|
||||||
write_section_heading(
|
|
||||||
w,
|
|
||||||
"Provided Associated Types",
|
|
||||||
"provided-associated-types",
|
|
||||||
None,
|
|
||||||
"<div class=\"methods\">",
|
|
||||||
);
|
|
||||||
for t in provided_types {
|
|
||||||
trait_item(w, cx, t, it);
|
|
||||||
}
|
|
||||||
w.write_str("</div>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if !required_consts.is_empty() {
|
if !required_consts.is_empty() {
|
||||||
write_section_heading(
|
write_section_heading(
|
||||||
w,
|
w,
|
||||||
|
@ -897,6 +870,33 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
|
||||||
w.write_str("</div>");
|
w.write_str("</div>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !required_types.is_empty() {
|
||||||
|
write_section_heading(
|
||||||
|
w,
|
||||||
|
"Required Associated Types",
|
||||||
|
"required-associated-types",
|
||||||
|
None,
|
||||||
|
"<div class=\"methods\">",
|
||||||
|
);
|
||||||
|
for t in required_types {
|
||||||
|
trait_item(w, cx, t, it);
|
||||||
|
}
|
||||||
|
w.write_str("</div>");
|
||||||
|
}
|
||||||
|
if !provided_types.is_empty() {
|
||||||
|
write_section_heading(
|
||||||
|
w,
|
||||||
|
"Provided Associated Types",
|
||||||
|
"provided-associated-types",
|
||||||
|
None,
|
||||||
|
"<div class=\"methods\">",
|
||||||
|
);
|
||||||
|
for t in provided_types {
|
||||||
|
trait_item(w, cx, t, it);
|
||||||
|
}
|
||||||
|
w.write_str("</div>");
|
||||||
|
}
|
||||||
|
|
||||||
// Output the documentation for each function individually
|
// Output the documentation for each function individually
|
||||||
if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
|
if !required_methods.is_empty() || must_implement_one_of_functions.is_some() {
|
||||||
write_section_heading(
|
write_section_heading(
|
||||||
|
|
|
@ -302,10 +302,10 @@ fn sidebar_trait<'a>(
|
||||||
|
|
||||||
blocks.extend(
|
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),
|
("required-associated-consts", "Required Associated Constants", req_assoc_const),
|
||||||
("provided-associated-consts", "Provided Associated Constants", prov_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),
|
("required-methods", "Required Methods", req_method),
|
||||||
("provided-methods", "Provided Methods", prov_method),
|
("provided-methods", "Provided Methods", prov_method),
|
||||||
("foreign-impls", "Implementations on Foreign Types", foreign_impls),
|
("foreign-impls", "Implementations on Foreign Types", foreign_impls),
|
||||||
|
@ -394,6 +394,7 @@ fn sidebar_assoc_items<'a>(
|
||||||
let cache = cx.cache();
|
let cache = cx.cache();
|
||||||
|
|
||||||
let mut assoc_consts = Vec::new();
|
let mut assoc_consts = Vec::new();
|
||||||
|
let mut assoc_types = Vec::new();
|
||||||
let mut methods = Vec::new();
|
let mut methods = Vec::new();
|
||||||
if let Some(v) = cache.impls.get(&did) {
|
if let Some(v) = cache.impls.get(&did) {
|
||||||
let mut used_links = FxHashSet::default();
|
let mut used_links = FxHashSet::default();
|
||||||
|
@ -401,22 +402,14 @@ fn sidebar_assoc_items<'a>(
|
||||||
|
|
||||||
{
|
{
|
||||||
let used_links_bor = &mut used_links;
|
let used_links_bor = &mut used_links;
|
||||||
assoc_consts.extend(
|
for impl_ in v.iter().map(|i| i.inner_impl()).filter(|i| i.trait_.is_none()) {
|
||||||
v.iter()
|
assoc_consts.extend(get_associated_constants(impl_, used_links_bor));
|
||||||
.filter(|i| i.inner_impl().trait_.is_none())
|
assoc_types.extend(get_associated_types(impl_, used_links_bor));
|
||||||
.flat_map(|i| get_associated_constants(i.inner_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.
|
// We want links' order to be reproducible so we don't use unstable sort.
|
||||||
assoc_consts.sort();
|
assoc_consts.sort();
|
||||||
|
assoc_types.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.
|
|
||||||
methods.sort();
|
methods.sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,6 +419,11 @@ fn sidebar_assoc_items<'a>(
|
||||||
"associatedconstant",
|
"associatedconstant",
|
||||||
assoc_consts,
|
assoc_consts,
|
||||||
),
|
),
|
||||||
|
LinkBlock::new(
|
||||||
|
Link::new("implementations", "Associated Types"),
|
||||||
|
"associatedtype",
|
||||||
|
assoc_types,
|
||||||
|
),
|
||||||
LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
|
LinkBlock::new(Link::new("implementations", "Methods"), "method", methods),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -452,6 +450,7 @@ fn sidebar_assoc_items<'a>(
|
||||||
&mut blocks,
|
&mut blocks,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
links.append(&mut blocks);
|
links.append(&mut blocks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -715,3 +714,19 @@ fn get_associated_constants<'a>(
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_associated_types<'a>(
|
||||||
|
i: &'a clean::Impl,
|
||||||
|
used_links: &mut FxHashSet<String>,
|
||||||
|
) -> Vec<Link<'a>> {
|
||||||
|
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::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
|
@ -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<usize>,
|
||||||
|
code_html: Code,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "source.html")]
|
||||||
|
struct Source<Code: std::fmt::Display> {
|
||||||
|
lines: RangeInclusive<usize>,
|
||||||
|
code_html: Code,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum SourceContext<'a> {
|
||||||
Standalone,
|
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
|
/// 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<'_>,
|
context: &Context<'_>,
|
||||||
root_path: &str,
|
root_path: &str,
|
||||||
decoration_info: highlight::DecorationInfo,
|
decoration_info: highlight::DecorationInfo,
|
||||||
source_context: SourceContext,
|
source_context: SourceContext<'_>,
|
||||||
) {
|
) {
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "source.html")]
|
|
||||||
struct Source<Code: std::fmt::Display> {
|
|
||||||
embedded: bool,
|
|
||||||
needs_expansion: bool,
|
|
||||||
lines: RangeInclusive<usize>,
|
|
||||||
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
|
let current_href = context
|
||||||
.href_from_span(clean::Span::new(file_span), false)
|
.href_from_span(clean::Span::new(file_span), false)
|
||||||
.expect("only local crates should have sources emitted");
|
.expect("only local crates should have sources emitted");
|
||||||
|
@ -333,5 +343,14 @@ pub(crate) fn print_src(
|
||||||
);
|
);
|
||||||
Ok(())
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,8 @@ nav.sub {
|
||||||
--copy-path-button-color: #999;
|
--copy-path-button-color: #999;
|
||||||
--copy-path-img-filter: invert(50%);
|
--copy-path-img-filter: invert(50%);
|
||||||
--copy-path-img-hover-filter: invert(35%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||||
|
@ -162,6 +164,8 @@ nav.sub {
|
||||||
--copy-path-button-color: #999;
|
--copy-path-button-color: #999;
|
||||||
--copy-path-img-filter: invert(50%);
|
--copy-path-img-filter: invert(50%);
|
||||||
--copy-path-img-hover-filter: invert(65%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||||
|
|
|
@ -378,7 +378,7 @@ pre.item-decl {
|
||||||
.src .content pre {
|
.src .content pre {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
.rustdoc.src .example-wrap pre.src-line-numbers {
|
.rustdoc.src .example-wrap .src-line-numbers {
|
||||||
padding: 20px 0 20px 4px;
|
padding: 20px 0 20px 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,10 +757,32 @@ ul.block, .block li, .block ul {
|
||||||
margin-bottom: 10px;
|
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;
|
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
|
/* For the last child of a div, the margin will be taken care of
|
||||||
by the margin-top of the next item. */
|
by the margin-top of the next item. */
|
||||||
.rustdoc .example-wrap:last-child {
|
.rustdoc .example-wrap:last-child {
|
||||||
|
@ -772,15 +794,36 @@ ul.block, .block li, .block ul {
|
||||||
flex-grow: 1;
|
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;
|
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.example-line-numbers,
|
||||||
.rustdoc .example-wrap pre.src-line-numbers {
|
.rustdoc .example-wrap .src-line-numbers {
|
||||||
flex-grow: 0;
|
|
||||||
min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */
|
min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */
|
||||||
overflow: initial;
|
flex-grow: 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
@ -788,7 +831,7 @@ ul.block, .block li, .block ul {
|
||||||
color: var(--src-line-numbers-span-color);
|
color: var(--src-line-numbers-span-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rustdoc .example-wrap pre.src-line-numbers {
|
.rustdoc .scraped-example .src-line-numbers {
|
||||||
padding: 14px 0;
|
padding: 14px 0;
|
||||||
}
|
}
|
||||||
.src-line-numbers a, .src-line-numbers span {
|
.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 {
|
.example-wrap .button-holder.keep-visible {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
.example-wrap .button-holder .copy-button, .example-wrap .test-arrow {
|
.example-wrap .button-holder > * {
|
||||||
background: var(--main-background-color);
|
background: var(--main-background-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: var(--button-border-radius);
|
border-radius: var(--button-border-radius);
|
||||||
height: var(--copy-path-height);
|
height: var(--copy-path-height);
|
||||||
width: var(--copy-path-width);
|
width: var(--copy-path-width);
|
||||||
|
border: 0;
|
||||||
|
color: var(--code-example-button-color);
|
||||||
|
}
|
||||||
|
.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 {
|
.example-wrap .button-holder .copy-button {
|
||||||
margin-left: var(--button-left-margin);
|
|
||||||
padding: 2px 0 0 4px;
|
padding: 2px 0 0 4px;
|
||||||
border: 0;
|
|
||||||
}
|
}
|
||||||
.example-wrap .button-holder .copy-button::before,
|
.example-wrap .button-holder .copy-button::before,
|
||||||
.example-wrap .test-arrow::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. */
|
/* Should have min-width: (N + 1)px where N is the mobile breakpoint above. */
|
||||||
@media (min-width: 701px) {
|
@media (min-width: 701px) {
|
||||||
/* Places file-link for a scraped example on top of the example to save space.
|
/* 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);
|
color: var(--scrape-example-help-hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example {
|
.scraped-example:not(.expanded) .example-wrap::before,
|
||||||
/* So .scraped-example-title can be positioned absolutely */
|
.scraped-example:not(.expanded) .example-wrap::after {
|
||||||
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 {
|
|
||||||
content: " ";
|
content: " ";
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 5px;
|
height: 5px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.scraped-example:not(.expanded) .code-wrapper::before {
|
.scraped-example:not(.expanded) .example-wrap::before {
|
||||||
top: 0;
|
top: 0;
|
||||||
background: linear-gradient(to bottom,
|
background: linear-gradient(to bottom,
|
||||||
var(--scrape-example-code-wrapper-background-start),
|
var(--scrape-example-code-wrapper-background-start),
|
||||||
var(--scrape-example-code-wrapper-background-end));
|
var(--scrape-example-code-wrapper-background-end));
|
||||||
}
|
}
|
||||||
.scraped-example:not(.expanded) .code-wrapper::after {
|
.scraped-example:not(.expanded) .example-wrap::after {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: linear-gradient(to top,
|
background: linear-gradient(to top,
|
||||||
var(--scrape-example-code-wrapper-background-start),
|
var(--scrape-example-code-wrapper-background-start),
|
||||||
var(--scrape-example-code-wrapper-background-end));
|
var(--scrape-example-code-wrapper-background-end));
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example .code-wrapper .example-wrap {
|
.scraped-example:not(.expanded) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example:not(.expanded) .code-wrapper .example-wrap {
|
.scraped-example:not(.expanded) {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scraped-example .example-wrap .rust span.highlight {
|
.scraped-example .rust span.highlight {
|
||||||
background: var(--scrape-example-code-line-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);
|
background: var(--scrape-example-code-line-highlight-focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2542,6 +2534,8 @@ by default.
|
||||||
--copy-path-button-color: #999;
|
--copy-path-button-color: #999;
|
||||||
--copy-path-img-filter: invert(50%);
|
--copy-path-img-filter: invert(50%);
|
||||||
--copy-path-img-hover-filter: invert(35%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||||
|
@ -2644,6 +2638,8 @@ by default.
|
||||||
--copy-path-button-color: #999;
|
--copy-path-button-color: #999;
|
||||||
--copy-path-img-filter: invert(50%);
|
--copy-path-img-filter: invert(50%);
|
||||||
--copy-path-img-hover-filter: invert(65%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--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-button-color: #fff;
|
||||||
--copy-path-img-filter: invert(70%);
|
--copy-path-img-filter: invert(70%);
|
||||||
--copy-path-img-hover-filter: invert(100%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||||
|
|
|
@ -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.
|
// Since the button will be added, no need to keep this listener around.
|
||||||
elem.removeEventListener("mouseover", addCopyButton);
|
elem.removeEventListener("mouseover", addCopyButton);
|
||||||
|
|
||||||
const parent = document.createElement("div");
|
// If this is a scrapped example, there will already be a "button-holder" element.
|
||||||
parent.className = "button-holder";
|
let parent = elem.querySelector(".button-holder");
|
||||||
|
if (!parent) {
|
||||||
|
parent = document.createElement("div");
|
||||||
|
parent.className = "button-holder";
|
||||||
|
}
|
||||||
|
|
||||||
const runButton = elem.querySelector(".test-arrow");
|
const runButton = elem.querySelector(".test-arrow");
|
||||||
if (runButton !== null) {
|
if (runButton !== null) {
|
||||||
// If there is a run button, we move it into the same div.
|
// If there is a run button, we move it into the same div.
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
// Scroll code block to the given code location
|
// Scroll code block to the given code location
|
||||||
function scrollToLoc(elt, loc, isHidden) {
|
function scrollToLoc(elt, loc, isHidden) {
|
||||||
const lines = elt.querySelector(".src-line-numbers");
|
const lines = elt.querySelector(".src-line-numbers > pre");
|
||||||
let scrollOffset;
|
let scrollOffset;
|
||||||
|
|
||||||
// If the block is greater than the size of the viewer,
|
// If the block is greater than the size of the viewer,
|
||||||
|
@ -24,8 +24,7 @@
|
||||||
const line = Math.max(0, loc[0] - 1);
|
const line = Math.max(0, loc[0] - 1);
|
||||||
scrollOffset = lines.children[line].offsetTop;
|
scrollOffset = lines.children[line].offsetTop;
|
||||||
} else {
|
} else {
|
||||||
const wrapper = elt.querySelector(".code-wrapper");
|
const halfHeight = elt.offsetHeight / 2;
|
||||||
const halfHeight = wrapper.offsetHeight / 2;
|
|
||||||
const offsetTop = lines.children[loc[0]].offsetTop;
|
const offsetTop = lines.children[loc[0]].offsetTop;
|
||||||
const lastLine = lines.children[loc[1]];
|
const lastLine = lines.children[loc[1]];
|
||||||
const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
|
const offsetBot = lastLine.offsetTop + lastLine.offsetHeight;
|
||||||
|
@ -33,7 +32,7 @@
|
||||||
scrollOffset = offsetMid - halfHeight;
|
scrollOffset = offsetMid - halfHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.scrollTo(0, scrollOffset);
|
lines.parentElement.scrollTo(0, scrollOffset);
|
||||||
elt.querySelector(".rust").scrollTo(0, scrollOffset);
|
elt.querySelector(".rust").scrollTo(0, scrollOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<div class="scraped-example{% if !info.needs_expansion +%} expanded{% endif %}" data-locs="{{info.locations}}"> {# #}
|
||||||
|
<div class="scraped-example-title">
|
||||||
|
{{info.name +}} (<a href="{{info.url}}">{{info.title}}</a>) {# #}
|
||||||
|
</div>
|
||||||
|
<div class="example-wrap"> {# #}
|
||||||
|
{# 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. #}
|
||||||
|
<div class="src-line-numbers" data-nosnippet> {# #}
|
||||||
|
<pre>
|
||||||
|
{% for line in lines.clone() %}
|
||||||
|
{# ~#}
|
||||||
|
<span>{{line|safe}}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</pre> {# #}
|
||||||
|
</div> {# #}
|
||||||
|
<pre class="rust"> {# #}
|
||||||
|
<code>
|
||||||
|
{{code_html|safe}}
|
||||||
|
</code> {# #}
|
||||||
|
</pre> {# #}
|
||||||
|
{% if info.needs_prev_next_buttons || info.needs_expansion %}
|
||||||
|
<div class="button-holder">
|
||||||
|
{% if info.needs_prev_next_buttons %}
|
||||||
|
<button class="prev">≺</button> {# #}
|
||||||
|
<button class="next">≻</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if info.needs_expansion %}
|
||||||
|
<button class="expand">↕</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div> {# #}
|
||||||
|
</div> {# #}
|
|
@ -1,21 +1,15 @@
|
||||||
<div class="example-wrap"> {# #}
|
<div class="example-wrap">
|
||||||
{# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
|
{# 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. #}
|
Do not show "1 2 3 4 5 ..." in web search results. #}
|
||||||
<div data-nosnippet><pre class="src-line-numbers">
|
<div data-nosnippet><pre class="src-line-numbers">
|
||||||
{% for line in lines.clone() %}
|
{% for line in lines.clone() %}
|
||||||
{% if embedded %}
|
{# ~#}
|
||||||
<span>{{line|safe}}</span>
|
<a href="#{{line|safe}}" id="{{line|safe}}">{{line|safe}}</a>
|
||||||
{%~ else %}
|
|
||||||
<a href="#{{line|safe}}" id="{{line|safe}}">{{line|safe}}</a>
|
|
||||||
{%~ endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</pre></div> {# #}
|
</pre></div> {# #}
|
||||||
<pre class="rust"> {# #}
|
<pre class="rust"> {# #}
|
||||||
<code>
|
<code>
|
||||||
{% if needs_expansion %}
|
|
||||||
<button class="expand">↕</button>
|
|
||||||
{% endif %}
|
|
||||||
{{code_html|safe}}
|
{{code_html|safe}}
|
||||||
</code> {# #}
|
</code> {# #}
|
||||||
</pre> {# #}
|
</pre> {# #}
|
||||||
</div>
|
</div> {# #}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
/// This is a custom command wrapper that simplifies working with commands and makes it easier to
|
/// 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.
|
/// 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 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
|
/// 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
|
/// containing constructed but never executed commands is dangerous because it can give a false
|
||||||
/// sense of confidence.
|
/// 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`]: Self::run
|
||||||
/// [`run_fail`]: Self::run_fail
|
/// [`run_fail`]: Self::run_fail
|
||||||
/// [`run_unchecked`]: Self::run_unchecked
|
/// [`run_unchecked`]: Self::run_unchecked
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Command {
|
pub struct Command {
|
||||||
cmd: StdCommand,
|
cmd: StdCommand,
|
||||||
stdin: Option<Box<[u8]>>,
|
// Convience for providing a quick stdin buffer.
|
||||||
|
stdin_buf: Option<Box<[u8]>>,
|
||||||
|
|
||||||
|
// Configurations for child process's std{in,out,err} handles.
|
||||||
|
stdin: Option<Stdio>,
|
||||||
|
stdout: Option<Stdio>,
|
||||||
|
stderr: Option<Stdio>,
|
||||||
|
|
||||||
|
// Emulate linear type semantics.
|
||||||
drop_bomb: DropBomb,
|
drop_bomb: DropBomb,
|
||||||
|
already_executed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new<P: AsRef<OsStr>>(program: P) -> Self {
|
pub fn new<P: AsRef<OsStr>>(program: P) -> Self {
|
||||||
let program = program.as_ref();
|
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
|
/// Specify a stdin input buffer. This is a convenience helper,
|
||||||
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
||||||
self.stdin = Some(input.as_ref().to_vec().into_boxed_slice());
|
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<T: Into<Stdio>>(&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<T: Into<Stdio>>(&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<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
|
||||||
|
self.stderr = Some(cfg.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +152,8 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the constructed command and assert that it is successfully run.
|
/// Run the constructed command and assert that it is successfully run.
|
||||||
|
///
|
||||||
|
/// By default, std{in,out,err} are [`Stdio::piped()`].
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn run(&mut self) -> CompletedProcess {
|
pub fn run(&mut self) -> CompletedProcess {
|
||||||
let output = self.command_output();
|
let output = self.command_output();
|
||||||
|
@ -115,6 +164,8 @@ impl Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the constructed command and assert that it does not successfully run.
|
/// Run the constructed command and assert that it does not successfully run.
|
||||||
|
///
|
||||||
|
/// By default, std{in,out,err} are [`Stdio::piped()`].
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn run_fail(&mut self) -> CompletedProcess {
|
pub fn run_fail(&mut self) -> CompletedProcess {
|
||||||
let output = self.command_output();
|
let output = self.command_output();
|
||||||
|
@ -124,10 +175,10 @@ impl Command {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the command but do not check its exit status.
|
/// Run the command but do not check its exit status. Only use if you explicitly don't care
|
||||||
/// Only use if you explicitly don't care about the exit status.
|
/// about the exit status.
|
||||||
/// Prefer to use [`Self::run`] and [`Self::run_fail`]
|
///
|
||||||
/// whenever possible.
|
/// Prefer to use [`Self::run`] and [`Self::run_fail`] whenever possible.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn run_unchecked(&mut self) -> CompletedProcess {
|
pub fn run_unchecked(&mut self) -> CompletedProcess {
|
||||||
self.command_output()
|
self.command_output()
|
||||||
|
@ -135,13 +186,19 @@ impl Command {
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn command_output(&mut self) -> CompletedProcess {
|
fn command_output(&mut self) -> CompletedProcess {
|
||||||
|
if self.already_executed {
|
||||||
|
panic!("command was already executed");
|
||||||
|
} else {
|
||||||
|
self.already_executed = true;
|
||||||
|
}
|
||||||
|
|
||||||
self.drop_bomb.defuse();
|
self.drop_bomb.defuse();
|
||||||
// let's make sure we piped all the input and outputs
|
// let's make sure we piped all the input and outputs
|
||||||
self.cmd.stdin(Stdio::piped());
|
self.cmd.stdin(self.stdin.take().unwrap_or(Stdio::piped()));
|
||||||
self.cmd.stdout(Stdio::piped());
|
self.cmd.stdout(self.stdout.take().unwrap_or(Stdio::piped()));
|
||||||
self.cmd.stderr(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();
|
let mut child = self.cmd.spawn().unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -227,9 +227,10 @@ impl LlvmFilecheck {
|
||||||
Self { cmd }
|
Self { cmd }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call.
|
/// Provide a buffer representing standard input containing patterns that will be matched
|
||||||
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
/// against the `.patterns(path)` call.
|
||||||
self.cmd.stdin(input);
|
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
||||||
|
self.cmd.stdin_buf(input);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,9 +291,9 @@ impl Rustc {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a stdin input
|
/// Specify a stdin input buffer.
|
||||||
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
||||||
self.cmd.stdin(input);
|
self.cmd.stdin_buf(input);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,9 +85,9 @@ impl Rustdoc {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify a stdin input
|
/// Specify a stdin input buffer.
|
||||||
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
|
||||||
self.cmd.stdin(input);
|
self.cmd.stdin_buf(input);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ pub mod rfs {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-exports of third-party library crates.
|
// Re-exports of third-party library crates.
|
||||||
|
// tidy-alphabetical-start
|
||||||
pub use bstr;
|
pub use bstr;
|
||||||
pub use gimli;
|
pub use gimli;
|
||||||
pub use libc;
|
pub use libc;
|
||||||
|
@ -41,6 +42,7 @@ pub use object;
|
||||||
pub use regex;
|
pub use regex;
|
||||||
pub use serde_json;
|
pub use serde_json;
|
||||||
pub use wasmparser;
|
pub use wasmparser;
|
||||||
|
// tidy-alphabetical-end
|
||||||
|
|
||||||
// Re-exports of external dependencies.
|
// Re-exports of external dependencies.
|
||||||
pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc};
|
pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc};
|
||||||
|
|
|
@ -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 ()
|
yields ()
|
||||||
{
|
{
|
||||||
debug _task_context => _2;
|
debug _task_context => _2;
|
||||||
debug a => (_1.0: i32);
|
debug a => (_1.0: i32);
|
||||||
debug b => (_1.1: i32);
|
debug b => (*(_1.1: &i32));
|
||||||
let mut _0: ();
|
let mut _0: ();
|
||||||
let _3: i32;
|
let _3: i32;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
|
@ -28,7 +28,7 @@ yields ()
|
||||||
_4 = &_3;
|
_4 = &_3;
|
||||||
FakeRead(ForLet(None), _4);
|
FakeRead(ForLet(None), _4);
|
||||||
StorageLive(_5);
|
StorageLive(_5);
|
||||||
_5 = &(_1.1: i32);
|
_5 = &(*(_1.1: &i32));
|
||||||
FakeRead(ForLet(None), _5);
|
FakeRead(ForLet(None), _5);
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
StorageDead(_5);
|
StorageDead(_5);
|
|
@ -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 ()
|
yields ()
|
||||||
{
|
{
|
||||||
debug _task_context => _2;
|
debug _task_context => _2;
|
|
@ -1,10 +1,10 @@
|
||||||
// MIR for `main::{closure#0}::{closure#0}` 0 coroutine_closure_by_move
|
// 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} {
|
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:53:53: 56:10};
|
let mut _0: {async closure body@$DIR/async_closure_shims.rs:54:53: 57:10};
|
||||||
|
|
||||||
bb0: {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 ()
|
yields ()
|
||||||
{
|
{
|
||||||
debug _task_context => _2;
|
debug _task_context => _2;
|
|
@ -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 ()
|
yields ()
|
||||||
{
|
{
|
||||||
debug _task_context => _2;
|
debug _task_context => _2;
|
|
@ -1,10 +1,10 @@
|
||||||
// MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_move
|
// 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} {
|
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:62:48: 65:10};
|
let mut _0: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10};
|
||||||
|
|
||||||
bb0: {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
// MIR for `main::{closure#0}::{closure#1}` 0 coroutine_closure_by_ref
|
// 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} {
|
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:62:48: 65:10};
|
let mut _0: {async closure body@$DIR/async_closure_shims.rs:63:48: 66:10};
|
||||||
|
|
||||||
bb0: {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
//@ edition:2021
|
//@ edition:2021
|
||||||
// skip-filecheck
|
// skip-filecheck
|
||||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
|
||||||
|
|
||||||
#![feature(async_closure, noop_waker, async_fn_traits)]
|
#![feature(async_closure, noop_waker, async_fn_traits)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
@ -22,7 +21,7 @@ pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn call(f: &mut impl AsyncFn(i32)) {
|
async fn call(f: &impl AsyncFn(i32)) {
|
||||||
f(0).await;
|
f(0).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +42,12 @@ async fn call_normal_mut<F: Future<Output = ()>>(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}.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_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}.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() {
|
pub fn main() {
|
||||||
block_on(async {
|
block_on(async {
|
||||||
let b = 2i32;
|
let b = 2i32;
|
||||||
|
@ -54,7 +55,7 @@ pub fn main() {
|
||||||
let a = &a;
|
let a = &a;
|
||||||
let b = &b;
|
let b = &b;
|
||||||
};
|
};
|
||||||
call(&mut async_closure).await;
|
call(&async_closure).await;
|
||||||
call_mut(&mut async_closure).await;
|
call_mut(&mut async_closure).await;
|
||||||
call_once(async_closure).await;
|
call_once(async_closure).await;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
use run_make_support::rustc;
|
use run_make_support::rustc;
|
||||||
|
|
||||||
fn main() {
|
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");
|
output.assert_stderr_not_contains("panic");
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ fn main() {
|
||||||
|
|
||||||
rustc()
|
rustc()
|
||||||
.arg("-")
|
.arg("-")
|
||||||
.stdin("fn main() {}")
|
.stdin_buf("fn main() {}")
|
||||||
.emit("link,obj")
|
.emit("link,obj")
|
||||||
.arg("-Csave-temps")
|
.arg("-Csave-temps")
|
||||||
.target(target)
|
.target(target)
|
||||||
|
|
|
@ -8,6 +8,6 @@
|
||||||
use run_make_support::{run, rustc};
|
use run_make_support::{run, rustc};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
rustc().arg("-").stdin("fn main() {}").run();
|
rustc().arg("-").stdin_buf("fn main() {}").run();
|
||||||
run("rust_out");
|
run("rust_out");
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ fn run_tests(extra_args: &[&str], expected_file: &str) {
|
||||||
.run_fail();
|
.run_fail();
|
||||||
let test_stdout = &cmd_out.stdout_utf8();
|
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()
|
diff()
|
||||||
.expected_file(expected_file)
|
.expected_file(expected_file)
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn main() {
|
||||||
.codegen_units(16)
|
.codegen_units(16)
|
||||||
.opt_level("2")
|
.opt_level("2")
|
||||||
.target(&env_var("TARGET"))
|
.target(&env_var("TARGET"))
|
||||||
.stdin("fn main(){}")
|
.stdin_buf("fn main(){}")
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
// `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR
|
// `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR
|
||||||
|
|
|
@ -11,8 +11,8 @@ fn main() {
|
||||||
let p = cwd();
|
let p = cwd();
|
||||||
path_bc = p.join("nonexistant_dir_bc");
|
path_bc = p.join("nonexistant_dir_bc");
|
||||||
path_ir = p.join("nonexistant_dir_ir");
|
path_ir = p.join("nonexistant_dir_ir");
|
||||||
rustc().input("-").stdin("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run();
|
rustc().input("-").stdin_buf("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_ir).emit("llvm-ir").run();
|
||||||
assert!(path_bc.exists());
|
assert!(path_bc.exists());
|
||||||
assert!(path_ir.exists());
|
assert!(path_ir.exists());
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,5 +9,5 @@ use run_make_support::{llvm_filecheck, rfs, rustc};
|
||||||
fn main() {
|
fn main() {
|
||||||
rustc().input("no_builtins.rs").emit("link").run();
|
rustc().input("no_builtins.rs").emit("link").run();
|
||||||
rustc().input("main.rs").emit("llvm-ir").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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,5 +35,8 @@ fn main() {
|
||||||
.codegen_units(1)
|
.codegen_units(1)
|
||||||
.emit("llvm-ir")
|
.emit("llvm-ir")
|
||||||
.run();
|
.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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,8 @@ fn main() {
|
||||||
.codegen_units(1)
|
.codegen_units(1)
|
||||||
.emit("llvm-ir")
|
.emit("llvm-ir")
|
||||||
.run();
|
.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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,5 +51,5 @@ fn main() {
|
||||||
let lines: Vec<_> = ir.lines().rev().collect();
|
let lines: Vec<_> = ir.lines().rev().collect();
|
||||||
let mut reversed_ir = lines.join("\n");
|
let mut reversed_ir = lines.join("\n");
|
||||||
reversed_ir.push('\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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
use run_make_support::{run, rustc};
|
use run_make_support::{run, rustc};
|
||||||
|
|
||||||
fn main() {
|
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();
|
rustc().arg("-Zlink-only").input("rust_out.rlink").run();
|
||||||
run("rust_out");
|
run("rust_out");
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ fn ok_compiler_version(compiler: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
let compiler_output =
|
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 re = Regex::new(r"(?m)^(\d+)").unwrap();
|
||||||
let version: u32 =
|
let version: u32 =
|
||||||
re.captures(&compiler_output).unwrap().get(1).unwrap().as_str().parse().unwrap();
|
re.captures(&compiler_output).unwrap().get(1).unwrap().as_str().parse().unwrap();
|
||||||
|
|
|
@ -14,7 +14,7 @@ const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// echo $HELLO_WORLD | rustc -
|
// echo $HELLO_WORLD | rustc -
|
||||||
rustc().arg("-").stdin(HELLO_WORLD).run();
|
rustc().arg("-").stdin_buf(HELLO_WORLD).run();
|
||||||
assert!(
|
assert!(
|
||||||
PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" })
|
PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" })
|
||||||
.try_exists()
|
.try_exists()
|
||||||
|
@ -22,7 +22,7 @@ fn main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
// echo $NOT_UTF8 | rustc -
|
// 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",
|
"error: couldn't read from stdin, as it did not contain valid UTF-8",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,11 @@ fn main() {
|
||||||
let out_dir = PathBuf::from("doc");
|
let out_dir = PathBuf::from("doc");
|
||||||
|
|
||||||
// rustdoc -
|
// 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());
|
assert!(out_dir.join("rust_out/struct.F.html").try_exists().unwrap());
|
||||||
|
|
||||||
// rustdoc --test -
|
// rustdoc --test -
|
||||||
rustdoc().arg("--test").arg("-").stdin(INPUT).run();
|
rustdoc().arg("--test").arg("-").stdin_buf(INPUT).run();
|
||||||
|
|
||||||
// rustdoc file.rs -
|
// rustdoc file.rs -
|
||||||
rustdoc().arg("file.rs").arg("-").run_fail();
|
rustdoc().arg("file.rs").arg("-").run_fail();
|
||||||
|
|
|
@ -34,7 +34,7 @@ fn check_crate_is_unstable(cr: &Crate) {
|
||||||
.target(target())
|
.target(target())
|
||||||
.extern_(name, path)
|
.extern_(name, path)
|
||||||
.input("-")
|
.input("-")
|
||||||
.stdin(format!("extern crate {name};"))
|
.stdin_buf(format!("extern crate {name};"))
|
||||||
.run_fail();
|
.run_fail();
|
||||||
|
|
||||||
// Make sure it failed for the intended reason, not some other reason.
|
// Make sure it failed for the intended reason, not some other reason.
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
use run_make_support::{diff, rustc};
|
use run_make_support::{diff, rustc};
|
||||||
|
|
||||||
fn main() {
|
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()
|
diff()
|
||||||
.actual_text("actual-stdout", out.stdout_utf8())
|
.actual_text("actual-stdout", out.stdout_utf8())
|
||||||
.expected_file("unknown-mod.stdout")
|
.expected_file("unknown-mod.stdout")
|
||||||
|
|
|
@ -78,19 +78,23 @@ fn check(func_re: &str, mut checks: &str) {
|
||||||
// This is because frame pointers are optional, and them being enabled requires
|
// This is because frame pointers are optional, and them being enabled requires
|
||||||
// an additional `popq` in the pattern checking file.
|
// an additional `popq` in the pattern checking file.
|
||||||
if func_re == "std::io::stdio::_print::[[:alnum:]]+" {
|
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() {
|
if !output.status().success() {
|
||||||
checks = "print.without_frame_pointers.checks";
|
checks = "print.without_frame_pointers.checks";
|
||||||
llvm_filecheck().stdin(&dump).patterns(checks).run();
|
llvm_filecheck().stdin_buf(&dump).patterns(checks).run();
|
||||||
}
|
}
|
||||||
} else {
|
} 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"]
|
if !["rust_plus_one_global_asm", "cmake_plus_one_c_global_asm", "cmake_plus_one_cxx_global_asm"]
|
||||||
.contains(&func_re)
|
.contains(&func_re)
|
||||||
{
|
{
|
||||||
// The assembler cannot avoid explicit `ret` instructions. Sequences
|
// The assembler cannot avoid explicit `ret` instructions. Sequences
|
||||||
// of `shlq $0x0, (%rsp); lfence; retq` are used instead.
|
// 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,3 +94,24 @@ call-function: ("check-buttons",{
|
||||||
"filter": "invert(0.5)",
|
"filter": "invert(0.5)",
|
||||||
"filter_hover": "invert(0.35)",
|
"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"})
|
||||||
|
|
|
@ -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.
|
// We check that without this setting, there is no line number displayed.
|
||||||
assert-false: "pre.example-line-numbers"
|
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.
|
// We set the setting to show the line numbers on code examples.
|
||||||
set-local-storage: {"rustdoc-line-numbers": "true"}
|
set-local-storage: {"rustdoc-line-numbers": "true"}
|
||||||
reload:
|
reload:
|
||||||
|
@ -29,9 +41,21 @@ define-function: (
|
||||||
"margin": "0px",
|
"margin": "0px",
|
||||||
"padding": "14px 8px",
|
"padding": "14px 8px",
|
||||||
"text-align": "right",
|
"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,
|
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", {
|
call-function: ("check-colors", {
|
||||||
|
@ -64,7 +88,56 @@ wait-for: 100 // wait-for-false does not exist
|
||||||
assert-false: "pre.example-line-numbers"
|
assert-false: "pre.example-line-numbers"
|
||||||
assert-local-storage: {"rustdoc-line-numbers": "false" }
|
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.
|
// Finally, turn it on again.
|
||||||
click: "input#line-numbers"
|
click: "input#line-numbers"
|
||||||
wait-for: "pre.example-line-numbers"
|
wait-for: "pre.example-line-numbers"
|
||||||
assert-local-storage: {"rustdoc-line-numbers": "true" }
|
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,
|
||||||
|
)
|
||||||
|
|
|
@ -3,29 +3,53 @@
|
||||||
go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
|
go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
|
||||||
|
|
||||||
// The next/prev buttons vertically scroll the code viewport between examples
|
// 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"
|
focus: ".scraped-example-list > .scraped-example .next"
|
||||||
press-key: "Enter"
|
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|
|
"scrollTop": |initialScrollTop|
|
||||||
}, NEAR)
|
}, NEAR)
|
||||||
focus: ".scraped-example-list > .scraped-example .prev"
|
focus: ".scraped-example-list > .scraped-example .prev"
|
||||||
press-key: "Enter"
|
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|
|
"scrollTop": |initialScrollTop|
|
||||||
}, NEAR)
|
}, NEAR)
|
||||||
|
|
||||||
// The expand button increases the scrollHeight of the minimized code viewport
|
// The expand button increases the scrollHeight of the minimized code viewport
|
||||||
store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": smallOffsetHeight})
|
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|
|
"scrollHeight": |smallOffsetHeight|
|
||||||
}, NEAR)
|
}, NEAR)
|
||||||
focus: ".scraped-example-list > .scraped-example .expand"
|
focus: ".scraped-example-list > .scraped-example .expand"
|
||||||
press-key: "Enter"
|
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|
|
"offsetHeight": |smallOffsetHeight|
|
||||||
}, NEAR)
|
}, NEAR)
|
||||||
store-property: (".scraped-example-list > .scraped-example pre", {"offsetHeight": fullOffsetHeight})
|
assert-property-false: (".scraped-example-list > .scraped-example .rust", {
|
||||||
assert-property: (".scraped-example-list > .scraped-example pre", {
|
"offsetHeight": |smallOffsetHeight|
|
||||||
|
}, NEAR)
|
||||||
|
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|
|
"scrollHeight": |fullOffsetHeight|
|
||||||
}, NEAR)
|
}, NEAR)
|
||||||
|
|
|
@ -10,10 +10,10 @@ define-function: (
|
||||||
block {
|
block {
|
||||||
call-function: ("switch-theme", {"theme": |theme|})
|
call-function: ("switch-theme", {"theme": |theme|})
|
||||||
wait-for: ".more-examples-toggle"
|
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|,
|
"background-color": |highlight|,
|
||||||
}, ALL)
|
}, ALL)
|
||||||
assert-css: (".scraped-example .example-wrap .rust span.highlight.focus", {
|
assert-css: (".scraped-example .rust span.highlight.focus", {
|
||||||
"background-color": |highlight_focus|,
|
"background-color": |highlight_focus|,
|
||||||
}, ALL)
|
}, ALL)
|
||||||
|
|
||||||
|
@ -67,11 +67,11 @@ define-function: (
|
||||||
[theme, background_color_start, background_color_end],
|
[theme, background_color_start, background_color_end],
|
||||||
block {
|
block {
|
||||||
call-function: ("switch-theme", {"theme": |theme|})
|
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-image": "linear-gradient(" + |background_color_start| + ", " +
|
||||||
|background_color_end| + ")",
|
|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-image": "linear-gradient(to top, " + |background_color_start| + ", " +
|
||||||
|background_color_end| + ")",
|
|background_color_end| + ")",
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,48 +1,115 @@
|
||||||
// Check that the line number column has the correct layout.
|
// Check that the line number column has the correct layout.
|
||||||
go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
|
go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
|
||||||
|
|
||||||
|
set-window-size: (1000, 1000)
|
||||||
|
|
||||||
// Check that it's not zero.
|
// Check that it's not zero.
|
||||||
assert-property-false: (
|
assert-property-false: (
|
||||||
".more-scraped-examples .scraped-example .code-wrapper .src-line-numbers",
|
".more-scraped-examples .scraped-example .src-line-numbers",
|
||||||
{"clientWidth": "0"}
|
{"clientWidth": "0"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Check that examples with very long lines have the same width as ones that don't.
|
// Check that examples with very long lines have the same width as ones that don't.
|
||||||
store-property: (
|
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},
|
{"clientWidth": clientWidth},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert-property: (
|
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|}
|
{"clientWidth": |clientWidth|}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert-property: (
|
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|}
|
{"clientWidth": |clientWidth|}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert-property: (
|
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|}
|
{"clientWidth": |clientWidth|}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert-property: (
|
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|}
|
{"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
|
// Check that for both mobile and desktop sizes, the buttons in scraped examples are displayed
|
||||||
// correctly.
|
// correctly.
|
||||||
|
|
||||||
store-value: (offset_y, 4)
|
store-value: (offset_y, 4)
|
||||||
|
|
||||||
// First with desktop
|
// First with desktop
|
||||||
assert-position: (".scraped-example .code-wrapper", {"y": 226})
|
assert-position: (".scraped-example", {"y": 226})
|
||||||
assert-position: (".scraped-example .code-wrapper .prev", {"y": 226 + |offset_y|})
|
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
|
// Then with mobile
|
||||||
set-window-size: (600, 600)
|
set-window-size: (600, 600)
|
||||||
assert-position: (".scraped-example .code-wrapper", {"y": 308})
|
store-size: (".scraped-example .scraped-example-title", {"height": title_height})
|
||||||
assert-position: (".scraped-example .code-wrapper .prev", {"y": 308 + |offset_y|})
|
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", {})
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
--copy-path-button-color: #999;
|
--copy-path-button-color: #999;
|
||||||
--copy-path-img-filter: invert(50%);
|
--copy-path-img-filter: invert(50%);
|
||||||
--copy-path-img-hover-filter: invert(35%);
|
--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-hover-color: rgb(255, 0, 0);
|
||||||
--codeblock-error-color: rgba(255, 0, 0, .5);
|
--codeblock-error-color: rgba(255, 0, 0, .5);
|
||||||
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
--codeblock-ignore-hover-color: rgb(255, 142, 0);
|
||||||
|
|
|
@ -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() {}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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() {}
|
Loading…
Reference in New Issue