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:
bors 2022-05-06 20:14:01 +00:00
commit 77652b9ef3
38 changed files with 630 additions and 257 deletions

View file

@ -1,5 +1,6 @@
//! Borrow checker diagnostics. //! Borrow checker diagnostics.
use itertools::Itertools;
use rustc_const_eval::util::{call_kind, CallDesugaringKind}; use rustc_const_eval::util::{call_kind, CallDesugaringKind};
use rustc_errors::{Applicability, Diagnostic}; use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir; use rustc_hir as hir;
@ -161,158 +162,103 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} }
/// End-user visible description of `place` if one can be found. /// 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> { pub(super) fn describe_place(&self, place_ref: PlaceRef<'tcx>) -> Option<String> {
self.describe_place_with_options(place_ref, IncludingDowncast(false)) self.describe_place_with_options(place_ref, IncludingDowncast(false))
} }
/// End-user visible description of `place` if one can be found. If the /// End-user visible description of `place` if one can be found. If the place is a temporary
/// place is a temporary for instance, None will be returned. /// for instance, `None` will be returned.
/// `IncludingDowncast` parameter makes the function return `Err` if `ProjectionElem` is /// `IncludingDowncast` parameter makes the function return `None` if `ProjectionElem` is
/// `Downcast` and `IncludingDowncast` is true /// `Downcast` and `IncludingDowncast` is true
pub(super) fn describe_place_with_options( pub(super) fn describe_place_with_options(
&self, &self,
place: PlaceRef<'tcx>, place: PlaceRef<'tcx>,
including_downcast: IncludingDowncast, including_downcast: IncludingDowncast,
) -> Option<String> { ) -> Option<String> {
let local = place.local;
let mut autoderef_index = None;
let mut buf = String::new(); let mut buf = String::new();
match self.append_place_to_string(place, &mut buf, false, &including_downcast) { let mut ok = self.append_local_to_string(local, &mut buf);
Ok(()) => Some(buf),
Err(()) => None,
}
}
/// Appends end-user visible description of `place` to `buf`. for (index, elem) in place.projection.into_iter().enumerate() {
fn append_place_to_string( match elem {
&self, ProjectionElem::Deref => {
place: PlaceRef<'tcx>, if index == 0 {
buf: &mut String, if self.body.local_decls[local].is_ref_for_guard() {
mut autoderef: bool, continue;
including_downcast: &IncludingDowncast, }
) -> Result<(), ()> { if let Some(box LocalInfo::StaticRef { def_id, .. }) =
match place { &self.body.local_decls[local].local_info
PlaceRef { local, projection: [] } => { {
self.append_local_to_string(local, buf)?; buf.push_str(self.infcx.tcx.item_name(*def_id).as_str());
} ok = Ok(());
PlaceRef { local, projection: [ProjectionElem::Deref] } continue;
if self.body.local_decls[local].is_ref_for_guard() => }
{ }
self.append_place_to_string( if let Some(field) = self.is_upvar_field_projection(PlaceRef {
PlaceRef { local, projection: &[] }, local,
buf, projection: place.projection.split_at(index + 1).0,
autoderef, }) {
&including_downcast, let var_index = field.index();
)?; buf = self.upvars[var_index].place.to_string(self.infcx.tcx);
} ok = Ok(());
PlaceRef { local, projection: [ProjectionElem::Deref] } if !self.upvars[var_index].by_ref {
if self.body.local_decls[local].is_ref_to_static() => buf.insert(0, '*');
{ }
let local_info = &self.body.local_decls[local].local_info; } else {
if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info { if autoderef_index.is_none() {
buf.push_str(self.infcx.tcx.item_name(def_id).as_str()); autoderef_index =
} else { match place.projection.into_iter().rev().find_position(|elem| {
unreachable!(); !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().map(|_| buf)
Ok(())
} }
/// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have /// Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have

View file

@ -1441,7 +1441,7 @@ pub enum AsyncGeneratorKind {
/// An explicit `async` block written by the user. /// An explicit `async` block written by the user.
Block, Block,
/// An explicit `async` block written by the user. /// An explicit `async` closure written by the user.
Closure, Closure,
/// The `async` block generated as the body of an async function. /// The `async` block generated as the body of an async function.
@ -2078,10 +2078,7 @@ pub enum YieldSource {
impl YieldSource { impl YieldSource {
pub fn is_await(&self) -> bool { pub fn is_await(&self) -> bool {
match self { matches!(self, YieldSource::Await { .. })
YieldSource::Await { .. } => true,
YieldSource::Yield => false,
}
} }
} }

View file

@ -731,6 +731,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)` // | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)`
// | // |
// = note: cannot satisfy `Impl: Into<_>` // = note: cannot satisfy `Impl: Into<_>`
debug!(?segment);
if !impl_candidates.is_empty() && e.span.contains(span) if !impl_candidates.is_empty() && e.span.contains(span)
&& let Some(expr) = exprs.first() && let Some(expr) = exprs.first()
&& let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind && let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
@ -739,9 +740,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let mut eraser = TypeParamEraser(self.tcx); let mut eraser = TypeParamEraser(self.tcx);
let candidate_len = impl_candidates.len(); let candidate_len = impl_candidates.len();
let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| { 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); let candidate = candidate.super_fold_with(&mut eraser);
vec![ vec![
(expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)), (expr.span.shrink_to_lo(), format!("{}::{}({}", candidate, segment.ident, prefix)),
if exprs.len() == 1 { if exprs.len() == 1 {
(expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string()) (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string())
} else { } else {

View file

@ -136,6 +136,10 @@ pub trait Printer<'tcx>: Sized {
match key.disambiguated_data.data { match key.disambiguated_data.data {
// Closures' own generics are only captures, don't print them. // Closures' own generics are only captures, don't print them.
DefPathData::ClosureExpr => {} 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 // If we have any generic arguments to print, we do that
// on top of the same path, but without its own generics. // on top of the same path, but without its own generics.

View file

@ -158,6 +158,7 @@ impl<'tcx> Cx<'tcx> {
} }
fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { 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_ty = self.typeck_results().expr_ty(expr);
let expr_span = expr.span; let expr_span = expr.span;
let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); let temp_lifetime = self.region_scope_tree.temporary_scope(expr.hir_id.local_id);
@ -196,7 +197,7 @@ impl<'tcx> Cx<'tcx> {
let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e)); let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
let tupled_args = Expr { let tupled_args = Expr {
ty: self.tcx.mk_tup(arg_tys), ty: tcx.mk_tup(arg_tys),
temp_lifetime, temp_lifetime,
span: expr.span, span: expr.span,
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
@ -488,24 +489,24 @@ impl<'tcx> Cx<'tcx> {
out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)), out_expr: out_expr.as_ref().map(|expr| self.mirror_expr(expr)),
}, },
hir::InlineAsmOperand::Const { ref anon_const } => { 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( let value = mir::ConstantKind::from_anon_const(
self.tcx, tcx,
anon_const_def_id, anon_const_def_id,
self.param_env, 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 } InlineAsmOperand::Const { value, span }
} }
hir::InlineAsmOperand::SymFn { ref anon_const } => { 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( let value = mir::ConstantKind::from_anon_const(
self.tcx, tcx,
anon_const_def_id, anon_const_def_id,
self.param_env, 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 } InlineAsmOperand::SymFn { value, span }
} }
@ -519,21 +520,16 @@ impl<'tcx> Cx<'tcx> {
}, },
hir::ExprKind::ConstBlock(ref anon_const) => { hir::ExprKind::ConstBlock(ref anon_const) => {
let tcx = self.tcx; let ty = self.typeck_results().node_type(anon_const.hir_id);
let local_def_id = tcx.hir().local_def_id(anon_const.hir_id); let did = tcx.hir().local_def_id(anon_const.hir_id).to_def_id();
let anon_const_def_id = local_def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did);
// 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 parent_substs = let parent_substs =
tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id)); tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
let substs = let substs =
InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty }) InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
.substs; .substs;
ExprKind::ConstBlock { did: anon_const_def_id, substs } ExprKind::ConstBlock { did, substs }
} }
// Now comes the rote stuff: // Now comes the rote stuff:
hir::ExprKind::Repeat(ref v, _) => { hir::ExprKind::Repeat(ref v, _) => {
@ -591,7 +587,7 @@ impl<'tcx> Cx<'tcx> {
} }
hir::ExprKind::Field(ref source, ..) => ExprKind::Field { hir::ExprKind::Field(ref source, ..) => ExprKind::Field {
lhs: self.mirror_expr(source), 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) => { hir::ExprKind::Cast(ref source, ref cast_ty) => {
// Check for a user-given type annotation on this `cast` // Check for a user-given type annotation on this `cast`
@ -640,7 +636,7 @@ impl<'tcx> Cx<'tcx> {
let (d, o) = adt_def.discriminant_def_for_variant(idx); let (d, o) = adt_def.discriminant_def_for_variant(idx);
use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::util::IntTypeExt;
let ty = adt_def.repr().discr_type(); let ty = adt_def.repr().discr_type();
let ty = ty.to_ty(self.tcx()); let ty = ty.to_ty(tcx);
Some((d, o, ty)) Some((d, o, ty))
} }
_ => None, _ => None,
@ -652,8 +648,7 @@ impl<'tcx> Cx<'tcx> {
let source = if let Some((did, offset, var_ty)) = var { let source = if let Some((did, offset, var_ty)) = var {
let param_env_ty = self.param_env.and(var_ty); let param_env_ty = self.param_env.and(var_ty);
let size = self let size = tcx
.tcx
.layout_of(param_env_ty) .layout_of(param_env_ty)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
panic!("could not compute layout for {:?}: {:?}", param_env_ty, e) panic!("could not compute layout for {:?}: {:?}", param_env_ty, e)
@ -671,7 +666,7 @@ impl<'tcx> Cx<'tcx> {
Some(did) => { Some(did) => {
// in case we are offsetting from a computed discriminant // in case we are offsetting from a computed discriminant
// and not the beginning of discriminants (which is always `0`) // 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 = let kind =
ExprKind::NamedConst { def_id: did, substs, user_ty: None }; ExprKind::NamedConst { def_id: did, substs, user_ty: None };
let lhs = self.thir.exprs.push(Expr { let lhs = self.thir.exprs.push(Expr {

View file

@ -445,12 +445,9 @@ fn collect_items_rec<'tcx>(
// depend on any other items. // depend on any other items.
} }
hir::InlineAsmOperand::SymFn { anon_const } => { hir::InlineAsmOperand::SymFn { anon_const } => {
let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id(); let fn_ty =
if let Ok(val) = tcx.const_eval_poly(def_id) { tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
rustc_data_structures::stack::ensure_sufficient_stack(|| { visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors);
collect_const_value(tcx, val, &mut neighbors);
});
}
} }
hir::InlineAsmOperand::SymStatic { path: _, def_id } => { hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
let instance = Instance::mono(tcx, *def_id); let instance = Instance::mono(tcx, *def_id);

View file

@ -3105,6 +3105,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
); );
} }
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>) { fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
// First, record candidate traits for this expression if it could // First, record candidate traits for this expression if it could
// result in the invocation of a method call. // result in the invocation of a method call.
@ -3261,7 +3268,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
}); });
} }
ExprKind::ConstBlock(ref ct) => { ExprKind::ConstBlock(ref ct) => {
self.resolve_anon_const(ct, IsRepeatExpr::No); self.resolve_inline_const(ct);
} }
ExprKind::Index(ref elem, ref idx) => { ExprKind::Index(ref elem, ref idx) => {
self.resolve_expr(elem, Some(expr)); self.resolve_expr(elem, Some(expr));

View file

@ -914,6 +914,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
reasons.auto_traits.extend(auto_trait_reasons); reasons.auto_traits.extend(auto_trait_reasons);
reasons.drop_order = drop_order; 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 reasons
} }

View file

@ -248,7 +248,16 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
} }
// Index this method for searching later on. // 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 { let (parent, is_inherent_impl_item) = match *item.kind {
clean::StrippedItem(..) => ((None, None), false), clean::StrippedItem(..) => ((None, None), false),
clean::AssocConstItem(..) | clean::AssocTypeItem(..) clean::AssocConstItem(..) | clean::AssocTypeItem(..)

View file

@ -2542,7 +2542,16 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
let item_sections_in_use: FxHashSet<_> = items let item_sections_in_use: FxHashSet<_> = items
.iter() .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_())) .map(|it| item_ty_to_section(it.type_()))
.collect(); .collect();
for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) { for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {

View file

@ -3,7 +3,7 @@ use clean::AttributesExt;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt; use std::fmt;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::CtorKind; use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -346,7 +346,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
w.write_str(ITEM_TABLE_ROW_OPEN); w.write_str(ITEM_TABLE_ROW_OPEN);
write!( write!(
w, w,
"<div class=\"item-left {stab}{add}import-item\">\ "<div class=\"item-left {stab}{add}import-item\"{id}>\
<code>{vis}{imp}</code>\ <code>{vis}{imp}</code>\
</div>\ </div>\
<div class=\"item-right docblock-short\">{stab_tags}</div>", <div class=\"item-right docblock-short\">{stab_tags}</div>",
@ -355,6 +355,11 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
vis = myitem.visibility.print_with_space(myitem.item_id, cx), vis = myitem.visibility.print_with_space(myitem.item_id, cx),
imp = import.print(cx), imp = import.print(cx),
stab_tags = stab_tags.unwrap_or_default(), 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); w.write_str(ITEM_TABLE_ROW_CLOSE);
} }
@ -790,16 +795,18 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All); render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All);
let cache = cx.cache(); let cache = cx.cache();
let mut extern_crates = FxHashSet::default();
if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) { 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 // 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. // if any Types with the same name but different DefId have been found.
let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default(); let mut implementor_dups: FxHashMap<Symbol, (DefId, bool)> = FxHashMap::default();
for implementor in implementors { for implementor in implementors {
match implementor.inner_impl().for_ { if let Some(did) = implementor.inner_impl().for_.without_borrowed_ref().def_id(cx.cache()) &&
clean::Type::Path { ref path } !did.is_local() {
| clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } extern_crates.insert(did.krate);
if !path.is_assoc_ty() => }
{ match implementor.inner_impl().for_.without_borrowed_ref() {
clean::Type::Path { ref path } if !path.is_assoc_ty() => {
let did = path.def_id(); let did = path.def_id();
let &mut (prev_did, ref mut has_duplicates) = let &mut (prev_did, ref mut has_duplicates) =
implementor_dups.entry(path.last()).or_insert((did, false)); implementor_dups.entry(path.last()).or_insert((did, false));
@ -898,20 +905,96 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
} }
} }
// 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("..") let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..")
.take(cx.current.len()) .take(cx.current.len())
.chain(std::iter::once("implementors")) .chain(std::iter::once("implementors"))
.collect(); .collect();
if it.item_id.is_local() { if let Some(did) = it.item_id.as_def_id() &&
js_src_path.extend(cx.current.iter().copied()); 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 { } else {
let (ref path, _) = cache.external_paths[&it.item_id.expect_def_id()]; js_src_path.extend(cx.current.iter().copied());
js_src_path.extend(path[..path.len() - 1].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!( write!(
w, 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(), src = js_src_path.finish(),
); );
} }

View file

@ -501,10 +501,13 @@ pub(super) fn write_shared(
// //
// FIXME: this is a vague explanation for why this can't be a `get`, in // FIXME: this is a vague explanation for why this can't be a `get`, in
// theory it should be... // theory it should be...
let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) { let (remote_path, remote_item_type) = match cache.exact_paths.get(&did) {
Some(p) => p, 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) { None => match cache.external_paths.get(&did) {
Some(p) => p, Some((p, t)) => (p, t),
None => continue, None => continue,
}, },
}; };

View file

@ -1333,6 +1333,11 @@ pre.rust {
border-top: 2px solid; border-top: 2px solid;
} }
#titles > button:first-child:last-child {
margin-right: 1px;
width: calc(100% - 1px);
}
#titles > button:not(:last-child) { #titles > button:not(:last-child) {
margin-right: 1px; margin-right: 1px;
width: calc(33.3% - 1px); width: calc(33.3% - 1px);

