Auto merge of #120170 - GuillaumeGomez:rollup-edqdf30, r=GuillaumeGomez

Rollup of 6 pull requests

Successful merges:

 - #119997 (Fix impl stripped in rustdoc HTML whereas it should not be in case the impl is implemented on a type alias)
 - #120000 (Ensure `callee_id`s are body owners)
 - #120063 (Remove special handling of `box` expressions from parser)
 - #120116 (Remove alignment-changing in-place collect)
 - #120138 (Increase vscode settings.json `git.detectSubmodulesLimit`)
 - #120169 (Spelling fix)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-01-20 19:55:26 +00:00
commit 038d115cd8
49 changed files with 97 additions and 76 deletions

View File

@ -33,7 +33,6 @@ use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::source_map::{self, Spanned}; use rustc_span::source_map::{self, Spanned};
use rustc_span::symbol::kw::PathRoot;
use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span};
use thin_vec::{thin_vec, ThinVec}; use thin_vec::{thin_vec, ThinVec};
@ -642,26 +641,13 @@ impl<'a> Parser<'a> {
} }
/// Parse `box expr` - this syntax has been removed, but we still parse this /// Parse `box expr` - this syntax has been removed, but we still parse this
/// for now to provide an automated way to fix usages of it /// for now to provide a more useful error
fn parse_expr_box(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {
let (span, expr) = self.parse_expr_prefix_common(lo)?; let (span, _) = self.parse_expr_prefix_common(box_kw)?;
let code = self.sess.source_map().span_to_snippet(span.with_lo(lo.hi())).unwrap(); let inner_span = span.with_lo(box_kw.hi());
self.dcx().emit_err(errors::BoxSyntaxRemoved { span, code: code.trim() }); let code = self.sess.source_map().span_to_snippet(inner_span).unwrap();
// So typechecking works, parse `box <expr>` as `::std::boxed::Box::new(expr)` self.dcx().emit_err(errors::BoxSyntaxRemoved { span: span, code: code.trim() });
let path = Path { Ok((span, ExprKind::Err))
span,
segments: [
PathSegment::from_ident(Ident::with_dummy_span(PathRoot)),
PathSegment::from_ident(Ident::with_dummy_span(sym::std)),
PathSegment::from_ident(Ident::from_str("boxed")),
PathSegment::from_ident(Ident::from_str("Box")),
PathSegment::from_ident(Ident::with_dummy_span(sym::new)),
]
.into(),
tokens: None,
};
let path = self.mk_expr(span, ExprKind::Path(None, path));
Ok((span, self.mk_call(path, ThinVec::from([expr]))))
} }
fn is_mistaken_not_ident_negation(&self) -> bool { fn is_mistaken_not_ident_negation(&self) -> bool {

View File

@ -168,7 +168,9 @@ const fn in_place_collectible<DEST, SRC>(
step_merge: Option<NonZeroUsize>, step_merge: Option<NonZeroUsize>,
step_expand: Option<NonZeroUsize>, step_expand: Option<NonZeroUsize>,
) -> bool { ) -> bool {
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() } { // Require matching alignments because an alignment-changing realloc is inefficient on many
// system allocators and better implementations would require the unstable Allocator trait.
if const { SRC::IS_ZST || DEST::IS_ZST || mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
return false; return false;
} }
@ -188,7 +190,8 @@ const fn in_place_collectible<DEST, SRC>(
const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool { const fn needs_realloc<SRC, DEST>(src_cap: usize, dst_cap: usize) -> bool {
if const { mem::align_of::<SRC>() != mem::align_of::<DEST>() } { if const { mem::align_of::<SRC>() != mem::align_of::<DEST>() } {
return src_cap > 0; // FIXME: use unreachable! once that works in const
panic!("in_place_collectible() prevents this");
} }
// If src type size is an integer multiple of the destination type size then // If src type size is an integer multiple of the destination type size then
@ -276,8 +279,8 @@ where
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap }; let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap };
src.forget_allocation_drop_remaining(); src.forget_allocation_drop_remaining();
// Adjust the allocation if the alignment didn't match or the source had a capacity in bytes // Adjust the allocation if the source had a capacity in bytes that wasn't a multiple
// that wasn't a multiple of the destination type size. // of the destination type size.
// Since the discrepancy should generally be small this should only result in some // Since the discrepancy should generally be small this should only result in some
// bookkeeping updates and no memmove. // bookkeeping updates and no memmove.
if needs_realloc::<I::Src, T>(src_cap, dst_cap) { if needs_realloc::<I::Src, T>(src_cap, dst_cap) {
@ -290,7 +293,7 @@ where
let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap); let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap);
let old_layout = Layout::from_size_align_unchecked(src_size, src_align); let old_layout = Layout::from_size_align_unchecked(src_size, src_align);
// The must be equal or smaller for in-place iteration to be possible // The allocation must be equal or smaller for in-place iteration to be possible
// therefore the new layout must be ≤ the old one and therefore valid. // therefore the new layout must be ≤ the old one and therefore valid.
let dst_align = mem::align_of::<T>(); let dst_align = mem::align_of::<T>();
let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap); let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap);

View File

@ -13,13 +13,13 @@ use super::{IntoIter, SpecExtend, SpecFromIterNested, Vec};
/// +-+-----------+ /// +-+-----------+
/// | /// |
/// v /// v
/// +-+-------------------------------+ +---------------------+ /// +-+---------------------------------+ +---------------------+
/// |SpecFromIter +---->+SpecFromIterNested | /// |SpecFromIter +---->+SpecFromIterNested |
/// |where I: | | |where I: | /// |where I: | | |where I: |
/// | Iterator (default)----------+ | | Iterator (default) | /// | Iterator (default)------------+ | | Iterator (default) |
/// | vec::IntoIter | | | TrustedLen | /// | vec::IntoIter | | | TrustedLen |
/// | SourceIterMarker---fallback-+ | +---------------------+ /// | InPlaceCollect--(fallback to)-+ | +---------------------+
/// +---------------------------------+ /// +-----------------------------------+
/// ``` /// ```
pub(super) trait SpecFromIter<T, I> { pub(super) trait SpecFromIter<T, I> {
fn from_iter(iter: I) -> Self; fn from_iter(iter: I) -> Self;

View File

@ -415,7 +415,7 @@ where
// Request and its methods // Request and its methods
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/// `Request` supports generic, type-driven access to data. It's use is currently restricted to the /// `Request` supports generic, type-driven access to data. Its use is currently restricted to the
/// standard library in cases where trait authors wish to allow trait implementors to share generic /// standard library in cases where trait authors wish to allow trait implementors to share generic
/// information across trait boundaries. The motivating and prototypical use case is /// information across trait boundaries. The motivating and prototypical use case is
/// `core::error::Error` which would otherwise require a method per concrete type (eg. /// `core::error::Error` which would otherwise require a method per concrete type (eg.

View File

@ -37,6 +37,7 @@ static SETTINGS_HASHES: &[&str] = &[
"3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
"47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
"b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
"828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
]; ];
static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json"); static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json");

View File

@ -1,4 +1,5 @@
{ {
"git.detectSubmodulesLimit": 20,
"rust-analyzer.check.invocationLocation": "root", "rust-analyzer.check.invocationLocation": "root",
"rust-analyzer.check.invocationStrategy": "once", "rust-analyzer.check.invocationStrategy": "once",
"rust-analyzer.check.overrideCommand": [ "rust-analyzer.check.overrideCommand": [

View File

@ -56,13 +56,10 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
| clean::TraitItem(..) | clean::TraitItem(..)
| clean::FunctionItem(..) | clean::FunctionItem(..)
| clean::VariantItem(..) | clean::VariantItem(..)
| clean::MethodItem(..)
| clean::ForeignFunctionItem(..) | clean::ForeignFunctionItem(..)
| clean::ForeignStaticItem(..) | clean::ForeignStaticItem(..)
| clean::ConstantItem(..) | clean::ConstantItem(..)
| clean::UnionItem(..) | clean::UnionItem(..)
| clean::AssocConstItem(..)
| clean::AssocTypeItem(..)
| clean::TraitAliasItem(..) | clean::TraitAliasItem(..)
| clean::MacroItem(..) | clean::MacroItem(..)
| clean::ForeignTypeItem => { | clean::ForeignTypeItem => {
@ -80,6 +77,16 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
} }
} }
clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
let item_id = i.item_id;
if item_id.is_local()
&& !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id())
{
debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
return None;
}
}
clean::StructFieldItem(..) => { clean::StructFieldItem(..) => {
if i.visibility(self.tcx) != Some(Visibility::Public) { if i.visibility(self.tcx) != Some(Visibility::Public) {
return Some(strip_item(i)); return Some(strip_item(i));
@ -192,16 +199,16 @@ impl<'a> DocFolder for ImplStripper<'a, '_> {
&& imp.items.iter().all(|i| { && imp.items.iter().all(|i| {
let item_id = i.item_id; let item_id = i.item_id;
item_id.is_local() item_id.is_local()
&& !is_item_reachable( && !self
self.tcx, .cache
self.is_json_output, .effective_visibilities
&self.cache.effective_visibilities, .is_reachable(self.tcx, item_id.expect_def_id())
item_id,
)
}) })
{ {
debug!("ImplStripper: no public item; removing {imp:?}");
return None; return None;
} else if imp.items.is_empty() && i.doc_value().is_empty() { } else if imp.items.is_empty() && i.doc_value().is_empty() {
debug!("ImplStripper: no item and no doc; removing {imp:?}");
return None; return None;
} }
} }
@ -212,13 +219,13 @@ impl<'a> DocFolder for ImplStripper<'a, '_> {
&& !imp.for_.is_assoc_ty() && !imp.for_.is_assoc_ty()
&& !self.should_keep_impl(&i, did) && !self.should_keep_impl(&i, did)
{ {
debug!("ImplStripper: impl item for stripped type; removing"); debug!("ImplStripper: impl item for stripped type; removing {imp:?}");
return None; return None;
} }
if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id())
&& !self.should_keep_impl(&i, did) && !self.should_keep_impl(&i, did)
{ {
debug!("ImplStripper: impl item for stripped trait; removing"); debug!("ImplStripper: impl item for stripped trait; removing {imp:?}");
return None; return None;
} }
if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) { if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
@ -226,7 +233,7 @@ impl<'a> DocFolder for ImplStripper<'a, '_> {
if let Some(did) = typaram.def_id(self.cache) if let Some(did) = typaram.def_id(self.cache)
&& !self.should_keep_impl(&i, did) && !self.should_keep_impl(&i, did)
{ {
debug!("ImplStripper: stripped item in trait's generics; removing impl"); debug!("ImplStripper: stripped item in trait's generics; removing {imp:?}");
return None; return None;
} }
} }

