Auto merge of #96785 - GuillaumeGomez:rollup-rgiwa57, r=GuillaumeGomez
Rollup of 10 pull requests Successful merges: - #96557 (Allow inline consts to reference generic params) - #96590 (rustdoc: when running a function-signature search, tweak the tab bar) - #96650 (Collect function instance used in `global_asm!` sym operand) - #96733 (turn `append_place_to_string` from recursion into iteration) - #96748 (Fixes reexports in search) - #96752 (Put the incompatible_closure_captures lint messages in alphabetical order) - #96754 (rustdoc: ensure HTML/JS side implementors don't have dups) - #96772 (Suggest fully qualified path with appropriate params) - #96776 (Fix two minor issues in hir.rs) - #96782 (a small `mirror_expr` cleanup) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
77652b9ef3
@ -1,5 +1,6 @@
|
||||
//! Borrow checker diagnostics.
|
||||
|
||||
use itertools::Itertools;
|
||||
use rustc_const_eval::util::{call_kind, CallDesugaringKind};
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir as hir;
|
||||
@ -161,158 +162,103 @@ pub(super) fn describe_any_place(&self, place_ref: PlaceRef<'tcx>) -> String {
|
||||
}
|
||||
|
||||
/// End-user visible description of `place` if one can be found.
|
||||
/// If the place is a temporary for instance, None will be returned.
|
||||
/// If the place is a temporary for instance, `None` will be returned.
|
||||
pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
|
||||
self.describe_place_with_options(place_ref, IncludingDowncast(false))
|
||||
}
|
||||
|
||||
/// End-user visible description of `place` if one can be found. If the
|
||||
/// place is a temporary for instance, None will be returned.
|
||||
/// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is
|
||||
/// End-user visible description of `place` if one can be found. If the place is a temporary
|
||||
/// for instance, `None` will be returned.
|
||||
/// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is
|
||||
/// `Downcast` and `IncludingDowncast` is true
|
||||
pub(super) fn describe_place_with_options(
|
||||
&self,
|
||||
place: PlaceRef<'tcx>,
|
||||
including_downcast: IncludingDowncast,
|
||||
) -> Option<String> {
|
||||
let local = place.local;
|
||||
let mut autoderef_index = None;
|
||||
let mut buf = String::new();
|
||||
match self.append_place_to_string(place, &mut buf, false, &including_downcast) {
|
||||
Ok(()) => Some(buf),
|
||||
Err(()) => None,
|
||||
}
|
||||
}
|
||||
let mut ok = self.append_local_to_string(local, &mut buf);
|
||||
|
||||
/// Appends end-user visible description of `place` to `buf`.
|
||||
fn append_place_to_string(
|
||||
&self,
|
||||
place: PlaceRef<'tcx>,
|
||||
buf: &mut String,
|
||||
mut autoderef: bool,
|
||||
including_downcast: &IncludingDowncast,
|
||||
) -> Result<(), ()> {
|
||||
match place {
|
||||
PlaceRef { local, projection: [] } => {
|
||||
self.append_local_to_string(local, buf)?;
|
||||
}
|
||||
PlaceRef { local, projection: [ProjectionElem::Deref] }
|
||||
if self.body.local_decls[local].is_ref_for_guard() =>
|
||||
{
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: &[] },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
}
|
||||
PlaceRef { local, projection: [ProjectionElem::Deref] }
|
||||
if self.body.local_decls[local].is_ref_to_static() =>
|
||||
{
|
||||
let local_info = &self.body.local_decls[local].local_info;
|
||||
if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
|
||||
buf.push_str(self.infcx.tcx.item_name(def_id).as_str());
|
||||
} else {
|
||||
unreachable!();
|
||||
for (index, elem) in place.projection.into_iter().enumerate() {
|
||||
match elem {
|
||||
ProjectionElem::Deref => {
|
||||
if index == 0 {
|
||||
if self.body.local_decls[local].is_ref_for_guard() {
|
||||
continue;
|
||||
}
|
||||
if let Some(box LocalInfo::StaticRef { def_id, .. }) =
|
||||
&self.body.local_decls[local].local_info
|
||||
{
|
||||
buf.push_str(self.infcx.tcx.item_name(*def_id).as_str());
|
||||
ok = Ok(());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
||||
local,
|
||||
projection: place.projection.split_at(index + 1).0,
|
||||
}) {
|
||||
let var_index = field.index();
|
||||
buf = self.upvars[var_index].place.to_string(self.infcx.tcx);
|
||||
ok = Ok(());
|
||||
if !self.upvars[var_index].by_ref {
|
||||
buf.insert(0, '*');
|
||||
}
|
||||
} else {
|
||||
if autoderef_index.is_none() {
|
||||
autoderef_index =
|
||||
match place.projection.into_iter().rev().find_position(|elem| {
|
||||
!matches!(
|
||||
elem,
|
||||
ProjectionElem::Deref | ProjectionElem::Downcast(..)
|
||||
)
|
||||
}) {
|
||||
Some((index, _)) => Some(place.projection.len() - index),
|
||||
None => Some(0),
|
||||
};
|
||||
}
|
||||
if index >= autoderef_index.unwrap() {
|
||||
buf.insert(0, '*');
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Downcast(..) if including_downcast.0 => return None,
|
||||
ProjectionElem::Downcast(..) => (),
|
||||
ProjectionElem::Field(field, _ty) => {
|
||||
// FIXME(project-rfc_2229#36): print capture precisely here.
|
||||
if let Some(field) = self.is_upvar_field_projection(PlaceRef {
|
||||
local,
|
||||
projection: place.projection.split_at(index + 1).0,
|
||||
}) {
|
||||
buf = self.upvars[field.index()].place.to_string(self.infcx.tcx);
|
||||
ok = Ok(());
|
||||
} else {
|
||||
let field_name = self.describe_field(
|
||||
PlaceRef { local, projection: place.projection.split_at(index).0 },
|
||||
*field,
|
||||
);
|
||||
buf.push('.');
|
||||
buf.push_str(&field_name);
|
||||
}
|
||||
}
|
||||
ProjectionElem::Index(index) => {
|
||||
buf.push('[');
|
||||
if self.append_local_to_string(*index, &mut buf).is_err() {
|
||||
buf.push('_');
|
||||
}
|
||||
buf.push(']');
|
||||
}
|
||||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
|
||||
// Since it isn't possible to borrow an element on a particular index and
|
||||
// then use another while the borrow is held, don't output indices details
|
||||
// to avoid confusing the end-user
|
||||
buf.push_str("[..]");
|
||||
}
|
||||
}
|
||||
PlaceRef { local, projection: [proj_base @ .., elem] } => {
|
||||
match elem {
|
||||
ProjectionElem::Deref => {
|
||||
let upvar_field_projection = self.is_upvar_field_projection(place);
|
||||
if let Some(field) = upvar_field_projection {
|
||||
let var_index = field.index();
|
||||
let name = self.upvars[var_index].place.to_string(self.infcx.tcx);
|
||||
if self.upvars[var_index].by_ref {
|
||||
buf.push_str(&name);
|
||||
} else {
|
||||
buf.push('*');
|
||||
buf.push_str(&name);
|
||||
}
|
||||
} else {
|
||||
if autoderef {
|
||||
// FIXME turn this recursion into iteration
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
} else {
|
||||
buf.push('*');
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
ProjectionElem::Downcast(..) => {
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
if including_downcast.0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
ProjectionElem::Field(field, _ty) => {
|
||||
autoderef = true;
|
||||
|
||||
// FIXME(project-rfc_2229#36): print capture precisely here.
|
||||
let upvar_field_projection = self.is_upvar_field_projection(place);
|
||||
if let Some(field) = upvar_field_projection {
|
||||
let var_index = field.index();
|
||||
let name = self.upvars[var_index].place.to_string(self.infcx.tcx);
|
||||
buf.push_str(&name);
|
||||
} else {
|
||||
let field_name = self
|
||||
.describe_field(PlaceRef { local, projection: proj_base }, *field);
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
buf.push('.');
|
||||
buf.push_str(&field_name);
|
||||
}
|
||||
}
|
||||
ProjectionElem::Index(index) => {
|
||||
autoderef = true;
|
||||
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
buf.push('[');
|
||||
if self.append_local_to_string(*index, buf).is_err() {
|
||||
buf.push('_');
|
||||
}
|
||||
buf.push(']');
|
||||
}
|
||||
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
|
||||
autoderef = true;
|
||||
// Since it isn't possible to borrow an element on a particular index and
|
||||
// then use another while the borrow is held, don't output indices details
|
||||
// to avoid confusing the end-user
|
||||
self.append_place_to_string(
|
||||
PlaceRef { local, projection: proj_base },
|
||||
buf,
|
||||
autoderef,
|
||||
&including_downcast,
|
||||
)?;
|
||||
buf.push_str("[..]");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
ok.ok().map(|_| buf)
|
||||
}
|
||||
|
||||
/// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
|
||||
|
@ -1441,7 +1441,7 @@ pub enum AsyncGeneratorKind {
|
||||
/// An explicit `async` block written by the user.
|
||||
Block,
|
||||
|
||||
/// An explicit `async` block written by the user.
|
||||
/// An explicit `async` closure written by the user.
|
||||
Closure,
|
||||
|
||||
/// The `async` block generated as the body of an async function.
|
||||
@ -2078,10 +2078,7 @@ pub enum YieldSource {
|
||||
|
||||
impl YieldSource {
|
||||
pub fn is_await(&self) -> bool {
|
||||
match self {
|
||||
YieldSource::Await { .. } => true,
|
||||
YieldSource::Yield => false,
|
||||
}
|
||||
matches!(self, YieldSource::Await { .. })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -731,6 +731,7 @@ pub fn emit_inference_failure_err(
|
||||
// | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)`
|
||||
// |
|
||||
// = note: cannot satisfy `Impl: Into<_>`
|
||||
debug!(?segment);
|
||||
if !impl_candidates.is_empty() && e.span.contains(span)
|
||||
&& let Some(expr) = exprs.first()
|
||||
&& let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
|
||||
@ -739,9 +740,29 @@ pub fn emit_inference_failure_err(
|
||||
let mut eraser = TypeParamEraser(self.tcx);
|
||||
let candidate_len = impl_candidates.len();
|
||||
let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| {
|
||||
let trait_item = self.tcx
|
||||
.associated_items(candidate.def_id)
|
||||
.find_by_name_and_kind(
|
||||
self.tcx,
|
||||
segment.ident,
|
||||
ty::AssocKind::Fn,
|
||||
candidate.def_id
|
||||
);
|
||||
let prefix = if let Some(trait_item) = trait_item
|
||||
&& let Some(trait_m) = trait_item.def_id.as_local()
|
||||
&& let hir::TraitItemKind::Fn(fn_, _) = &self.tcx.hir().trait_item(hir::TraitItemId { def_id: trait_m }).kind
|
||||
{
|
||||
match fn_.decl.implicit_self {
|
||||
hir::ImplicitSelfKind::ImmRef => "&",
|
||||
hir::ImplicitSelfKind::MutRef => "&mut ",
|
||||
_ => "",
|
||||
}
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let candidate = candidate.super_fold_with(&mut eraser);
|
||||
vec![
|
||||
(expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)),
|
||||
(expr.span.shrink_to_lo(), format!("{}::{}({}", candidate, segment.ident, prefix)),
|
||||
if exprs.len() == 1 {
|
||||
(expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string())
|
||||
} else {
|
||||
|
@ -136,6 +136,10 @@ fn default_print_def_path(
|
||||
match key.disambiguated_data.data {
|
||||
// Closures' own generics are only captures, don't print them.
|
||||
DefPathData::ClosureExpr => {}
|
||||
// This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
|
||||
// Anon consts doesn't have their own generics, and inline consts' own
|
||||
// generics are their inferred types, so don't print them.
|
||||
DefPathData::AnonConst => {}
|
||||
|
||||
// If we have any generic arguments to print, we do that
|
||||
// on top of the same path, but without its own generics.
|
||||
|
@ -158,6 +158,7 @@ fn apply_adjustment(
|
||||
}
|
||||
|
||||
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
let expr_ty = self.typeck_results().expr_ty(expr);
|
||||
let expr_span = expr.span;
|
||||
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
|
||||
@ -196,7 +197,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
|
||||
let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
|
||||
let tupled_args = Expr {
|
||||
ty: self.tcx.mk_tup(arg_tys),
|
||||
ty: tcx.mk_tup(arg_tys),
|
||||
temp_lifetime,
|
||||
span: expr.span,
|
||||
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
|
||||
@ -488,24 +489,24 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)),
|
||||
},
|
||||
hir::InlineAsmOperand::Const { ref anon_const } => {
|
||||
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
|
||||
let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
|
||||
let value = mir::ConstantKind::from_anon_const(
|
||||
self.tcx,
|
||||
tcx,
|
||||
anon_const_def_id,
|
||||
self.param_env,
|
||||
);
|
||||
let span = self.tcx.hir().span(anon_const.hir_id);
|
||||
let span = tcx.hir().span(anon_const.hir_id);
|
||||
|
||||
InlineAsmOperand::Const { value, span }
|
||||
}
|
||||
hir::InlineAsmOperand::SymFn { ref anon_const } => {
|
||||
let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id);
|
||||
let anon_const_def_id = tcx.hir().local_def_id(anon_const.hir_id);
|
||||
let value = mir::ConstantKind::from_anon_const(
|
||||
self.tcx,
|
||||
tcx,
|
||||
anon_const_def_id,
|
||||
self.param_env,
|
||||
);
|
||||
let span = self.tcx.hir().span(anon_const.hir_id);
|
||||
let span = tcx.hir().span(anon_const.hir_id);
|
||||
|
||||
InlineAsmOperand::SymFn { value, span }
|
||||
}
|
||||
@ -519,21 +520,16 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
},
|
||||
|
||||
hir::ExprKind::ConstBlock(ref anon_const) => {
|
||||
let tcx = self.tcx;
|
||||
let local_def_id = tcx.hir().local_def_id(anon_const.hir_id);
|
||||
let anon_const_def_id = local_def_id.to_def_id();
|
||||
|
||||
// Need to include the parent substs
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
|
||||
let ty = tcx.typeck(local_def_id).node_type(hir_id);
|
||||
let typeck_root_def_id = tcx.typeck_root_def_id(anon_const_def_id);
|
||||
let ty = self.typeck_results().node_type(anon_const.hir_id);
|
||||
let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id();
|
||||
let typeck_root_def_id = tcx.typeck_root_def_id(did);
|
||||
let parent_substs =
|
||||
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
|
||||
let substs =
|
||||
InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
|
||||
.substs;
|
||||
|
||||
ExprKind::ConstBlock { did: anon_const_def_id, substs }
|
||||
ExprKind::ConstBlock { did, substs }
|
||||
}
|
||||
// Now comes the rote stuff:
|
||||
hir::ExprKind::Repeat(ref v, _) => {
|
||||
@ -591,7 +587,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
}
|
||||
hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
|
||||
lhs: self.mirror_expr(source),
|
||||
name: Field::new(self.tcx.field_index(expr.hir_id, self.typeck_results)),
|
||||
name: Field::new(tcx.field_index(expr.hir_id, self.typeck_results)),
|
||||
},
|
||||
hir::ExprKind::Cast(ref source, ref cast_ty) => {
|
||||
// Check for a user-given type annotation on this `cast`
|
||||
@ -640,7 +636,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
let (d, o) = adt_def.discriminant_def_for_variant(idx);
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
let ty = adt_def.repr().discr_type();
|
||||
let ty = ty.to_ty(self.tcx());
|
||||
let ty = ty.to_ty(tcx);
|
||||
Some((d, o, ty))
|
||||
}
|
||||
_ => None,
|
||||
@ -652,8 +648,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
|
||||
let source = if let Some((did, offset, var_ty)) = var {
|
||||
let param_env_ty = self.param_env.and(var_ty);
|
||||
let size = self
|
||||
.tcx
|
||||
let size = tcx
|
||||
.layout_of(param_env_ty)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
|
||||
@ -671,7 +666,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
|
||||
Some(did) => {
|
||||
// in case we are offsetting from a computed discriminant
|
||||
// and not the beginning of discriminants (which is always `0`)
|
||||
let substs = InternalSubsts::identity_for_item(self.tcx(), did);
|
||||
let substs = InternalSubsts::identity_for_item(tcx, did);
|
||||
let kind =
|
||||
ExprKind::NamedConst { def_id: did, substs, user_ty: None };
|
||||
let lhs = self.thir.exprs.push(Expr {
|
||||
|
@ -445,12 +445,9 @@ fn collect_items_rec<'tcx>(
|
||||
// depend on any other items.
|
||||
}
|
||||
hir::InlineAsmOperand::SymFn { anon_const } => {
|
||||
let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id();
|
||||
if let Ok(val) = tcx.const_eval_poly(def_id) {
|
||||
rustc_data_structures::stack::ensure_sufficient_stack(|| {
|
||||
collect_const_value(tcx, val, &mut neighbors);
|
||||
});
|
||||
}
|
||||
let fn_ty =
|
||||
tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
|
||||
visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors);
|
||||
}
|
||||
hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
|
||||
let instance = Instance::mono(tcx, *def_id);
|
||||
|
@ -3105,6 +3105,13 @@ fn resolve_anon_const(&mut self, constant: &'ast AnonConst, is_repeat: IsRepeatE
|
||||
);
|
||||
}
|
||||
|
||||
fn resolve_inline_const(&mut self, constant: &'ast AnonConst) {
|
||||
debug!("resolve_anon_const {constant:?}");
|
||||
self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| {
|
||||
visit::walk_anon_const(this, constant);
|
||||
});
|
||||
}
|
||||
|
||||
fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
|
||||
// First, record candidate traits for this expression if it could
|
||||
// result in the invocation of a method call.
|
||||
@ -3261,7 +3268,7 @@ fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
|
||||
});
|
||||
}
|
||||
ExprKind::ConstBlock(ref ct) => {
|
||||
self.resolve_anon_const(ct, IsRepeatExpr::No);
|
||||
self.resolve_inline_const(ct);
|
||||
}
|
||||
ExprKind::Index(ref elem, ref idx) => {
|
||||
self.resolve_expr(elem, Some(expr));
|
||||
|
@ -914,6 +914,10 @@ fn compute_2229_migrations_reasons(
|
||||
reasons.auto_traits.extend(auto_trait_reasons);
|
||||
reasons.drop_order = drop_order;
|
||||
|
||||
// `auto_trait_reasons` are in hashset order, so sort them to put the
|
||||
// diagnostics we emit later in a cross-platform-consistent order.
|
||||
reasons.auto_traits.sort_unstable();
|
||||
|
||||
reasons
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,16 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
||||
}
|
||||
|
||||
// Index this method for searching later on.
|
||||
if let Some(ref s) = item.name {
|
||||
if let Some(ref s) = item.name.or_else(|| {
|
||||
if item.is_stripped() {
|
||||
None
|
||||
} else if let clean::ImportItem(ref i) = *item.kind &&
|
||||
let clean::ImportKind::Simple(s) = i.kind {
|
||||
Some(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
let (parent, is_inherent_impl_item) = match *item.kind {
|
||||
clean::StrippedItem(..) => ((None, None), false),
|
||||
clean::AssocConstItem(..) | clean::AssocTypeItem(..)
|
||||
|
@ -2542,7 +2542,16 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
|
||||
|
||||
let item_sections_in_use: FxHashSet<_> = items
|
||||
.iter()
|
||||
.filter(|it| !it.is_stripped() && it.name.is_some())
|
||||
.filter(|it| {
|
||||
!it.is_stripped()
|
||||
&& it
|
||||
.name
|
||||
.or_else(|| {
|
||||
if let clean::ImportItem(ref i) = *it.kind &&
|
||||
let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
|
||||
})
|
||||
.is_some()
|
||||
})
|
||||
.map(|it| item_ty_to_section(it.type_()))
|
||||
.collect();
|
||||
for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -346,7 +346,7 @@ fn cmp(
|
||||
w.write_str(ITEM_TABLE_ROW_OPEN);
|
||||
write!(
|
||||
w,
|
||||
"<div class=\"item-left {stab}{add}import-item\">\
|
||||
"<div class=\"item-left {stab}{add}import-item\"{id}>\
|
||||
<code>{vis}{imp}</code>\
|
||||
</div>\
|
||||
<div class=\"item-right docblock-short\">{stab_tags}</div>",
|
||||
@ -355,6 +355,11 @@ fn cmp(
|
||||
vis = myitem.visibility.print_with_space(myitem.item_id, cx),
|
||||
imp = import.print(cx),
|
||||
stab_tags = stab_tags.unwrap_or_default(),
|
||||
id = match import.kind {
|
||||
clean::ImportKind::Simple(s) =>
|
||||
format!(" id=\"{}\"", cx.derive_id(format!("reexport.{}", s))),
|
||||
clean::ImportKind::Glob => String::new(),
|
||||
},
|
||||
);
|
||||
w.write_str(ITEM_TABLE_ROW_CLOSE);
|
||||
}
|
||||
@ -790,16 +795,18 @@ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item
|
||||
render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All);
|
||||
|
||||
let cache = cx.cache();
|
||||
let mut extern_crates = FxHashSet::default();
|
||||
if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) {
|
||||
// The DefId is for the first Type found with that name. The bool is
|
||||
// if any Types with the same name but different DefId have been found.
|
||||
let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
|
||||
for implementor in implementors {
|
||||
match implementor.inner_impl().for_ {
|
||||
clean::Type::Path { ref path }
|
||||
| clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. }
|
||||
if !path.is_assoc_ty() =>
|
||||
{
|
||||
if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cx.cache()) &&
|
||||
!did.is_local() {
|
||||
extern_crates.insert(did.krate);
|
||||
}
|
||||
match implementor.inner_impl().for_.without_borrowed_ref() {
|
||||
clean::Type::Path { ref path } if !path.is_assoc_ty() => {
|
||||
let did = path.def_id();
|
||||
let &mut (prev_did, ref mut has_duplicates) =
|
||||
implementor_dups.entry(path.last()).or_insert((did, false));
|
||||
@ -898,20 +905,96 @@ fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item
|
||||
}
|
||||
}
|
||||
|
||||
// Include implementors in crates that depend on the current crate.
|
||||
//
|
||||
// This is complicated by the way rustdoc is invoked, which is basically
|
||||
// the same way rustc is invoked: it gets called, one at a time, for each
|
||||
// crate. When building the rustdocs for the current crate, rustdoc can
|
||||
// see crate metadata for its dependencies, but cannot see metadata for its
|
||||
// dependents.
|
||||
//
|
||||
// To make this work, we generate a "hook" at this stage, and our
|
||||
// dependents can "plug in" to it when they build. For simplicity's sake,
|
||||
// it's [JSONP]: a JavaScript file with the data we need (and can parse),
|
||||
// surrounded by a tiny wrapper that the Rust side ignores, but allows the
|
||||
// JavaScript side to include without having to worry about Same Origin
|
||||
// Policy. The code for *that* is in `write_shared.rs`.
|
||||
//
|
||||
// This is further complicated by `#[doc(inline)]`. We want all copies
|
||||
// of an inlined trait to reference the same JS file, to address complex
|
||||
// dependency graphs like this one (lower crates depend on higher crates):
|
||||
//
|
||||
// ```text
|
||||
// --------------------------------------------
|
||||
// | crate A: trait Foo |
|
||||
// --------------------------------------------
|
||||
// | |
|
||||
// -------------------------------- |
|
||||
// | crate B: impl A::Foo for Bar | |
|
||||
// -------------------------------- |
|
||||
// | |
|
||||
// ---------------------------------------------
|
||||
// | crate C: #[doc(inline)] use A::Foo as Baz |
|
||||
// | impl Baz for Quux |
|
||||
// ---------------------------------------------
|
||||
// ```
|
||||
//
|
||||
// Basically, we want `C::Baz` and `A::Foo` to show the same set of
|
||||
// impls, which is easier if they both treat `/implementors/A/trait.Foo.js`
|
||||
// as the Single Source of Truth.
|
||||
//
|
||||
// We also want the `impl Baz for Quux` to be written to
|
||||
// `trait.Foo.js`. However, when we generate plain HTML for `C::Baz`,
|
||||
// we're going to want to generate plain HTML for `impl Baz for Quux` too,
|
||||
// because that'll load faster, and it's better for SEO. And we don't want
|
||||
// the same impl to show up twice on the same page.
|
||||
//
|
||||
// To make this work, the implementors JS file has a structure kinda
|
||||
// like this:
|
||||
//
|
||||
// ```js
|
||||
// JSONP({
|
||||
// "B": {"impl A::Foo for Bar"},
|
||||
// "C": {"impl Baz for Quux"},
|
||||
// });
|
||||
// ```
|
||||
//
|
||||
// First of all, this means we can rebuild a crate, and it'll replace its own
|
||||
// data if something changes. That is, `rustdoc` is idempotent. The other
|
||||
// advantage is that we can list the crates that get included in the HTML,
|
||||
// and ignore them when doing the JavaScript-based part of rendering.
|
||||
// So C's HTML will have something like this:
|
||||
//
|
||||
// ```html
|
||||
// <script type="text/javascript" src="/implementors/A/trait.Foo.js"
|
||||
// data-ignore-extern-crates="A,B" async></script>
|
||||
// ```
|
||||
//
|
||||
// And, when the JS runs, anything in data-ignore-extern-crates is known
|
||||
// to already be in the HTML, and will be ignored.
|
||||
//
|
||||
// [JSONP]: https://en.wikipedia.org/wiki/JSONP
|
||||
let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
|
||||
.take(cx.current.len())
|
||||
.chain(std::iter::once("implementors"))
|
||||
.collect();
|
||||
if it.item_id.is_local() {
|
||||
js_src_path.extend(cx.current.iter().copied());
|
||||
if let Some(did) = it.item_id.as_def_id() &&
|
||||
let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } &&
|
||||
let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) {
|
||||
js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
|
||||
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
|
||||
} else {
|
||||
let (ref path, _) = cache.external_paths[&it.item_id.expect_def_id()];
|
||||
js_src_path.extend(path[..path.len() - 1].iter().copied());
|
||||
js_src_path.extend(cx.current.iter().copied());
|
||||
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
|
||||
}
|
||||
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), it.name.unwrap()));
|
||||
let extern_crates = extern_crates
|
||||
.into_iter()
|
||||
.map(|cnum| cx.shared.tcx.crate_name(cnum).to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
write!(
|
||||
w,
|
||||
"<script type=\"text/javascript\" src=\"{src}\" async></script>",
|
||||
"<script type=\"text/javascript\" src=\"{src}\" data-ignore-extern-crates=\"{extern_crates}\" async></script>",
|
||||
src = js_src_path.finish(),
|
||||
);
|
||||
}
|
||||
|
@ -501,10 +501,13 @@ fn to_json_string(&self) -> String {
|
||||
//
|
||||
// FIXME: this is a vague explanation for why this can't be a `get`, in
|
||||
// theory it should be...
|
||||
let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) {
|
||||
Some(p) => p,
|
||||
let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
|
||||
Some(p) => match cache.paths.get(&did).or_else(|| cache.external_paths.get(&did)) {
|
||||
Some((_, t)) => (p, t),
|
||||
None => continue,
|
||||
},
|
||||
None => match cache.external_paths.get(&did) {
|
||||
Some(p) => p,
|
||||
Some((p, t)) => (p, t),
|
||||
None => continue,
|
||||
},
|
||||
};
|
||||
|
@ -1333,6 +1333,11 @@ pre.rust {
|
||||
border-top: 2px solid;
|
||||
}
|
||||
|
||||
#titles > button:first-child:last-child {
|
||||
margin-right: 1px;
|
||||
width: calc(100% - 1px);
|
||||
}
|
||||
|
||||
#titles > button:not(:last-child) {
|
||||
margin-right: 1px;
|
||||
width: calc(33.3% - 1px);
|
||||
|
@ -759,8 +759,14 @@ function loadCss(cssFileName) {
|
||||
const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
|
||||
const baseIdName = "impl-" + traitName + "-";
|
||||
const libs = Object.getOwnPropertyNames(imp);
|
||||
// We don't want to include impls from this JS file, when the HTML already has them.
|
||||
// The current crate should always be ignored. Other crates that should also be
|
||||
// ignored are included in the attribute `data-ignore-extern-crates`.
|
||||
const ignoreExternCrates = document
|
||||
.querySelector("script[data-ignore-extern-crates]")
|
||||
.getAttribute("data-ignore-extern-crates");
|
||||
for (const lib of libs) {
|
||||
if (lib === window.currentCrate) {
|
||||
if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) {
|
||||
continue;
|
||||
}
|
||||
const structs = imp[lib];
|
||||
|
@ -45,26 +45,33 @@ const TY_KEYWORD = itemTypes.indexOf("keyword");
|
||||
|
||||
// In the search display, allows to switch between tabs.
|
||||
function printTab(nb) {
|
||||
if (nb === 0 || nb === 1 || nb === 2) {
|
||||
searchState.currentTab = nb;
|
||||
}
|
||||
let nb_copy = nb;
|
||||
let iter = 0;
|
||||
let foundCurrentTab = false;
|
||||
let foundCurrentResultSet = false;
|
||||
onEachLazy(document.getElementById("titles").childNodes, elem => {
|
||||
if (nb_copy === 0) {
|
||||
if (nb === iter) {
|
||||
addClass(elem, "selected");
|
||||
foundCurrentTab = true;
|
||||
} else {
|
||||
removeClass(elem, "selected");
|
||||
}
|
||||
nb_copy -= 1;
|
||||
iter += 1;
|
||||
});
|
||||
iter = 0;
|
||||
onEachLazy(document.getElementById("results").childNodes, elem => {
|
||||
if (nb === 0) {
|
||||
if (nb === iter) {
|
||||
addClass(elem, "active");
|
||||
foundCurrentResultSet = true;
|
||||
} else {
|
||||
removeClass(elem, "active");
|
||||
}
|
||||
nb -= 1;
|
||||
iter += 1;
|
||||
});
|
||||
if (foundCurrentTab && foundCurrentResultSet) {
|
||||
searchState.currentTab = nb;
|
||||
} else if (nb != 0) {
|
||||
printTab(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1409,18 +1416,12 @@ window.initSearch = rawSearchIndex => {
|
||||
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
|
||||
row = searchIndex[i];
|
||||
in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
|
||||
addIntoResults(results_returned, row.id, i, -1, in_returned);
|
||||
addIntoResults(results_others, row.id, i, -1, in_returned);
|
||||
}
|
||||
}
|
||||
} else if (parsedQuery.foundElems > 0) {
|
||||
let container = results_others;
|
||||
// In the special case where only a "returned" information is available, we want to
|
||||
// put the information into the "results_returned" dict.
|
||||
if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
|
||||
container = results_returned;
|
||||
}
|
||||
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
|
||||
handleArgs(searchIndex[i], i, container);
|
||||
handleArgs(searchIndex[i], i, results_others);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1509,6 +1510,9 @@ window.initSearch = rawSearchIndex => {
|
||||
displayPath = path + "::";
|
||||
href = window.rootPath + path.replace(/::/g, "/") + "/" +
|
||||
name + "/index.html";
|
||||
} else if (type === "import") {
|
||||
displayPath = item.path + "::";
|
||||
href = window.rootPath + item.path.replace(/::/g, "/") + "/index.html#reexport." + name;
|
||||
} else if (type === "primitive" || type === "keyword") {
|
||||
displayPath = "";
|
||||
href = window.rootPath + path.replace(/::/g, "/") +
|
||||
@ -1725,12 +1729,26 @@ window.initSearch = rawSearchIndex => {
|
||||
`${typeFilter}</h1> in ${crates} </div>`;
|
||||
if (results.query.error !== null) {
|
||||
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
|
||||
output += '<div id="titles">' +
|
||||
makeTabHeader(0, "In Names", ret_others[1]) +
|
||||
"</div>";
|
||||
currentTab = 0;
|
||||
} else if (results.query.foundElems <= 1 && results.query.returned.length === 0) {
|
||||
output += `<div id="titles">` +
|
||||
makeTabHeader(0, "In Names", ret_others[1]) +
|
||||
makeTabHeader(1, "In Parameters", ret_in_args[1]) +
|
||||
makeTabHeader(2, "In Return Types", ret_returned[1]) +
|
||||
"</div>";
|
||||
} else {
|
||||
const signatureTabTitle =
|
||||
results.query.elems.length === 0 ? "In Function Return Types" :
|
||||
results.query.returned.length === 0 ? "In Function Parameters" :
|
||||
"In Function Signatures";
|
||||
output += '<div id="titles">' +
|
||||
makeTabHeader(0, signatureTabTitle, ret_others[1]) +
|
||||
"</div>";
|
||||
currentTab = 0;
|
||||
}
|
||||
output += `<div id="titles">` +
|
||||
makeTabHeader(0, "In Names", ret_others[1]) +
|
||||
makeTabHeader(1, "In Parameters", ret_in_args[1]) +
|
||||
makeTabHeader(2, "In Return Types", ret_returned[1]) +
|
||||
"</div>";
|
||||
|
||||
const resultsElem = document.createElement("div");
|
||||
resultsElem.id = "results";
|
||||
@ -1745,12 +1763,16 @@ window.initSearch = rawSearchIndex => {
|
||||
}
|
||||
search.appendChild(resultsElem);
|
||||
// Reset focused elements.
|
||||
searchState.focusedByTab = [null, null, null];
|
||||
searchState.showResults(search);
|
||||
const elems = document.getElementById("titles").childNodes;
|
||||
elems[0].onclick = () => { printTab(0); };
|
||||
elems[1].onclick = () => { printTab(1); };
|
||||
elems[2].onclick = () => { printTab(2); };
|
||||
searchState.focusedByTab = [];
|
||||
let i = 0;
|
||||
for (const elem of elems) {
|
||||
const j = i;
|
||||
elem.onclick = () => { printTab(j); };
|
||||
searchState.focusedByTab.push(null);
|
||||
i += 1;
|
||||
}
|
||||
printTab(currentTab);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// only-linux
|
||||
// assembly-output: emit-asm
|
||||
// compile-flags: -C llvm-args=--x86-asm-syntax=intel
|
||||
// compile-flags: -C symbol-mangling-version=v0
|
||||
|
||||
#![feature(asm_const, asm_sym)]
|
||||
#![crate_type = "rlib"]
|
||||
@ -24,3 +25,7 @@ fn my_func() {}
|
||||
global_asm!("call {}", sym my_func);
|
||||
// CHECK: lea rax, [rip + MY_STATIC]
|
||||
global_asm!("lea rax, [rip + {}]", sym MY_STATIC);
|
||||
// CHECK: call _RNvCsiubXh4Yz005_10global_asm6foobar
|
||||
global_asm!("call {}", sym foobar);
|
||||
// CHECK: _RNvCsiubXh4Yz005_10global_asm6foobar:
|
||||
fn foobar() { loop {} }
|
||||
|
@ -18,3 +18,10 @@ assert: "#implementors-list .impl:nth-child(2) > .code-header.in-band"
|
||||
goto: file://|DOC_PATH|/test_docs/struct.HasEmptyTraits.html
|
||||
compare-elements-position-near-false: ("#impl-EmptyTrait1", "#impl-EmptyTrait2", {"y": 30})
|
||||
compare-elements-position-near: ("#impl-EmptyTrait3 h3", "#impl-EmptyTrait3 .item-info", {"y": 30})
|
||||
|
||||
// Now check that re-exports work correctly.
|
||||
// There should be exactly one impl shown on both of these pages.
|
||||
goto: file://|DOC_PATH|/lib2/trait.TraitToReexport.html
|
||||
assert-count: ("#implementors-list .impl", 1)
|
||||
goto: file://|DOC_PATH|/implementors/trait.TraitToReexport.html
|
||||
assert-count: ("#implementors-list .impl", 1)
|
||||
|
29
src/test/rustdoc-gui/search-reexport.goml
Normal file
29
src/test/rustdoc-gui/search-reexport.goml
Normal file
@ -0,0 +1,29 @@
|
||||
// Checks that the reexports are present in the search index, can have
|
||||
// doc aliases and are highligted when their ID is the hash of the page.
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
|
||||
reload:
|
||||
// First we check that the reexport has the correct ID and no background color.
|
||||
assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;")
|
||||
assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"})
|
||||
write: (".search-input", "TheStdReexport")
|
||||
wait-for: "//a[@class='result-import']"
|
||||
assert-attribute: (
|
||||
"//a[@class='result-import']",
|
||||
{"href": "../test_docs/index.html#reexport.TheStdReexport"},
|
||||
)
|
||||
assert-text: ("//a[@class='result-import']", "test_docs::TheStdReexport")
|
||||
click: "//a[@class='result-import']"
|
||||
// We check that it has the background modified thanks to the focus.
|
||||
wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
|
||||
|
||||
// We now check that the alias is working as well on the reexport.
|
||||
write: (".search-input", "AliasForTheStdReexport")
|
||||
wait-for: "//a[@class='result-import']"
|
||||
assert-text: (
|
||||
"//a[@class='result-import']",
|
||||
"AliasForTheStdReexport - see test_docs::TheStdReexport",
|
||||
)
|
||||
// Same thing again, we click on it to ensure the background is once again set as expected.
|
||||
click: "//a[@class='result-import']"
|
||||
wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"})
|
64
src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml
Normal file
64
src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml
Normal file
@ -0,0 +1,64 @@
|
||||
// Checks that the search tab results work correctly with function signature syntax
|
||||
// First, try a search-by-name
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "Foo")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
assert-text: ("#titles > button:nth-of-type(1)", "In Names", STARTS_WITH)
|
||||
assert: "input.search-input:focus"
|
||||
// Use left-right keys
|
||||
press-key: "ArrowDown"
|
||||
assert: "#results > .search-results.active > a:nth-of-type(1):focus"
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(2)", {"class": "selected"})
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
press-key: "ArrowLeft"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
|
||||
|
||||
// Now try search-by-return
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> String")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
|
||||
assert: "input.search-input:focus"
|
||||
// Use left-right keys
|
||||
press-key: "ArrowDown"
|
||||
assert: "#results > .search-results.active > a:nth-of-type(1):focus"
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
press-key: "ArrowRight"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
press-key: "ArrowLeft"
|
||||
wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
|
||||
// Try with a search-by-return with no results
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> Something")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
|
||||
|
||||
// Try with a search-by-parameter
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "usize pattern")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
|
||||
|
||||
// Try with a search-by-parameter-and-return
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "pattern -> str")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
assert-text: ("#titles > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
|
@ -1,23 +0,0 @@
|
||||
// Checks that the first non-empty search result tab is selected if the default/currently selected
|
||||
// one is empty.
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "Foo")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
||||
|
||||
// To go back to the original "state"
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> String")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
// With this search, only the last tab shouldn't be empty so it should be selected.
|
||||
assert-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
|
||||
|
||||
// To go back to the original "state"
|
||||
goto: file://|DOC_PATH|/test_docs/index.html
|
||||
write: (".search-input", "-> Something")
|
||||
// Waiting for the search results to appear...
|
||||
wait-for: "#titles"
|
||||
// With this search, all the tabs are empty so the first one should remain selected.
|
||||
assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
|
@ -13,15 +13,16 @@ assert-css: ("#all-types", {"color": "rgb(53, 109, 164)"})
|
||||
// We check that we have the crates list and that the "current" on is "test_docs".
|
||||
assert-text: (".sidebar-elems .crate > ul > li > a.current", "test_docs")
|
||||
// And we're also supposed to have the list of items in the current module.
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Modules")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Macros")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Structs")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Enums")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Traits")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Functions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Type Definitions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Unions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Keywords")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Re-exports")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Macros")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Structs")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Enums")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Traits")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Functions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Type Definitions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Unions")
|
||||
assert-text: (".sidebar-elems section ul > li:nth-child(10)", "Keywords")
|
||||
assert-text: ("#structs + .item-table .item-left > a", "Foo")
|
||||
click: "#structs + .item-table .item-left > a"
|
||||
|
||||
|
@ -9,3 +9,12 @@ fn method() {}
|
||||
impl Whatever for Struct {
|
||||
type Foo = u8;
|
||||
}
|
||||
|
||||
mod traits {
|
||||
pub trait TraitToReexport {
|
||||
fn method() {}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use traits::TraitToReexport;
|
||||
|
@ -43,6 +43,13 @@ impl implementors::Whatever for Foo {
|
||||
type Foo = u32;
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use implementors::TraitToReexport;
|
||||
|
||||
pub struct StructToImplOnReexport;
|
||||
|
||||
impl TraitToReexport for StructToImplOnReexport {}
|
||||
|
||||
pub mod sub_mod {
|
||||
/// ```txt
|
||||
/// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
|
@ -274,3 +274,6 @@ impl EmptyTrait3 for HasEmptyTraits {}
|
||||
|
||||
mod macros;
|
||||
pub use macros::*;
|
||||
|
||||
#[doc(alias = "AliasForTheStdReexport")]
|
||||
pub use ::std as TheStdReexport;
|
||||
|
@ -12,6 +12,6 @@ pub mod __hidden {
|
||||
|
||||
// @has foo/trait.Clone.html
|
||||
// @!has - 'Foo'
|
||||
// @has implementors/foo/trait.Clone.js
|
||||
// @has implementors/core/clone/trait.Clone.js
|
||||
// @!has - 'Foo'
|
||||
pub use std::clone::Clone;
|
||||
|
@ -29,8 +29,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos
|
||||
LL | thread::spawn(move || unsafe {
|
||||
| ^^^^^^^^^^^^^^
|
||||
| |
|
||||
| in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync`
|
||||
| in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send`
|
||||
| in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync`
|
||||
...
|
||||
LL | *fptr.0.0 = 20;
|
||||
| --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
|
||||
|
@ -4,8 +4,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos
|
||||
LL | let result = panic::catch_unwind(move || {
|
||||
| ^^^^^^^
|
||||
| |
|
||||
| in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe`
|
||||
| in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe`
|
||||
| in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe`
|
||||
...
|
||||
LL | f.0()
|
||||
| --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
|
||||
|
@ -94,8 +94,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos
|
||||
LL | thread::spawn(move || unsafe {
|
||||
| ^^^^^^^^^^^^^^
|
||||
| |
|
||||
| in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync`
|
||||
| in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send`
|
||||
| in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync`
|
||||
| in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send`
|
||||
...
|
||||
LL | *fptr1.0.0 = 20;
|
||||
|
15
src/test/ui/inline-const/const-expr-generic-err.rs
Normal file
15
src/test/ui/inline-const/const-expr-generic-err.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// build-fail
|
||||
#![feature(inline_const)]
|
||||
|
||||
fn foo<T>() {
|
||||
const { assert!(std::mem::size_of::<T>() == 0); } //~ ERROR E0080
|
||||
}
|
||||
|
||||
fn bar<const N: usize>() -> usize {
|
||||
const { N - 1 } //~ ERROR E0080
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo::<i32>();
|
||||
bar::<0>();
|
||||
}
|
29
src/test/ui/inline-const/const-expr-generic-err.stderr
Normal file
29
src/test/ui/inline-const/const-expr-generic-err.stderr
Normal file
@ -0,0 +1,29 @@
|
||||
error[E0080]: evaluation of `foo::<i32>::{constant#0}` failed
|
||||
--> $DIR/const-expr-generic-err.rs:5:13
|
||||
|
|
||||
LL | const { assert!(std::mem::size_of::<T>() == 0); }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/const-expr-generic-err.rs:5:13
|
||||
|
|
||||
= note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
note: the above error was encountered while instantiating `fn foo::<i32>`
|
||||
--> $DIR/const-expr-generic-err.rs:13:5
|
||||
|
|
||||
LL | foo::<i32>();
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error[E0080]: evaluation of `bar::<0_usize>::{constant#0}` failed
|
||||
--> $DIR/const-expr-generic-err.rs:9:13
|
||||
|
|
||||
LL | const { N - 1 }
|
||||
| ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
|
||||
|
||||
note: the above error was encountered while instantiating `fn bar::<0_usize>`
|
||||
--> $DIR/const-expr-generic-err.rs:14:5
|
||||
|
|
||||
LL | bar::<0>();
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0080`.
|
10
src/test/ui/inline-const/const-expr-generic-err2.rs
Normal file
10
src/test/ui/inline-const/const-expr-generic-err2.rs
Normal file
@ -0,0 +1,10 @@
|
||||
#![feature(inline_const)]
|
||||
|
||||
fn foo<T>() {
|
||||
let _ = [0u8; const { std::mem::size_of::<T>() }];
|
||||
//~^ ERROR: constant expression depends on a generic parameter
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo::<i32>();
|
||||
}
|
10
src/test/ui/inline-const/const-expr-generic-err2.stderr
Normal file
10
src/test/ui/inline-const/const-expr-generic-err2.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: constant expression depends on a generic parameter
|
||||
--> $DIR/const-expr-generic-err2.rs:4:19
|
||||
|
|
||||
LL | let _ = [0u8; const { std::mem::size_of::<T>() }];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this may fail depending on what value the parameter takes
|
||||
|
||||
error: aborting due to previous error
|
||||
|
15
src/test/ui/inline-const/const-expr-generic.rs
Normal file
15
src/test/ui/inline-const/const-expr-generic.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// check-pass
|
||||
#![feature(inline_const)]
|
||||
|
||||
fn foo<T>() -> usize {
|
||||
const { std::mem::size_of::<T>() }
|
||||
}
|
||||
|
||||
fn bar<const N: usize>() -> usize {
|
||||
const { N + 1 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo::<i32>();
|
||||
bar::<1>();
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(inline_const_pat)]
|
||||
#![feature(generic_const_exprs)]
|
||||
|
||||
// rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter
|
||||
|
||||
@ -16,7 +15,7 @@ const fn f(x: usize) -> usize {
|
||||
x + 1
|
||||
}
|
||||
|
||||
fn bar<const V: usize>() where [(); f(V)]: {
|
||||
fn bar<const V: usize>() {
|
||||
match 0 {
|
||||
const { f(V) } => {},
|
||||
//~^ ERROR constant pattern depends on a generic parameter
|
||||
|
@ -1,17 +1,17 @@
|
||||
error[E0158]: const parameters cannot be referenced in patterns
|
||||
--> $DIR/const-match-pat-generic.rs:9:9
|
||||
--> $DIR/const-match-pat-generic.rs:8:9
|
||||
|
|
||||
LL | const { V } => {},
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: constant pattern depends on a generic parameter
|
||||
--> $DIR/const-match-pat-generic.rs:21:9
|
||||
--> $DIR/const-match-pat-generic.rs:20:9
|
||||
|
|
||||
LL | const { f(V) } => {},
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: constant pattern depends on a generic parameter
|
||||
--> $DIR/const-match-pat-generic.rs:21:9
|
||||
--> $DIR/const-match-pat-generic.rs:20:9
|
||||
|
|
||||
LL | const { f(V) } => {},
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
@ -0,0 +1,24 @@
|
||||
struct Thing;
|
||||
|
||||
trait Method<T> {
|
||||
fn method(&self) -> T;
|
||||
fn mut_method(&mut self) -> T;
|
||||
}
|
||||
|
||||
impl Method<i32> for Thing {
|
||||
fn method(&self) -> i32 { 0 }
|
||||
fn mut_method(&mut self) -> i32 { 0 }
|
||||
}
|
||||
|
||||
impl Method<u32> for Thing {
|
||||
fn method(&self) -> u32 { 0 }
|
||||
fn mut_method(&mut self) -> u32 { 0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let thing = Thing;
|
||||
thing.method();
|
||||
//~^ ERROR type annotations needed
|
||||
//~| ERROR type annotations needed
|
||||
thing.mut_method(); //~ ERROR type annotations needed
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:20:11
|
||||
|
|
||||
LL | thing.method();
|
||||
| ------^^^^^^--
|
||||
| | |
|
||||
| | cannot infer type for type parameter `T` declared on the trait `Method`
|
||||
| this method call resolves to `T`
|
||||
|
||||
error[E0283]: type annotations needed
|
||||
--> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:20:11
|
||||
|
|
||||
LL | thing.method();
|
||||
| ------^^^^^^--
|
||||
| | |
|
||||
| | cannot infer type for type parameter `T` declared on the trait `Method`
|
||||
| this method call resolves to `T`
|
||||
|
|
||||
note: multiple `impl`s satisfying `Thing: Method<_>` found
|
||||
--> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:8:1
|
||||
|
|
||||
LL | impl Method<i32> for Thing {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | impl Method<u32> for Thing {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: use the fully qualified path for the potential candidates
|
||||
|
|
||||
LL | <Thing as Method<i32>>::method(&thing);
|
||||
| ++++++++++++++++++++++++++++++++ ~
|
||||
LL | <Thing as Method<u32>>::method(&thing);
|
||||
| ++++++++++++++++++++++++++++++++ ~
|
||||
|
||||
error[E0283]: type annotations needed
|
||||
--> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:23:11
|
||||
|
|
||||
LL | thing.mut_method();
|
||||
| ------^^^^^^^^^^--
|
||||
| | |
|
||||
| | cannot infer type for type parameter `T` declared on the trait `Method`
|
||||
| this method call resolves to `T`
|
||||
|
|
||||
note: multiple `impl`s satisfying `Thing: Method<_>` found
|
||||
--> $DIR/suggest-fully-qualified-path-with-appropriate-params.rs:8:1
|
||||
|
|
||||
LL | impl Method<i32> for Thing {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | impl Method<u32> for Thing {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: use the fully qualified path for the potential candidates
|
||||
|
|
||||
LL | <Thing as Method<i32>>::mut_method(&mut thing);
|
||||
| +++++++++++++++++++++++++++++++++++++++ ~
|
||||
LL | <Thing as Method<u32>>::mut_method(&mut thing);
|
||||
| +++++++++++++++++++++++++++++++++++++++ ~
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0282, E0283.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
@ -1,4 +1,4 @@
|
||||
error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
|
||||
error[E0080]: evaluation of `main::{constant#3}` failed
|
||||
--> $DIR/indexing_slicing_index.rs:31:14
|
||||
|
|
||||
LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
|
||||
|
Loading…
Reference in New Issue
Block a user