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