View file

@ -759,8 +759,14 @@ function loadCss(cssFileName) {
const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent; const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
const baseIdName = "impl-" + traitName + "-"; const baseIdName = "impl-" + traitName + "-";
const libs = Object.getOwnPropertyNames(imp); 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) { for (const lib of libs) {
if (lib === window.currentCrate) { if (lib === window.currentCrate || ignoreExternCrates.indexOf(lib) !== -1) {
continue; continue;
} }
const structs = imp[lib]; const structs = imp[lib];

View file

@ -45,26 +45,33 @@ const TY_KEYWORD = itemTypes.indexOf("keyword");
// In the search display, allows to switch between tabs. // In the search display, allows to switch between tabs.
function printTab(nb) { function printTab(nb) {
if (nb === 0 || nb === 1 || nb === 2) { let iter = 0;
searchState.currentTab = nb; let foundCurrentTab = false;
} let foundCurrentResultSet = false;
let nb_copy = nb;
onEachLazy(document.getElementById("titles").childNodes, elem => { onEachLazy(document.getElementById("titles").childNodes, elem => {
if (nb_copy === 0) { if (nb === iter) {
addClass(elem, "selected"); addClass(elem, "selected");
foundCurrentTab = true;
} else { } else {
removeClass(elem, "selected"); removeClass(elem, "selected");
} }
nb_copy -= 1; iter += 1;
}); });
iter = 0;
onEachLazy(document.getElementById("results").childNodes, elem => { onEachLazy(document.getElementById("results").childNodes, elem => {
if (nb === 0) { if (nb === iter) {
addClass(elem, "active"); addClass(elem, "active");
foundCurrentResultSet = true;
} else { } else {
removeClass(elem, "active"); 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) { for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i]; row = searchIndex[i];
in_returned = checkReturned(row, elem, parsedQuery.typeFilter); 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) { } 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) { 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 + "::"; displayPath = path + "::";
href = window.rootPath + path.replace(/::/g, "/") + "/" + href = window.rootPath + path.replace(/::/g, "/") + "/" +
name + "/index.html"; 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") { } else if (type === "primitive" || type === "keyword") {
displayPath = ""; displayPath = "";
href = window.rootPath + path.replace(/::/g, "/") + href = window.rootPath + path.replace(/::/g, "/") +
@ -1725,12 +1729,26 @@ window.initSearch = rawSearchIndex => {
`${typeFilter}</h1> in ${crates} </div>`; `${typeFilter}</h1> in ${crates} </div>`;
if (results.query.error !== null) { if (results.query.error !== null) {
output += `<h3>Query parser error: "${results.query.error}".</h3>`; 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"); const resultsElem = document.createElement("div");
resultsElem.id = "results"; resultsElem.id = "results";
@ -1745,12 +1763,16 @@ window.initSearch = rawSearchIndex => {
} }
search.appendChild(resultsElem); search.appendChild(resultsElem);
// Reset focused elements. // Reset focused elements.
searchState.focusedByTab = [null, null, null];
searchState.showResults(search); searchState.showResults(search);
const elems = document.getElementById("titles").childNodes; const elems = document.getElementById("titles").childNodes;
elems[0].onclick = () => { printTab(0); }; searchState.focusedByTab = [];
elems[1].onclick = () => { printTab(1); }; let i = 0;
elems[2].onclick = () => { printTab(2); }; for (const elem of elems) {
const j = i;
elem.onclick = () => { printTab(j); };
searchState.focusedByTab.push(null);
i += 1;
}
printTab(currentTab); printTab(currentTab);
} }

View file

@ -2,6 +2,7 @@
// only-linux // only-linux
// assembly-output: emit-asm // assembly-output: emit-asm
// compile-flags: -C llvm-args=--x86-asm-syntax=intel // compile-flags: -C llvm-args=--x86-asm-syntax=intel
// compile-flags: -C symbol-mangling-version=v0
#![feature(asm_const, asm_sym)] #![feature(asm_const, asm_sym)]
#![crate_type = "rlib"] #![crate_type = "rlib"]
@ -24,3 +25,7 @@ global_asm!("movl ${}, %ecx", const 5, options(att_syntax));
global_asm!("call {}", sym my_func); global_asm!("call {}", sym my_func);
// CHECK: lea rax, [rip + MY_STATIC] // CHECK: lea rax, [rip + MY_STATIC]
global_asm!("lea rax, [rip + {}]", sym 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 {} }

View file

@ -18,3 +18,10 @@ assert: "#implementors-list .impl:nth-child(2) > .code-header.in-band"
goto: file://|DOC_PATH|/test_docs/struct.HasEmptyTraits.html goto: file://|DOC_PATH|/test_docs/struct.HasEmptyTraits.html
compare-elements-position-near-false: ("#impl-EmptyTrait1", "#impl-EmptyTrait2", {"y": 30}) compare-elements-position-near-false: ("#impl-EmptyTrait1", "#impl-EmptyTrait2", {"y": 30})
compare-elements-position-near: ("#impl-EmptyTrait3 h3", "#impl-EmptyTrait3 .item-info", {"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)

View 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)"})

View 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)

View file

@ -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"})

View file

@ -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". // 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") 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. // 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(1)", "Re-exports")
assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Macros") assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules")
assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Structs") assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Macros")
assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Enums") assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Structs")
assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Traits") assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Enums")
assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Functions") assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Traits")
assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Type Definitions") assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Functions")
assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Unions") assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Type Definitions")
assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Keywords") 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") assert-text: ("#structs + .item-table .item-left > a", "Foo")
click: "#structs + .item-table .item-left > a" click: "#structs + .item-table .item-left > a"