View File

@ -451,12 +451,12 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
&& let Some(def_id) = trait_ref.trait_def_id() && let Some(def_id) = trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
&& let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
&& !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, adt.did(),&[]) && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[])
// If all of our fields implement `Eq`, we can implement `Eq` too // If all of our fields implement `Eq`, we can implement `Eq` too
&& adt && adt
.all_fields() .all_fields()
.map(|f| f.ty(cx.tcx, args)) .map(|f| f.ty(cx.tcx, args))
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, adt.did(), &[])) .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]))
{ {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View File

@ -118,7 +118,7 @@ fn is_ref_iterable<'tcx>(
.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder()) .liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
&& let param_env = cx.tcx.param_env(fn_id) && let param_env = cx.tcx.param_env(fn_id)
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, fn_id, &[]) && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, Some(fn_id), &[])
&& let Some(into_iter_ty) = && let Some(into_iter_ty) =
make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty]) make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
&& let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty) && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)

View File

@ -214,36 +214,21 @@ pub fn implements_trait<'tcx>(
trait_id: DefId, trait_id: DefId,
args: &[GenericArg<'tcx>], args: &[GenericArg<'tcx>],
) -> bool { ) -> bool {
let callee_id = cx implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, None, args.iter().map(|&x| Some(x)))
.enclosing_body
.map(|body| cx.tcx.hir().body_owner(body).owner.to_def_id());
implements_trait_with_env_from_iter(
cx.tcx,
cx.param_env,
ty,
trait_id,
callee_id,
args.iter().map(|&x| Some(x)),
)
} }
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
///
/// The `callee_id` argument is used to determine whether this is a function call in a `const fn` environment, used for checking const traits.
pub fn implements_trait_with_env<'tcx>( pub fn implements_trait_with_env<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>, param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
trait_id: DefId, trait_id: DefId,
callee_id: DefId, callee_id: Option<DefId>,
args: &[GenericArg<'tcx>], args: &[GenericArg<'tcx>],
) -> bool { ) -> bool {
implements_trait_with_env_from_iter( implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x)))
tcx,
param_env,
ty,
trait_id,
Some(callee_id),
args.iter().map(|&x| Some(x)),
)
} }
/// Same as `implements_trait_from_env` but takes the arguments as an iterator. /// Same as `implements_trait_from_env` but takes the arguments as an iterator.
@ -258,6 +243,13 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
// Clippy shouldn't have infer types // Clippy shouldn't have infer types
assert!(!ty.has_infer()); assert!(!ty.has_infer());
// If a `callee_id` is passed, then we assert that it is a body owner
// through calling `body_owner_kind`, which would panic if the callee
// does not have a body.
if let Some(callee_id) = callee_id {
let _ = tcx.hir().body_owner_kind(callee_id);
}
let ty = tcx.erase_regions(ty); let ty = tcx.erase_regions(ty);
if ty.has_escaping_bound_vars() { if ty.has_escaping_bound_vars() {
return false; return false;

View File

@ -48,6 +48,8 @@ impl Foo {
pub trait Pattern<'a> {} pub trait Pattern<'a> {}
impl Pattern<'_> for () {}
pub trait Trait<const N: usize> {} pub trait Trait<const N: usize> {}
// @has async_fn/fn.const_generics.html // @has async_fn/fn.const_generics.html
// @has - '//pre[@class="rust item-decl"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)' // @has - '//pre[@class="rust item-decl"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
@ -57,18 +59,18 @@ pub async fn const_generics<const N: usize>(_: impl Trait<N>) {}
// regression test for #63037 // regression test for #63037
// @has async_fn/fn.elided.html // @has async_fn/fn.elided.html
// @has - '//pre[@class="rust item-decl"]' 'pub async fn elided(foo: &str) -> &str' // @has - '//pre[@class="rust item-decl"]' 'pub async fn elided(foo: &str) -> &str'
pub async fn elided(foo: &str) -> &str {} pub async fn elided(foo: &str) -> &str { "" }
// This should really be shown as written, but for implementation reasons it's difficult. // This should really be shown as written, but for implementation reasons it's difficult.
// See `impl Clean for TyKind::Ref`. // See `impl Clean for TyKind::Ref`.
// @has async_fn/fn.user_elided.html // @has async_fn/fn.user_elided.html
// @has - '//pre[@class="rust item-decl"]' 'pub async fn user_elided(foo: &str) -> &str' // @has - '//pre[@class="rust item-decl"]' 'pub async fn user_elided(foo: &str) -> &str'
pub async fn user_elided(foo: &'_ str) -> &str {} pub async fn user_elided(foo: &'_ str) -> &str { "" }
// @has async_fn/fn.static_trait.html // @has async_fn/fn.static_trait.html
// @has - '//pre[@class="rust item-decl"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>' // @has - '//pre[@class="rust item-decl"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>'
pub async fn static_trait(foo: &str) -> Box<dyn Bar> {} pub async fn static_trait(foo: &str) -> Box<dyn Bar> { Box::new(()) }
// @has async_fn/fn.lifetime_for_trait.html // @has async_fn/fn.lifetime_for_trait.html
// @has - '//pre[@class="rust item-decl"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>" // @has - '//pre[@class="rust item-decl"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>"
pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_> {} pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_> { Box::new(()) }
// @has async_fn/fn.elided_in_input_trait.html // @has async_fn/fn.elided_in_input_trait.html
// @has - '//pre[@class="rust item-decl"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)" // @has - '//pre[@class="rust item-decl"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)"
pub async fn elided_in_input_trait(t: impl Pattern<'_>) {} pub async fn elided_in_input_trait(t: impl Pattern<'_>) {}
@ -78,10 +80,12 @@ struct AsyncFdReadyGuard<'a, T> { x: &'a T }
impl Foo { impl Foo {
// @has async_fn/struct.Foo.html // @has async_fn/struct.Foo.html
// @has - '//*[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar ) -> impl Iterator<Item = &usize>' // @has - '//*[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar ) -> impl Iterator<Item = &usize>'
pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator<Item = &usize> {} pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator<Item = &usize> {
[0].iter()
}
// taken from `tokio` as an example of a method that was particularly bad before // taken from `tokio` as an example of a method that was particularly bad before
// @has - '//*[@class="method"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>" // @has - '//*[@class="method"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>"
pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()> {} pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()> { Err(()) }
// @has - '//*[@class="method"]' "pub async fn mut_self(&mut self)" // @has - '//*[@class="method"]' "pub async fn mut_self(&mut self)"
pub async fn mut_self(&mut self) {} pub async fn mut_self(&mut self) {}
} }
@ -89,7 +93,7 @@ impl Foo {
// test named lifetimes, just in case // test named lifetimes, just in case
// @has async_fn/fn.named.html // @has async_fn/fn.named.html
// @has - '//pre[@class="rust item-decl"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str" // @has - '//pre[@class="rust item-decl"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str"
pub async fn named<'a, 'b>(foo: &'a str) -> &'b str {} pub async fn named<'a, 'b>(foo: &'a str) -> &'b str { "" }
// @has async_fn/fn.named_trait.html // @has async_fn/fn.named_trait.html
// @has - '//pre[@class="rust item-decl"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>" // @has - '//pre[@class="rust item-decl"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>"
pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {} pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {}

View File

@ -0,0 +1,27 @@
#![crate_name = "foo"]
// @has 'foo/index.html'
// There should be only `type A`.
// @count - '//*[@class="item-table"]//*[@class="item-name"]' 1
// @has - '//*[@class="item-name"]/a[@href="type.A.html"]' 'A'
mod foo {
pub struct S;
}
use foo::S;
pub type A = S;
// @has 'foo/type.A.html'
// @has - '//*[@id="method.default"]/h4' 'fn default() -> Self'
impl Default for A {
fn default() -> Self {
S
}
}
// @has - '//*[@id="method.a"]/h4' 'pub fn a(&self)'
impl A {
pub fn a(&self) {}
}