View file

@ -9,3 +9,12 @@ pub struct Struct;
impl Whatever for Struct { impl Whatever for Struct {
type Foo = u8; type Foo = u8;
} }
mod traits {
pub trait TraitToReexport {
fn method() {}
}
}
#[doc(inline)]
pub use traits::TraitToReexport;

View file

@ -43,6 +43,13 @@ impl implementors::Whatever for Foo {
type Foo = u32; type Foo = u32;
} }
#[doc(inline)]
pub use implementors::TraitToReexport;
pub struct StructToImplOnReexport;
impl TraitToReexport for StructToImplOnReexport {}
pub mod sub_mod { pub mod sub_mod {
/// ```txt /// ```txt
/// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa /// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

View file

@ -274,3 +274,6 @@ impl EmptyTrait3 for HasEmptyTraits {}
mod macros; mod macros;
pub use macros::*; pub use macros::*;
#[doc(alias = "AliasForTheStdReexport")]
pub use ::std as TheStdReexport;

View file

@ -12,6 +12,6 @@ pub mod __hidden {
// @has foo/trait.Clone.html // @has foo/trait.Clone.html
// @!has - 'Foo' // @!has - 'Foo'
// @has implementors/foo/trait.Clone.js // @has implementors/core/clone/trait.Clone.js
// @!has - 'Foo' // @!has - 'Foo'
pub use std::clone::Clone; pub use std::clone::Clone;

View file

@ -29,8 +29,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos
LL | thread::spawn(move || unsafe { 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 `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; 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` | --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`

View file

@ -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 || { 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 `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() LL | f.0()
| --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0` | --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`

View file

@ -94,8 +94,8 @@ error: changes to closure capture in Rust 2021 will affect which traits the clos
LL | thread::spawn(move || unsafe { 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 `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` | 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; LL | *fptr1.0.0 = 20;

View 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>();
}

View 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`.

View 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>();
}

View 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

View 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>();
}

View file

@ -1,6 +1,5 @@
#![allow(incomplete_features)] #![allow(incomplete_features)]
#![feature(inline_const_pat)] #![feature(inline_const_pat)]
#![feature(generic_const_exprs)]
// rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter // 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 x + 1
} }
fn bar<const V: usize>() where [(); f(V)]: { fn bar<const V: usize>() {
match 0 { match 0 {
const { f(V) } => {}, const { f(V) } => {},
//~^ ERROR constant pattern depends on a generic parameter //~^ ERROR constant pattern depends on a generic parameter

View file

@ -1,17 +1,17 @@
error[E0158]: const parameters cannot be referenced in patterns 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 } => {}, LL | const { V } => {},
| ^^^^^^^^^^^ | ^^^^^^^^^^^
error: constant pattern depends on a generic parameter 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) } => {}, LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: constant pattern depends on a generic parameter 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) } => {}, LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^

View file

@ -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
}

View file

@ -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`.

View file

@ -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 --> $DIR/indexing_slicing_index.rs:31:14
| |
LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.