Auto merge of #121252 - fmease:rollup-x7zogl8, r=fmease
Rollup of 7 pull requests Successful merges: - #120526 (rustdoc: Correctly handle long crate names on mobile) - #121100 (Detect when method call on argument could be removed to fulfill failed trait bound) - #121160 (rustdoc: fix and refactor HTML rendering a bit) - #121198 (Add more checks for `unnamed_fields` during HIR analysis) - #121218 (Fix missing trait impls for type in rustc docs) - #121221 (AstConv: Refactor lowering of associated item bindings a bit) - #121237 (Use better heuristic for printing Cargo specific diagnostics) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
23a3d777c8
41 changed files with 678 additions and 498 deletions
|
@ -362,8 +362,11 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for LinkingFailed<'_> {
|
|||
// which by now we have no way to translate.
|
||||
if contains_undefined_ref {
|
||||
diag.note(fluent::codegen_ssa_extern_funcs_not_found)
|
||||
.note(fluent::codegen_ssa_specify_libraries_to_link)
|
||||
.note(fluent::codegen_ssa_use_cargo_directive);
|
||||
.note(fluent::codegen_ssa_specify_libraries_to_link);
|
||||
|
||||
if rustc_session::utils::was_invoked_from_cargo() {
|
||||
diag.note(fluent::codegen_ssa_use_cargo_directive);
|
||||
}
|
||||
}
|
||||
diag
|
||||
}
|
||||
|
|
|
@ -2998,6 +2998,12 @@ impl<'hir> Item<'hir> {
|
|||
ItemId { owner_id: self.owner_id }
|
||||
}
|
||||
|
||||
/// Check if this is an [`ItemKind::Enum`], [`ItemKind::Struct`] or
|
||||
/// [`ItemKind::Union`].
|
||||
pub fn is_adt(&self) -> bool {
|
||||
matches!(self.kind, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..))
|
||||
}
|
||||
|
||||
expect_methods_self_kind! {
|
||||
expect_extern_crate, Option<Symbol>, ItemKind::ExternCrate(s), *s;
|
||||
|
||||
|
|
|
@ -198,6 +198,8 @@ hir_analysis_invalid_union_field =
|
|||
hir_analysis_invalid_union_field_sugg =
|
||||
wrap the field type in `ManuallyDrop<...>`
|
||||
|
||||
hir_analysis_invalid_unnamed_field_ty = unnamed fields can only have struct or union types
|
||||
|
||||
hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
|
||||
.label = const parameter declared here
|
||||
|
||||
|
|
|
@ -9,9 +9,7 @@ use rustc_span::{ErrorGuaranteed, Span};
|
|||
use rustc_trait_selection::traits;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::astconv::{
|
||||
AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter,
|
||||
};
|
||||
use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
|
||||
use crate::bounds::Bounds;
|
||||
use crate::errors;
|
||||
|
||||
|
@ -238,7 +236,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
&self,
|
||||
hir_ref_id: hir::HirId,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
binding: &ConvertedBinding<'_, 'tcx>,
|
||||
binding: &hir::TypeBinding<'tcx>,
|
||||
bounds: &mut Bounds<'tcx>,
|
||||
speculative: bool,
|
||||
dup_bindings: &mut FxIndexMap<DefId, Span>,
|
||||
|
@ -263,21 +261,20 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
|
||||
let tcx = self.tcx();
|
||||
|
||||
let assoc_kind =
|
||||
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
|
||||
ty::AssocKind::Fn
|
||||
} else if let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
&& let ty::TermKind::Const(_) = term.node.unpack()
|
||||
{
|
||||
ty::AssocKind::Const
|
||||
} else {
|
||||
ty::AssocKind::Type
|
||||
};
|
||||
let assoc_kind = if binding.gen_args.parenthesized
|
||||
== hir::GenericArgsParentheses::ReturnTypeNotation
|
||||
{
|
||||
ty::AssocKind::Fn
|
||||
} else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind {
|
||||
ty::AssocKind::Const
|
||||
} else {
|
||||
ty::AssocKind::Type
|
||||
};
|
||||
|
||||
let candidate = if self.trait_defines_associated_item_named(
|
||||
trait_ref.def_id(),
|
||||
assoc_kind,
|
||||
binding.item_name,
|
||||
binding.ident,
|
||||
) {
|
||||
// Simple case: The assoc item is defined in the current trait.
|
||||
trait_ref
|
||||
|
@ -289,14 +286,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
trait_ref.skip_binder().print_only_trait_name(),
|
||||
None,
|
||||
assoc_kind,
|
||||
binding.item_name,
|
||||
binding.ident,
|
||||
path_span,
|
||||
Some(&binding),
|
||||
Some(binding),
|
||||
)?
|
||||
};
|
||||
|
||||
let (assoc_ident, def_scope) =
|
||||
tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
|
||||
tcx.adjust_ident_and_get_scope(binding.ident, candidate.def_id(), hir_ref_id);
|
||||
|
||||
// We have already adjusted the item name above, so compare with `.normalize_to_macros_2_0()`
|
||||
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||
|
@ -312,7 +309,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
.dcx()
|
||||
.struct_span_err(
|
||||
binding.span,
|
||||
format!("{} `{}` is private", assoc_item.kind, binding.item_name),
|
||||
format!("{} `{}` is private", assoc_item.kind, binding.ident),
|
||||
)
|
||||
.with_span_label(binding.span, format!("private {}", assoc_item.kind))
|
||||
.emit();
|
||||
|
@ -327,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
|
||||
span: binding.span,
|
||||
prev_span: *prev_span,
|
||||
item_name: binding.item_name,
|
||||
item_name: binding.ident,
|
||||
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
|
||||
});
|
||||
})
|
||||
|
@ -390,14 +387,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
{
|
||||
alias_ty
|
||||
} else {
|
||||
return Err(self.tcx().dcx().emit_err(
|
||||
crate::errors::ReturnTypeNotationOnNonRpitit {
|
||||
span: binding.span,
|
||||
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
|
||||
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
|
||||
note: (),
|
||||
},
|
||||
));
|
||||
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
|
||||
span: binding.span,
|
||||
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
|
||||
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
|
||||
note: (),
|
||||
}));
|
||||
};
|
||||
|
||||
// Finally, move the fn return type's bound vars over to account for the early bound
|
||||
|
@ -410,9 +405,11 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
let bound_vars = tcx.late_bound_vars(binding.hir_id);
|
||||
ty::Binder::bind_with_vars(instantiation_output, bound_vars)
|
||||
} else {
|
||||
// Append the generic arguments of the associated type to the `trait_ref`.
|
||||
// Create the generic arguments for the associated type or constant by joining the
|
||||
// parent arguments (the arguments of the trait) and the own arguments (the ones of
|
||||
// the associated item itself) and construct an alias type using them.
|
||||
candidate.map_bound(|trait_ref| {
|
||||
let ident = Ident::new(assoc_item.name, binding.item_name.span);
|
||||
let ident = Ident::new(assoc_item.name, binding.ident.span);
|
||||
let item_segment = hir::PathSegment {
|
||||
ident,
|
||||
hir_id: binding.hir_id,
|
||||
|
@ -421,77 +418,82 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
infer_args: false,
|
||||
};
|
||||
|
||||
let args_trait_ref_and_assoc_item = self.create_args_for_associated_item(
|
||||
let alias_args = self.create_args_for_associated_item(
|
||||
path_span,
|
||||
assoc_item.def_id,
|
||||
&item_segment,
|
||||
trait_ref.args,
|
||||
);
|
||||
debug!(?alias_args);
|
||||
|
||||
debug!(?args_trait_ref_and_assoc_item);
|
||||
|
||||
ty::AliasTy::new(tcx, assoc_item.def_id, args_trait_ref_and_assoc_item)
|
||||
// Note that we're indeed also using `AliasTy` (alias *type*) for associated
|
||||
// *constants* to represent *const projections*. Alias *term* would be a more
|
||||
// appropriate name but alas.
|
||||
ty::AliasTy::new(tcx, assoc_item.def_id, alias_args)
|
||||
})
|
||||
};
|
||||
|
||||
if !speculative {
|
||||
// Find any late-bound regions declared in `ty` that are not
|
||||
// declared in the trait-ref or assoc_item. These are not well-formed.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
|
||||
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
|
||||
if let ConvertedBindingKind::Equality(ty) = binding.kind {
|
||||
let late_bound_in_trait_ref =
|
||||
tcx.collect_constrained_late_bound_regions(&projection_ty);
|
||||
let late_bound_in_ty =
|
||||
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty.node));
|
||||
debug!(?late_bound_in_trait_ref);
|
||||
debug!(?late_bound_in_ty);
|
||||
|
||||
// FIXME: point at the type params that don't have appropriate lifetimes:
|
||||
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
|
||||
// ---- ---- ^^^^^^^
|
||||
self.validate_late_bound_regions(
|
||||
late_bound_in_trait_ref,
|
||||
late_bound_in_ty,
|
||||
|br_name| {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
binding.span,
|
||||
E0582,
|
||||
"binding for associated type `{}` references {}, \
|
||||
which does not appear in the trait input types",
|
||||
binding.item_name,
|
||||
br_name
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
|
||||
return Err(self.tcx().dcx().emit_err(
|
||||
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
|
||||
));
|
||||
hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => {
|
||||
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
|
||||
span: binding.span,
|
||||
}));
|
||||
}
|
||||
ConvertedBindingKind::Equality(term) => {
|
||||
hir::TypeBindingKind::Equality { term } => {
|
||||
let term = match term {
|
||||
hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(),
|
||||
hir::Term::Const(ct) => ty::Const::from_anon_const(tcx, ct.def_id).into(),
|
||||
};
|
||||
|
||||
if !speculative {
|
||||
// Find any late-bound regions declared in `ty` that are not
|
||||
// declared in the trait-ref or assoc_item. These are not well-formed.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
|
||||
// for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
|
||||
let late_bound_in_projection_ty =
|
||||
tcx.collect_constrained_late_bound_regions(&projection_ty);
|
||||
let late_bound_in_term =
|
||||
tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term));
|
||||
debug!(?late_bound_in_projection_ty);
|
||||
debug!(?late_bound_in_term);
|
||||
|
||||
// FIXME: point at the type params that don't have appropriate lifetimes:
|
||||
// struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
|
||||
// ---- ---- ^^^^^^^
|
||||
// NOTE(associated_const_equality): This error should be impossible to trigger
|
||||
// with associated const equality bounds.
|
||||
self.validate_late_bound_regions(
|
||||
late_bound_in_projection_ty,
|
||||
late_bound_in_term,
|
||||
|br_name| {
|
||||
struct_span_code_err!(
|
||||
tcx.dcx(),
|
||||
binding.span,
|
||||
E0582,
|
||||
"binding for associated type `{}` references {}, \
|
||||
which does not appear in the trait input types",
|
||||
binding.ident,
|
||||
br_name
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
|
||||
// the "projection predicate" for:
|
||||
//
|
||||
// `<T as Iterator>::Item = u32`
|
||||
bounds.push_projection_bound(
|
||||
tcx,
|
||||
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
|
||||
projection_ty,
|
||||
term: term.node,
|
||||
}),
|
||||
projection_ty
|
||||
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
|
||||
binding.span,
|
||||
);
|
||||
}
|
||||
ConvertedBindingKind::Constraint(ast_bounds) => {
|
||||
hir::TypeBindingKind::Constraint { bounds: ast_bounds } => {
|
||||
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
|
||||
//
|
||||
// `<T as Iterator>::Item: Debug`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::astconv::{AstConv, ConvertedBindingKind};
|
||||
use crate::astconv::AstConv;
|
||||
use crate::errors::{
|
||||
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
|
||||
ParenthesizedFnTraitExpansion,
|
||||
|
@ -111,7 +111,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
||||
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||
) -> ErrorGuaranteed
|
||||
where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
|
@ -243,7 +243,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
None,
|
||||
) && suggested_name != assoc_name.name
|
||||
{
|
||||
// We suggested constraining a type parameter, but the associated type on it
|
||||
// We suggested constraining a type parameter, but the associated item on it
|
||||
// was also not an exact match, so we also suggest changing it.
|
||||
err.span_suggestion_verbose(
|
||||
assoc_name.span,
|
||||
|
@ -258,16 +258,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
}
|
||||
}
|
||||
|
||||
// If we still couldn't find any associated type, and only one associated type exists,
|
||||
// If we still couldn't find any associated item, and only one associated item exists,
|
||||
// suggests using it.
|
||||
if let [candidate_name] = all_candidate_names.as_slice() {
|
||||
// this should still compile, except on `#![feature(associated_type_defaults)]`
|
||||
// where it could suggests `type A = Self::A`, thus recursing infinitely
|
||||
let applicability = if tcx.features().associated_type_defaults {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
// This should still compile, except on `#![feature(associated_type_defaults)]`
|
||||
// where it could suggests `type A = Self::A`, thus recursing infinitely.
|
||||
let applicability =
|
||||
if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
|
||||
Applicability::Unspecified
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
|
||||
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
|
||||
span: assoc_name.span,
|
||||
|
@ -289,13 +290,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
assoc_kind: ty::AssocKind,
|
||||
ident: Ident,
|
||||
span: Span,
|
||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
||||
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||
) -> ErrorGuaranteed {
|
||||
let tcx = self.tcx();
|
||||
|
||||
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
|
||||
&& let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Constraint(_) = binding.kind
|
||||
&& let hir::TypeBindingKind::Constraint { .. } = binding.kind
|
||||
{
|
||||
let lo = if binding.gen_args.span_ext.is_dummy() {
|
||||
ident.span
|
||||
|
@ -309,25 +310,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
|
||||
// FIXME(associated_const_equality): This has quite a few false positives and negatives.
|
||||
let wrap_in_braces_sugg = if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
&& let ty::TermKind::Ty(ty) = term.node.unpack()
|
||||
&& let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind
|
||||
&& let ty = self.ast_ty_to_ty(hir_ty)
|
||||
&& (ty.is_enum() || ty.references_error())
|
||||
&& tcx.features().associated_const_equality
|
||||
{
|
||||
Some(errors::AssocKindMismatchWrapInBracesSugg {
|
||||
lo: term.span.shrink_to_lo(),
|
||||
hi: term.span.shrink_to_hi(),
|
||||
lo: hir_ty.span.shrink_to_lo(),
|
||||
hi: hir_ty.span.shrink_to_hi(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since
|
||||
// one can argue that that's more “untuitive” to the user.
|
||||
// one can argue that that's more “intuitive” to the user.
|
||||
let (span, expected_because_label, expected, got) = if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(term) = binding.kind
|
||||
&& let hir::TypeBindingKind::Equality { term } = binding.kind
|
||||
{
|
||||
(term.span, Some(ident.span), assoc_item.kind, assoc_kind)
|
||||
let span = match term {
|
||||
hir::Term::Ty(ty) => ty.span,
|
||||
hir::Term::Const(ct) => tcx.def_span(ct.def_id),
|
||||
};
|
||||
(span, Some(ident.span), assoc_item.kind, assoc_kind)
|
||||
} else {
|
||||
(ident.span, None, assoc_kind, assoc_item.kind)
|
||||
};
|
||||
|
|
|
@ -35,7 +35,6 @@ use rustc_middle::ty::{
|
|||
};
|
||||
use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::source_map::{respan, Spanned};
|
||||
use rustc_span::symbol::{kw, Ident, Symbol};
|
||||
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
|
||||
use rustc_target::spec::abi;
|
||||
|
@ -151,21 +150,6 @@ pub trait AstConv<'tcx> {
|
|||
fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ConvertedBinding<'a, 'tcx> {
|
||||
hir_id: hir::HirId,
|
||||
item_name: Ident,
|
||||
kind: ConvertedBindingKind<'a, 'tcx>,
|
||||
gen_args: &'tcx GenericArgs<'tcx>,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ConvertedBindingKind<'a, 'tcx> {
|
||||
Equality(Spanned<ty::Term<'tcx>>),
|
||||
Constraint(&'a [hir::GenericBound<'tcx>]),
|
||||
}
|
||||
|
||||
/// New-typed boolean indicating whether explicit late-bound lifetimes
|
||||
/// are present in a set of generic arguments.
|
||||
///
|
||||
|
@ -316,7 +300,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
/// Given the type/lifetime/const arguments provided to some path (along with
|
||||
/// an implicit `Self`, if this is a trait reference), returns the complete
|
||||
/// set of generic arguments. This may involve applying defaulted type parameters.
|
||||
/// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`.
|
||||
///
|
||||
/// Constraints on associated types are not converted here but
|
||||
/// separately in `add_predicates_for_ast_type_binding`.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
|
@ -329,8 +315,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
/// 2. The path in question is the path to the trait `std::ops::Index`,
|
||||
/// which will have been resolved to a `def_id`
|
||||
/// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
|
||||
/// parameters are returned in the `GenericArgsRef`, the associated type bindings like
|
||||
/// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`.
|
||||
/// parameters are returned in the `GenericArgsRef`
|
||||
/// 4. Associated type bindings like `Output = u32` are contained in `generic_args.bindings`.
|
||||
///
|
||||
/// Note that the type listing given here is *exactly* what the user provided.
|
||||
///
|
||||
|
@ -591,52 +577,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
(args, arg_count)
|
||||
}
|
||||
|
||||
fn create_assoc_bindings_for_generic_args<'a>(
|
||||
&self,
|
||||
generic_args: &'a hir::GenericArgs<'tcx>,
|
||||
) -> Vec<ConvertedBinding<'a, 'tcx>> {
|
||||
// Convert associated-type bindings or constraints into a separate vector.
|
||||
// Example: Given this:
|
||||
//
|
||||
// T: Iterator<Item = u32>
|
||||
//
|
||||
// The `T` is passed in as a self-type; the `Item = u32` is
|
||||
// not a "type parameter" of the `Iterator` trait, but rather
|
||||
// a restriction on `<T as Iterator>::Item`, so it is passed
|
||||
// back separately.
|
||||
let assoc_bindings = generic_args
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|binding| {
|
||||
let kind = match &binding.kind {
|
||||
hir::TypeBindingKind::Equality { term } => match term {
|
||||
hir::Term::Ty(ty) => ConvertedBindingKind::Equality(respan(
|
||||
ty.span,
|
||||
self.ast_ty_to_ty(ty).into(),
|
||||
)),
|
||||
hir::Term::Const(c) => {
|
||||
let span = self.tcx().def_span(c.def_id);
|
||||
let c = Const::from_anon_const(self.tcx(), c.def_id);
|
||||
ConvertedBindingKind::Equality(respan(span, c.into()))
|
||||
}
|
||||
},
|
||||
hir::TypeBindingKind::Constraint { bounds } => {
|
||||
ConvertedBindingKind::Constraint(bounds)
|
||||
}
|
||||
};
|
||||
ConvertedBinding {
|
||||
hir_id: binding.hir_id,
|
||||
item_name: binding.ident,
|
||||
kind,
|
||||
gen_args: binding.gen_args,
|
||||
span: binding.span,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
assoc_bindings
|
||||
}
|
||||
|
||||
pub fn create_args_for_associated_item(
|
||||
&self,
|
||||
span: Span,
|
||||
|
@ -742,18 +682,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id);
|
||||
debug!(?bound_vars);
|
||||
|
||||
let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
|
||||
|
||||
let poly_trait_ref = ty::Binder::bind_with_vars(
|
||||
ty::TraitRef::new(tcx, trait_def_id, generic_args),
|
||||
bound_vars,
|
||||
);
|
||||
|
||||
debug!(?poly_trait_ref, ?assoc_bindings);
|
||||
debug!(?poly_trait_ref);
|
||||
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
|
||||
|
||||
let mut dup_bindings = FxIndexMap::default();
|
||||
for binding in &assoc_bindings {
|
||||
for binding in args.bindings {
|
||||
// Don't register additional associated type bounds for negative bounds,
|
||||
// since we should have emitten an error for them earlier, and they will
|
||||
// not be well-formed!
|
||||
|
@ -1029,7 +967,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
assoc_kind: ty::AssocKind,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
binding: Option<&ConvertedBinding<'_, 'tcx>>,
|
||||
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
|
||||
where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
|
@ -1069,7 +1007,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
// Provide a more specific error code index entry for equality bindings.
|
||||
err.code(
|
||||
if let Some(binding) = binding
|
||||
&& let ConvertedBindingKind::Equality(_) = binding.kind
|
||||
&& let hir::TypeBindingKind::Equality { .. } = binding.kind
|
||||
{
|
||||
E0222
|
||||
} else {
|
||||
|
@ -1094,16 +1032,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
);
|
||||
if let Some(binding) = binding {
|
||||
match binding.kind {
|
||||
ConvertedBindingKind::Equality(term) => {
|
||||
hir::TypeBindingKind::Equality { term } => {
|
||||
let term: ty::Term<'_> = match term {
|
||||
hir::Term::Ty(ty) => self.ast_ty_to_ty(ty).into(),
|
||||
hir::Term::Const(ct) => {
|
||||
ty::Const::from_anon_const(tcx, ct.def_id).into()
|
||||
}
|
||||
};
|
||||
// FIXME(#97583): This isn't syntactically well-formed!
|
||||
where_bounds.push(format!(
|
||||
" T: {trait}::{assoc_name} = {term}",
|
||||
trait = bound.print_only_trait_path(),
|
||||
term = term.node,
|
||||
));
|
||||
}
|
||||
// FIXME: Provide a suggestion.
|
||||
ConvertedBindingKind::Constraint(_bounds) => {}
|
||||
hir::TypeBindingKind::Constraint { bounds: _ } => {}
|
||||
}
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
|
|
|
@ -129,17 +129,20 @@ fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
|
|||
for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
|
||||
let field_ty = tcx.type_of(field.did).instantiate_identity();
|
||||
if let Some(adt) = field_ty.ty_adt_def()
|
||||
&& !adt.is_anonymous()
|
||||
&& !adt.repr().c()
|
||||
&& !adt.is_enum()
|
||||
{
|
||||
let field_ty_span = tcx.def_span(adt.did());
|
||||
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
|
||||
span: tcx.def_span(field.did),
|
||||
field_ty_span,
|
||||
field_ty,
|
||||
field_adt_kind: adt.descr(),
|
||||
sugg_span: field_ty_span.shrink_to_lo(),
|
||||
});
|
||||
if !adt.is_anonymous() && !adt.repr().c() {
|
||||
let field_ty_span = tcx.def_span(adt.did());
|
||||
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
|
||||
span: tcx.def_span(field.did),
|
||||
field_ty_span,
|
||||
field_ty,
|
||||
field_adt_kind: adt.descr(),
|
||||
sugg_span: field_ty_span.shrink_to_lo(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
tcx.dcx().emit_err(errors::InvalidUnnamedFieldTy { span: tcx.def_span(field.did) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -943,7 +943,15 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> {
|
|||
}
|
||||
}
|
||||
hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => {
|
||||
self.check_field_in_nested_adt(self.tcx.adt_def(res.def_id()), field.span);
|
||||
// If this is a direct path to an ADT, we can check it
|
||||
// If this is a type alias or non-ADT, `check_unnamed_fields` should verify it
|
||||
if let Some(def_id) = res.opt_def_id()
|
||||
&& let Some(local) = def_id.as_local()
|
||||
&& let Node::Item(item) = self.tcx.hir_node_by_def_id(local)
|
||||
&& item.is_adt()
|
||||
{
|
||||
self.check_field_in_nested_adt(self.tcx.adt_def(def_id), field.span);
|
||||
}
|
||||
}
|
||||
// Abort due to errors (there must be an error if an unnamed field
|
||||
// has any type kind other than an anonymous adt or a named adt)
|
||||
|
|
|
@ -661,6 +661,13 @@ pub(crate) struct InvalidUnionField {
|
|||
pub note: (),
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_invalid_unnamed_field_ty)]
|
||||
pub struct InvalidUnnamedFieldTy {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_return_type_notation_on_non_rpitit)]
|
||||
pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
|
||||
|
|
|
@ -293,7 +293,7 @@ pub enum HelpUseLatestEdition {
|
|||
impl HelpUseLatestEdition {
|
||||
pub fn new() -> Self {
|
||||
let edition = LATEST_STABLE_EDITION;
|
||||
if std::env::var_os("CARGO").is_some() {
|
||||
if rustc_session::utils::was_invoked_from_cargo() {
|
||||
Self::Cargo { edition }
|
||||
} else {
|
||||
Self::Standalone { edition }
|
||||
|
|
|
@ -492,7 +492,7 @@ fn lock_directory(
|
|||
lock_err,
|
||||
session_dir,
|
||||
is_unsupported_lock,
|
||||
is_cargo: std::env::var_os("CARGO").map(|_| ()),
|
||||
is_cargo: rustc_session::utils::was_invoked_from_cargo().then_some(()),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,7 +205,7 @@ pub(super) fn builtin(
|
|||
Vec::new()
|
||||
};
|
||||
|
||||
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
||||
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
|
||||
let mut is_feature_cfg = name == sym::feature;
|
||||
|
||||
if is_feature_cfg && is_from_cargo {
|
||||
|
@ -340,7 +340,7 @@ pub(super) fn builtin(
|
|||
.copied()
|
||||
.flatten()
|
||||
.collect();
|
||||
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
||||
let is_from_cargo = rustc_session::utils::was_invoked_from_cargo();
|
||||
|
||||
// Show the full list if all possible values for a given name, but don't do it
|
||||
// for names as the possibilities could be very long
|
||||
|
|
|
@ -2545,7 +2545,7 @@ pub enum HelpUseLatestEdition {
|
|||
impl HelpUseLatestEdition {
|
||||
pub fn new() -> Self {
|
||||
let edition = LATEST_STABLE_EDITION;
|
||||
if std::env::var_os("CARGO").is_some() {
|
||||
if rustc_session::utils::was_invoked_from_cargo() {
|
||||
Self::Cargo { edition }
|
||||
} else {
|
||||
Self::Standalone { edition }
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
use crate::session::Session;
|
||||
use rustc_data_structures::profiling::VerboseTimingGuard;
|
||||
use rustc_fs_util::try_canonicalize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
impl Session {
|
||||
pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> {
|
||||
|
@ -158,3 +161,18 @@ pub fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
|
|||
|
||||
if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
|
||||
}
|
||||
|
||||
/// Returns whenever rustc was launched by Cargo as opposed to another build system.
|
||||
///
|
||||
/// To be used in diagnostics to avoid printing Cargo specific suggestions to other
|
||||
/// build systems (like Bazel, Buck2, Makefile, ...).
|
||||
pub fn was_invoked_from_cargo() -> bool {
|
||||
static FROM_CARGO: OnceLock<bool> = OnceLock::new();
|
||||
|
||||
// To be able to detect Cargo, we use the simplest and least intrusive
|
||||
// way: we check whenever the `CARGO_CRATE_NAME` env is set.
|
||||
//
|
||||
// Note that it is common in Makefiles to define the `CARGO` env even
|
||||
// though we may not have been called by Cargo, so we avoid using it.
|
||||
*FROM_CARGO.get_or_init(|| std::env::var_os("CARGO_CRATE_NAME").is_some())
|
||||
}
|
||||
|
|
|
@ -3689,6 +3689,24 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
if let Node::Expr(expr) = tcx.hir_node(arg_hir_id)
|
||||
&& let Some(typeck_results) = &self.typeck_results
|
||||
{
|
||||
if let hir::Expr { kind: hir::ExprKind::MethodCall(_, rcvr, _, _), .. } = expr
|
||||
&& let Some(ty) = typeck_results.node_type_opt(rcvr.hir_id)
|
||||
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
|
||||
&& let pred = failed_pred.map_bound(|pred| pred.with_self_ty(tcx, ty))
|
||||
&& self.predicate_must_hold_modulo_regions(&Obligation::misc(
|
||||
tcx, expr.span, body_id, param_env, pred,
|
||||
))
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.with_lo(rcvr.span.hi()),
|
||||
format!(
|
||||
"consider removing this method call, as the receiver has type `{ty}` and \
|
||||
`{pred}` trivially holds",
|
||||
),
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
|
||||
let inner_expr = expr.peel_blocks();
|
||||
let ty = typeck_results
|
||||
|
@ -3824,7 +3842,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Node::Expr(expr) = tcx.hir_node(call_hir_id) {
|
||||
if let Node::Expr(expr) = call_node {
|
||||
if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
|
||||
| hir::ExprKind::MethodCall(
|
||||
hir::PathSegment { ident: Ident { span, .. }, .. },
|
||||
|
|
|
@ -443,11 +443,13 @@ pub(crate) fn build_impl(
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(stab) = tcx.lookup_stability(did)
|
||||
&& stab.is_unstable()
|
||||
&& stab.feature == sym::rustc_private
|
||||
{
|
||||
return;
|
||||
if !tcx.features().rustc_private && !cx.render_options.force_unstable_if_unmarked {
|
||||
if let Some(stab) = tcx.lookup_stability(did)
|
||||
&& stab.is_unstable()
|
||||
&& stab.feature == sym::rustc_private
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,8 +479,11 @@ pub(crate) fn build_impl(
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(stab) = tcx.lookup_stability(did) {
|
||||
if stab.is_unstable() && stab.feature == sym::rustc_private {
|
||||
if !tcx.features().rustc_private && !cx.render_options.force_unstable_if_unmarked {
|
||||
if let Some(stab) = tcx.lookup_stability(did)
|
||||
&& stab.is_unstable()
|
||||
&& stab.feature == sym::rustc_private
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -281,6 +281,8 @@ pub(crate) struct RenderOptions {
|
|||
pub(crate) no_emit_shared: bool,
|
||||
/// If `true`, HTML source code pages won't be generated.
|
||||
pub(crate) html_no_source: bool,
|
||||
/// Whether `-Zforce-unstable-if-unmarked` unstable option is set
|
||||
pub(crate) force_unstable_if_unmarked: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
|
@ -347,6 +349,7 @@ impl Options {
|
|||
|
||||
let codegen_options = CodegenOptions::build(early_dcx, matches);
|
||||
let unstable_opts = UnstableOptions::build(early_dcx, matches);
|
||||
let force_unstable_if_unmarked = unstable_opts.force_unstable_if_unmarked;
|
||||
|
||||
let dcx = new_dcx(error_format, None, diagnostic_width, &unstable_opts);
|
||||
|
||||
|
@ -760,6 +763,7 @@ impl Options {
|
|||
call_locations,
|
||||
no_emit_shared: false,
|
||||
html_no_source,
|
||||
force_unstable_if_unmarked,
|
||||
};
|
||||
Some((options, render_options))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! HTML formatting module
|
||||
//!
|
||||
//! This module contains a large number of `fmt::Display` implementations for
|
||||
//! This module contains a large number of `Display` implementations for
|
||||
//! various types in `rustdoc::clean`.
|
||||
//!
|
||||
//! These implementations all emit HTML. As an internal implementation detail,
|
||||
|
@ -9,7 +9,7 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::fmt::{self, Write};
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::iter::{self, once};
|
||||
|
||||
use rustc_ast as ast;
|
||||
|
@ -150,16 +150,16 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn comma_sep<T: fmt::Display>(
|
||||
pub(crate) fn comma_sep<T: Display>(
|
||||
items: impl Iterator<Item = T>,
|
||||
space_after_comma: bool,
|
||||
) -> impl fmt::Display {
|
||||
) -> impl Display {
|
||||
display_fn(move |f| {
|
||||
for (i, item) in items.enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
|
||||
}
|
||||
fmt::Display::fmt(&item, f)?;
|
||||
item.fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -168,7 +168,7 @@ pub(crate) fn comma_sep<T: fmt::Display>(
|
|||
pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
|
||||
bounds: &'a [clean::GenericBound],
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
let mut bounds_dup = FxHashSet::default();
|
||||
|
||||
|
@ -176,7 +176,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
|
|||
if i > 0 {
|
||||
f.write_str(" + ")?;
|
||||
}
|
||||
fmt::Display::fmt(&bound.print(cx), f)?;
|
||||
bound.print(cx).fmt(f)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -186,7 +186,7 @@ impl clean::GenericParamDef {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match &self.kind {
|
||||
clean::GenericParamDefKind::Lifetime { outlives } => {
|
||||
write!(f, "{}", self.name)?;
|
||||
|
@ -207,35 +207,27 @@ impl clean::GenericParamDef {
|
|||
f.write_str(self.name.as_str())?;
|
||||
|
||||
if !bounds.is_empty() {
|
||||
if f.alternate() {
|
||||
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
|
||||
} else {
|
||||
write!(f, ": {}", print_generic_bounds(bounds, cx))?;
|
||||
}
|
||||
f.write_str(": ")?;
|
||||
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||
}
|
||||
|
||||
if let Some(ref ty) = default {
|
||||
if f.alternate() {
|
||||
write!(f, " = {:#}", ty.print(cx))?;
|
||||
} else {
|
||||
write!(f, " = {}", ty.print(cx))?;
|
||||
}
|
||||
f.write_str(" = ")?;
|
||||
ty.print(cx).fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
clean::GenericParamDefKind::Const { ty, default, .. } => {
|
||||
if f.alternate() {
|
||||
write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
|
||||
} else {
|
||||
write!(f, "const {}: {}", self.name, ty.print(cx))?;
|
||||
}
|
||||
write!(f, "const {}: ", self.name)?;
|
||||
ty.print(cx).fmt(f)?;
|
||||
|
||||
if let Some(default) = default {
|
||||
f.write_str(" = ")?;
|
||||
if f.alternate() {
|
||||
write!(f, " = {default:#}")?;
|
||||
write!(f, "{default}")?;
|
||||
} else {
|
||||
write!(f, " = {default}")?;
|
||||
write!(f, "{}", Escape(default))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +241,7 @@ impl clean::Generics {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
|
||||
if real_params.peek().is_none() {
|
||||
|
@ -279,63 +271,50 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
|
|||
cx: &'a Context<'tcx>,
|
||||
indent: usize,
|
||||
ending: Ending,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
|
||||
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
|
||||
}).map(|pred| {
|
||||
display_fn(move |f| {
|
||||
if f.alternate() {
|
||||
f.write_str(" ")?;
|
||||
} else {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
let mut where_predicates = gens
|
||||
.where_predicates
|
||||
.iter()
|
||||
.map(|pred| {
|
||||
display_fn(move |f| {
|
||||
if f.alternate() {
|
||||
f.write_str(" ")?;
|
||||
} else {
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
|
||||
match pred {
|
||||
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
|
||||
let ty_cx = ty.print(cx);
|
||||
let generic_bounds = print_generic_bounds(bounds, cx);
|
||||
|
||||
if bound_params.is_empty() {
|
||||
if f.alternate() {
|
||||
write!(f, "{ty_cx:#}: {generic_bounds:#}")
|
||||
} else {
|
||||
write!(f, "{ty_cx}: {generic_bounds}")
|
||||
match pred {
|
||||
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
|
||||
print_higher_ranked_params_with_space(bound_params, cx).fmt(f)?;
|
||||
ty.print(cx).fmt(f)?;
|
||||
f.write_str(":")?;
|
||||
if !bounds.is_empty() {
|
||||
f.write_str(" ")?;
|
||||
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
|
||||
// We don't need to check `alternate` since we can be certain that neither
|
||||
// the lifetime nor the bounds contain any characters which need escaping.
|
||||
write!(f, "{}:", lifetime.print())?;
|
||||
if !bounds.is_empty() {
|
||||
write!(f, " {}", print_generic_bounds(bounds, cx))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
clean::WherePredicate::EqPredicate { lhs, rhs } => {
|
||||
if f.alternate() {
|
||||
write!(
|
||||
f,
|
||||
"for<{:#}> {ty_cx:#}: {generic_bounds:#}",
|
||||
comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
|
||||
)
|
||||
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"for<{}> {ty_cx}: {generic_bounds}",
|
||||
comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
|
||||
)
|
||||
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
|
||||
}
|
||||
}
|
||||
}
|
||||
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
|
||||
let mut bounds_display = String::new();
|
||||
for bound in bounds.iter().map(|b| b.print(cx)) {
|
||||
write!(bounds_display, "{bound} + ")?;
|
||||
}
|
||||
bounds_display.truncate(bounds_display.len() - " + ".len());
|
||||
write!(f, "{}: {bounds_display}", lifetime.print())
|
||||
}
|
||||
clean::WherePredicate::EqPredicate { lhs, rhs } => {
|
||||
if f.alternate() {
|
||||
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
|
||||
} else {
|
||||
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}).peekable();
|
||||
.peekable();
|
||||
|
||||
if where_predicates.peek().is_none() {
|
||||
return Ok(());
|
||||
|
@ -392,13 +371,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
|
|||
}
|
||||
|
||||
impl clean::Lifetime {
|
||||
pub(crate) fn print(&self) -> impl fmt::Display + '_ {
|
||||
pub(crate) fn print(&self) -> impl Display + '_ {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl clean::Constant {
|
||||
pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl fmt::Display + '_ {
|
||||
pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display + '_ {
|
||||
let expr = self.expr(tcx);
|
||||
display_fn(
|
||||
move |f| {
|
||||
|
@ -409,31 +388,10 @@ impl clean::Constant {
|
|||
}
|
||||
|
||||
impl clean::PolyTrait {
|
||||
fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
if !self.generic_params.is_empty() {
|
||||
if f.alternate() {
|
||||
write!(
|
||||
f,
|
||||
"for<{:#}> ",
|
||||
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
|
||||
)?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"for<{}> ",
|
||||
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", self.trait_.print(cx))
|
||||
} else {
|
||||
write!(f, "{}", self.trait_.print(cx))
|
||||
}
|
||||
print_higher_ranked_params_with_space(&self.generic_params, cx).fmt(f)?;
|
||||
self.trait_.print(cx).fmt(f)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -442,32 +400,25 @@ impl clean::GenericBound {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self {
|
||||
clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
|
||||
clean::GenericBound::TraitBound(ty, modifier) => {
|
||||
let modifier_str = match modifier {
|
||||
f.write_str(match modifier {
|
||||
hir::TraitBoundModifier::None => "",
|
||||
hir::TraitBoundModifier::Maybe => "?",
|
||||
hir::TraitBoundModifier::Negative => "!",
|
||||
// `const` and `~const` trait bounds are experimental; don't render them.
|
||||
hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "",
|
||||
};
|
||||
if f.alternate() {
|
||||
write!(f, "{modifier_str}{ty:#}", ty = ty.print(cx))
|
||||
} else {
|
||||
write!(f, "{modifier_str}{ty}", ty = ty.print(cx))
|
||||
}
|
||||
})?;
|
||||
ty.print(cx).fmt(f)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl clean::GenericArgs {
|
||||
fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
match self {
|
||||
clean::GenericArgs::AngleBracketed { args, bindings } => {
|
||||
|
@ -515,11 +466,7 @@ impl clean::GenericArgs {
|
|||
f.write_str(", ")?;
|
||||
}
|
||||
comma = true;
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", ty.print(cx))?;
|
||||
} else {
|
||||
write!(f, "{}", ty.print(cx))?;
|
||||
}
|
||||
ty.print(cx).fmt(f)?;
|
||||
}
|
||||
f.write_str(")")?;
|
||||
if let Some(ref ty) = *output {
|
||||
|
@ -973,31 +920,43 @@ fn primitive_link_fragment(
|
|||
None => {}
|
||||
}
|
||||
}
|
||||
std::fmt::Display::fmt(&name, f)?;
|
||||
Display::fmt(&name, f)?;
|
||||
if needs_termination {
|
||||
write!(f, "</a>")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper to render type parameters
|
||||
fn tybounds<'a, 'tcx: 'a>(
|
||||
bounds: &'a [clean::PolyTrait],
|
||||
lt: &'a Option<clean::Lifetime>,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
for (i, bound) in bounds.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, " + ")?;
|
||||
}
|
||||
|
||||
fmt::Display::fmt(&bound.print(cx), f)?;
|
||||
bound.print(cx).fmt(f)?;
|
||||
}
|
||||
|
||||
if let Some(lt) = lt {
|
||||
write!(f, " + ")?;
|
||||
fmt::Display::fmt(<.print(), f)?;
|
||||
// We don't need to check `alternate` since we can be certain that
|
||||
// the lifetime doesn't contain any characters which need escaping.
|
||||
write!(f, " + {}", lt.print())?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn print_higher_ranked_params_with_space<'a, 'tcx: 'a>(
|
||||
params: &'a [clean::GenericParamDef],
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
if !params.is_empty() {
|
||||
f.write_str(if f.alternate() { "for<" } else { "for<" })?;
|
||||
comma_sep(params.iter().map(|lt| lt.print(cx)), true).fmt(f)?;
|
||||
f.write_str(if f.alternate() { "> " } else { "> " })?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -1007,7 +966,7 @@ pub(crate) fn anchor<'a, 'cx: 'a>(
|
|||
did: DefId,
|
||||
text: Symbol,
|
||||
cx: &'cx Context<'_>,
|
||||
) -> impl fmt::Display + 'a {
|
||||
) -> impl Display + 'a {
|
||||
let parts = href(did, cx);
|
||||
display_fn(move |f| {
|
||||
if let Ok((url, short_ty, fqp)) = parts {
|
||||
|
@ -1039,7 +998,7 @@ fn fmt_type<'cx>(
|
|||
}
|
||||
clean::DynTrait(ref bounds, ref lt) => {
|
||||
f.write_str("dyn ")?;
|
||||
fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
|
||||
tybounds(bounds, lt, cx).fmt(f)
|
||||
}
|
||||
clean::Infer => write!(f, "_"),
|
||||
clean::Primitive(clean::PrimitiveType::Never) => {
|
||||
|
@ -1049,80 +1008,62 @@ fn fmt_type<'cx>(
|
|||
primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
|
||||
}
|
||||
clean::BareFunction(ref decl) => {
|
||||
print_higher_ranked_params_with_space(&decl.generic_params, cx).fmt(f)?;
|
||||
decl.unsafety.print_with_space().fmt(f)?;
|
||||
print_abi_with_space(decl.abi).fmt(f)?;
|
||||
if f.alternate() {
|
||||
write!(
|
||||
f,
|
||||
"{:#}{}{:#}fn{:#}",
|
||||
decl.print_hrtb_with_space(cx),
|
||||
decl.unsafety.print_with_space(),
|
||||
print_abi_with_space(decl.abi),
|
||||
decl.decl.print(cx),
|
||||
)
|
||||
f.write_str("fn")?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{}{}{}",
|
||||
decl.print_hrtb_with_space(cx),
|
||||
decl.unsafety.print_with_space(),
|
||||
print_abi_with_space(decl.abi)
|
||||
)?;
|
||||
primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?;
|
||||
write!(f, "{}", decl.decl.print(cx))
|
||||
}
|
||||
decl.decl.print(cx).fmt(f)
|
||||
}
|
||||
clean::Tuple(ref typs) => {
|
||||
match &typs[..] {
|
||||
&[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
|
||||
[one] => {
|
||||
if let clean::Generic(name) = one {
|
||||
primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
|
||||
} else {
|
||||
write!(f, "(")?;
|
||||
// Carry `f.alternate()` into this display w/o branching manually.
|
||||
fmt::Display::fmt(&one.print(cx), f)?;
|
||||
write!(f, ",)")
|
||||
}
|
||||
clean::Tuple(ref typs) => match &typs[..] {
|
||||
&[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
|
||||
[one] => {
|
||||
if let clean::Generic(name) = one {
|
||||
primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
|
||||
} else {
|
||||
write!(f, "(")?;
|
||||
one.print(cx).fmt(f)?;
|
||||
write!(f, ",)")
|
||||
}
|
||||
many => {
|
||||
let generic_names: Vec<Symbol> = many
|
||||
.iter()
|
||||
.filter_map(|t| match t {
|
||||
clean::Generic(name) => Some(*name),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let is_generic = generic_names.len() == many.len();
|
||||
if is_generic {
|
||||
primitive_link(
|
||||
f,
|
||||
PrimitiveType::Tuple,
|
||||
format_args!(
|
||||
"({})",
|
||||
generic_names.iter().map(|s| s.as_str()).join(", ")
|
||||
),
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
write!(f, "(")?;
|
||||
for (i, item) in many.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
// Carry `f.alternate()` into this display w/o branching manually.
|
||||
fmt::Display::fmt(&item.print(cx), f)?;
|
||||
}
|
||||
many => {
|
||||
let generic_names: Vec<Symbol> = many
|
||||
.iter()
|
||||
.filter_map(|t| match t {
|
||||
clean::Generic(name) => Some(*name),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let is_generic = generic_names.len() == many.len();
|
||||
if is_generic {
|
||||
primitive_link(
|
||||
f,
|
||||
PrimitiveType::Tuple,
|
||||
format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
write!(f, "(")?;
|
||||
for (i, item) in many.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, ")")
|
||||
item.print(cx).fmt(f)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
clean::Slice(ref t) => match **t {
|
||||
clean::Generic(name) => {
|
||||
primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
|
||||
}
|
||||
_ => {
|
||||
write!(f, "[")?;
|
||||
fmt::Display::fmt(&t.print(cx), f)?;
|
||||
t.print(cx).fmt(f)?;
|
||||
write!(f, "]")
|
||||
}
|
||||
},
|
||||
|
@ -1135,7 +1076,7 @@ fn fmt_type<'cx>(
|
|||
),
|
||||
_ => {
|
||||
write!(f, "[")?;
|
||||
fmt::Display::fmt(&t.print(cx), f)?;
|
||||
t.print(cx).fmt(f)?;
|
||||
if f.alternate() {
|
||||
write!(f, "; {n}")?;
|
||||
} else {
|
||||
|
@ -1175,7 +1116,7 @@ fn fmt_type<'cx>(
|
|||
}
|
||||
} else {
|
||||
primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
|
||||
fmt::Display::fmt(&t.print(cx), f)
|
||||
t.print(cx).fmt(f)
|
||||
}
|
||||
}
|
||||
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
|
||||
|
@ -1216,11 +1157,8 @@ fn fmt_type<'cx>(
|
|||
Ok(())
|
||||
}
|
||||
clean::ImplTrait(ref bounds) => {
|
||||
if f.alternate() {
|
||||
write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
|
||||
} else {
|
||||
write!(f, "impl {}", print_generic_bounds(bounds, cx))
|
||||
}
|
||||
f.write_str("impl ")?;
|
||||
print_generic_bounds(bounds, cx).fmt(f)
|
||||
}
|
||||
clean::QPath(box clean::QPathData {
|
||||
ref assoc,
|
||||
|
@ -1292,8 +1230,7 @@ fn fmt_type<'cx>(
|
|||
write!(f, "{}", assoc.name)
|
||||
}?;
|
||||
|
||||
// Carry `f.alternate()` into this display w/o branching manually.
|
||||
fmt::Display::fmt(&assoc.args.print(cx), f)
|
||||
assoc.args.print(cx).fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1302,7 +1239,7 @@ impl clean::Type {
|
|||
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'b + Captures<'tcx> {
|
||||
) -> impl Display + 'b + Captures<'tcx> {
|
||||
display_fn(move |f| fmt_type(self, f, false, cx))
|
||||
}
|
||||
}
|
||||
|
@ -1311,7 +1248,7 @@ impl clean::Path {
|
|||
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'b + Captures<'tcx> {
|
||||
) -> impl Display + 'b + Captures<'tcx> {
|
||||
display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
|
||||
}
|
||||
}
|
||||
|
@ -1321,20 +1258,18 @@ impl clean::Impl {
|
|||
&'a self,
|
||||
use_absolute: bool,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
if f.alternate() {
|
||||
write!(f, "impl{:#} ", self.generics.print(cx))?;
|
||||
} else {
|
||||
write!(f, "impl{} ", self.generics.print(cx))?;
|
||||
}
|
||||
f.write_str("impl")?;
|
||||
self.generics.print(cx).fmt(f)?;
|
||||
f.write_str(" ")?;
|
||||
|
||||
if let Some(ref ty) = self.trait_ {
|
||||
match self.polarity {
|
||||
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
|
||||
ty::ImplPolarity::Negative => write!(f, "!")?,
|
||||
}
|
||||
fmt::Display::fmt(&ty.print(cx), f)?;
|
||||
ty.print(cx).fmt(f)?;
|
||||
write!(f, " for ")?;
|
||||
}
|
||||
|
||||
|
@ -1359,14 +1294,9 @@ impl clean::Impl {
|
|||
// Hardcoded anchor library/core/src/primitive_docs.rs
|
||||
// Link should match `# Trait implementations`
|
||||
|
||||
let hrtb = bare_fn.print_hrtb_with_space(cx);
|
||||
let unsafety = bare_fn.unsafety.print_with_space();
|
||||
let abi = print_abi_with_space(bare_fn.abi);
|
||||
if f.alternate() {
|
||||
write!(f, "{hrtb:#}{unsafety}{abi:#}",)?;
|
||||
} else {
|
||||
write!(f, "{hrtb}{unsafety}{abi}",)?;
|
||||
}
|
||||
print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?;
|
||||
bare_fn.unsafety.print_with_space().fmt(f)?;
|
||||
print_abi_with_space(bare_fn.abi).fmt(f)?;
|
||||
let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
|
||||
primitive_link_fragment(
|
||||
f,
|
||||
|
@ -1386,8 +1316,7 @@ impl clean::Impl {
|
|||
fmt_type(&self.for_, f, use_absolute, cx)?;
|
||||
}
|
||||
|
||||
fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?;
|
||||
Ok(())
|
||||
print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1396,16 +1325,11 @@ impl clean::Arguments {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
for (i, input) in self.values.iter().enumerate() {
|
||||
write!(f, "{}: ", input.name)?;
|
||||
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", input.type_.print(cx))?;
|
||||
} else {
|
||||
write!(f, "{}", input.type_.print(cx))?;
|
||||
}
|
||||
input.type_.print(cx).fmt(f)?;
|
||||
if i + 1 < self.values.len() {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
|
@ -1415,25 +1339,6 @@ impl clean::Arguments {
|
|||
}
|
||||
}
|
||||
|
||||
impl clean::BareFunctionDecl {
|
||||
fn print_hrtb_with_space<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
if !self.generic_params.is_empty() {
|
||||
write!(
|
||||
f,
|
||||
"for<{}> ",
|
||||
comma_sep(self.generic_params.iter().map(|g| g.print(cx)), true)
|
||||
)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Implements Write but only counts the bytes "written".
|
||||
struct WriteCounter(usize);
|
||||
|
||||
|
@ -1447,7 +1352,7 @@ impl std::fmt::Write for WriteCounter {
|
|||
// Implements Display by emitting the given number of spaces.
|
||||
struct Indent(usize);
|
||||
|
||||
impl fmt::Display for Indent {
|
||||
impl Display for Indent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
(0..self.0).for_each(|_| {
|
||||
f.write_char(' ').unwrap();
|
||||
|
@ -1460,7 +1365,7 @@ impl clean::FnDecl {
|
|||
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'b + Captures<'tcx> {
|
||||
) -> impl Display + 'b + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
let ellipsis = if self.c_variadic { ", ..." } else { "" };
|
||||
if f.alternate() {
|
||||
|
@ -1494,7 +1399,7 @@ impl clean::FnDecl {
|
|||
header_len: usize,
|
||||
indent: usize,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
// First, generate the text form of the declaration, with no line wrapping, and count the bytes.
|
||||
let mut counter = WriteCounter(0);
|
||||
|
@ -1554,7 +1459,7 @@ impl clean::FnDecl {
|
|||
}
|
||||
clean::SelfExplicit(ref typ) => {
|
||||
write!(f, "self: ")?;
|
||||
fmt::Display::fmt(&typ.print(cx), f)?;
|
||||
typ.print(cx).fmt(f)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -1562,7 +1467,7 @@ impl clean::FnDecl {
|
|||
write!(f, "const ")?;
|
||||
}
|
||||
write!(f, "{}: ", input.name)?;
|
||||
fmt::Display::fmt(&input.type_.print(cx), f)?;
|
||||
input.type_.print(cx).fmt(f)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1578,14 +1483,13 @@ impl clean::FnDecl {
|
|||
Some(n) => write!(f, "\n{})", Indent(n))?,
|
||||
};
|
||||
|
||||
fmt::Display::fmt(&self.print_output(cx), f)?;
|
||||
Ok(())
|
||||
self.print_output(cx).fmt(f)
|
||||
}
|
||||
|
||||
fn print_output<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match &self.output {
|
||||
clean::Tuple(tys) if tys.is_empty() => Ok(()),
|
||||
ty if f.alternate() => {
|
||||
|
@ -1600,7 +1504,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
|
|||
visibility: Option<ty::Visibility<DefId>>,
|
||||
item_did: ItemId,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
use std::fmt::Write as _;
|
||||
|
||||
let to_print: Cow<'static, str> = match visibility {
|
||||
|
@ -1648,7 +1552,7 @@ pub(crate) fn visibility_to_src_with_space<'a, 'tcx: 'a>(
|
|||
visibility: Option<ty::Visibility<DefId>>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
item_did: DefId,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
let to_print: Cow<'static, str> = match visibility {
|
||||
None => "".into(),
|
||||
Some(ty::Visibility::Public) => "pub ".into(),
|
||||
|
@ -1727,7 +1631,7 @@ impl clean::Import {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self.kind {
|
||||
clean::ImportKind::Simple(name) => {
|
||||
if name == self.source.path.last() {
|
||||
|
@ -1751,7 +1655,7 @@ impl clean::ImportSource {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self.did {
|
||||
Some(did) => resolved_path(f, did, &self.path, true, false, cx),
|
||||
_ => {
|
||||
|
@ -1779,29 +1683,19 @@ impl clean::TypeBinding {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| {
|
||||
f.write_str(self.assoc.name.as_str())?;
|
||||
if f.alternate() {
|
||||
write!(f, "{:#}", self.assoc.args.print(cx))?;
|
||||
} else {
|
||||
write!(f, "{}", self.assoc.args.print(cx))?;
|
||||
}
|
||||
self.assoc.args.print(cx).fmt(f)?;
|
||||
match self.kind {
|
||||
clean::TypeBindingKind::Equality { ref term } => {
|
||||
if f.alternate() {
|
||||
write!(f, " = {:#}", term.print(cx))?;
|
||||
} else {
|
||||
write!(f, " = {}", term.print(cx))?;
|
||||
}
|
||||
f.write_str(" = ")?;
|
||||
term.print(cx).fmt(f)?;
|
||||
}
|
||||
clean::TypeBindingKind::Constraint { ref bounds } => {
|
||||
if !bounds.is_empty() {
|
||||
if f.alternate() {
|
||||
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
|
||||
} else {
|
||||
write!(f, ": {}", print_generic_bounds(bounds, cx))?;
|
||||
}
|
||||
f.write_str(": ")?;
|
||||
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1810,7 +1704,7 @@ impl clean::TypeBinding {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
|
||||
pub(crate) fn print_abi_with_space(abi: Abi) -> impl Display {
|
||||
display_fn(move |f| {
|
||||
let quot = if f.alternate() { "\"" } else { """ };
|
||||
match abi {
|
||||
|
@ -1828,34 +1722,32 @@ impl clean::GenericArg {
|
|||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self {
|
||||
clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f),
|
||||
clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
|
||||
clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
|
||||
clean::GenericArg::Infer => fmt::Display::fmt("_", f),
|
||||
clean::GenericArg::Lifetime(lt) => lt.print().fmt(f),
|
||||
clean::GenericArg::Type(ty) => ty.print(cx).fmt(f),
|
||||
clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f),
|
||||
clean::GenericArg::Infer => Display::fmt("_", f),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl clean::types::Term {
|
||||
impl clean::Term {
|
||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
) -> impl Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self {
|
||||
clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
|
||||
clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
|
||||
clean::Term::Type(ty) => ty.print(cx).fmt(f),
|
||||
clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display_fn(
|
||||
f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
|
||||
) -> impl fmt::Display {
|
||||
pub(crate) fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Display {
|
||||
struct WithFormatter<F>(Cell<Option<F>>);
|
||||
|
||||
impl<F> fmt::Display for WithFormatter<F>
|
||||
impl<F> Display for WithFormatter<F>
|
||||
where
|
||||
F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
|
||||
{
|
||||
|
|
|
@ -1941,13 +1941,8 @@ in src-script.js and main.js
|
|||
pixels to avoid overflowing the topbar when the user sets a bigger
|
||||
font size. */
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.mobile-topbar h2 a {
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mobile-topbar .logo-container > img {
|
||||
|
|
|
@ -45,7 +45,7 @@ function setMobileTopbar() {
|
|||
const mobileTitle = document.createElement("h2");
|
||||
mobileTitle.className = "location";
|
||||
if (hasClass(document.querySelector(".rustdoc"), "crate")) {
|
||||
mobileTitle.innerText = `Crate ${window.currentCrate}`;
|
||||
mobileTitle.innerHTML = `Crate <a href="#">${window.currentCrate}</a>`;
|
||||
} else if (locationTitle) {
|
||||
mobileTitle.innerHTML = locationTitle.innerHTML;
|
||||
}
|
||||
|
|
22
tests/rustdoc-gui/mobile-crate-name.goml
Normal file
22
tests/rustdoc-gui/mobile-crate-name.goml
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Checks that if the crate name is too long on mobile, it will not grow and overflow its parent
|
||||
// (thanks to text overflow ellipsis).
|
||||
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
|
||||
// First we change the title to make it big.
|
||||
set-window-size: (350, 800)
|
||||
// We ensure that the "format" of the title is the same as the one we'll use.
|
||||
assert-text: (".mobile-topbar .location a", "test_docs")
|
||||
// We store the height we know is correct.
|
||||
store-property: (".mobile-topbar .location", {"offsetHeight": height})
|
||||
// We change the crate name to something longer.
|
||||
set-text: (".mobile-topbar .location a", "cargo_packager_resource_resolver")
|
||||
// And we check that the size remained the same.
|
||||
assert-property: (".mobile-topbar .location", {"offsetHeight": |height|})
|
||||
|
||||
// Now we check if it works for the non-crate pages as well.
|
||||
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
|
||||
// We store the height we know is correct.
|
||||
store-property: (".mobile-topbar .location", {"offsetHeight": height})
|
||||
set-text: (".mobile-topbar .location a", "Something_incredibly_long_because")
|
||||
// And we check that the size remained the same.
|
||||
assert-property: (".mobile-topbar .location", {"offsetHeight": |height|})
|
|
@ -39,8 +39,8 @@ assert-property: ("pre.item-decl", {"scrollWidth": "950"})
|
|||
set-window-size: (600, 600)
|
||||
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
|
||||
// It shouldn't have an overflow in the topbar either.
|
||||
store-property: (".mobile-topbar h2", {"scrollWidth": scrollWidth})
|
||||
assert-property: (".mobile-topbar h2", {"clientWidth": |scrollWidth|})
|
||||
store-property: (".mobile-topbar", {"scrollWidth": scrollWidth})
|
||||
assert-property: (".mobile-topbar", {"clientWidth": |scrollWidth|})
|
||||
assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
|
||||
|
||||
// Check wrapping for top main-heading h1 and out-of-band.
|
||||
|
|
|
@ -18,3 +18,15 @@ pub trait T2 {
|
|||
fn f<T: Eq>()
|
||||
where Self: Eq, Self: Eq2, T: Eq2;
|
||||
}
|
||||
|
||||
// Checking that we support empty bounds (we used to crash on empty outlives-bounds).
|
||||
// Note that we don't want to hide them since they have a semantic effect.
|
||||
// For outlives-bounds, they force the lifetime param to be early-bound instead of late-bound.
|
||||
// For trait bounds, it can affect well-formedness (see `ClauseKind::WellFormed`).
|
||||
// @has 'foo/fn.empty.html'
|
||||
// @has - '//pre[@class="rust item-decl"]' "empty<'a, T>()where T:, 'a:,"
|
||||
pub fn empty<'a, T>()
|
||||
where
|
||||
T:,
|
||||
'a:,
|
||||
{}
|
|
@ -3,5 +3,5 @@
|
|||
#![allow(incomplete_features)]
|
||||
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
|
||||
// @has foo/struct.Ice.html '//pre[@class="rust item-decl"]' \
|
||||
// 'pub struct Ice<const N: usize>;'
|
||||
// 'pub struct Ice<const N: usize> where [(); { _ }]:;'
|
||||
pub struct Ice<const N: usize> where [(); N + 1]:;
|
||||
|
|
6
tests/rustdoc/inline_cross/auxiliary/issue-76736-1.rs
Normal file
6
tests/rustdoc/inline_cross/auxiliary/issue-76736-1.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
#![feature(staged_api)]
|
||||
#![unstable(feature = "rustc_private", issue = "none")]
|
||||
|
||||
pub trait MaybeResult<T> {}
|
||||
|
||||
impl<T> MaybeResult<T> for T {}
|
5
tests/rustdoc/inline_cross/auxiliary/issue-76736-2.rs
Normal file
5
tests/rustdoc/inline_cross/auxiliary/issue-76736-2.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#![feature(rustc_private)]
|
||||
|
||||
extern crate issue_76736_1;
|
||||
|
||||
pub struct Bar;
|
15
tests/rustdoc/inline_cross/issue-76736-1.rs
Normal file
15
tests/rustdoc/inline_cross/issue-76736-1.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// aux-build:issue-76736-1.rs
|
||||
// aux-build:issue-76736-2.rs
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
extern crate issue_76736_1;
|
||||
extern crate issue_76736_2;
|
||||
|
||||
// @has foo/struct.Foo.html
|
||||
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub struct Foo;
|
||||
|
||||
// @has foo/struct.Bar.html
|
||||
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub use issue_76736_2::Bar;
|
16
tests/rustdoc/inline_cross/issue-76736-2.rs
Normal file
16
tests/rustdoc/inline_cross/issue-76736-2.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// aux-build:issue-76736-1.rs
|
||||
// aux-build:issue-76736-2.rs
|
||||
|
||||
#![crate_name = "foo"]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate issue_76736_1;
|
||||
extern crate issue_76736_2;
|
||||
|
||||
// @has foo/struct.Foo.html
|
||||
// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub struct Foo;
|
||||
|
||||
// @has foo/struct.Bar.html
|
||||
// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub use issue_76736_2::Bar;
|
16
tests/rustdoc/inline_cross/issue-76736-3.rs
Normal file
16
tests/rustdoc/inline_cross/issue-76736-3.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
// compile-flags: -Zforce-unstable-if-unmarked
|
||||
// aux-build:issue-76736-1.rs
|
||||
// aux-build:issue-76736-2.rs
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
extern crate issue_76736_1;
|
||||
extern crate issue_76736_2;
|
||||
|
||||
// @has foo/struct.Foo.html
|
||||
// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub struct Foo;
|
||||
|
||||
// @has foo/struct.Bar.html
|
||||
// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'MaybeResult'
|
||||
pub use issue_76736_2::Bar;
|
|
@ -8,6 +8,11 @@ LL | let mut f = File::open(path.to_str())?;
|
|||
|
|
||||
note: required by a bound in `File::open`
|
||||
--> $SRC_DIR/std/src/fs.rs:LL:COL
|
||||
help: consider removing this method call, as the receiver has type `&Path` and `&Path: AsRef<Path>` trivially holds
|
||||
|
|
||||
LL - let mut f = File::open(path.to_str())?;
|
||||
LL + let mut f = File::open(path)?;
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//@ rustc-env:CARGO=/usr/bin/cargo
|
||||
//@ rustc-env:CARGO_CRATE_NAME=foo
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::future::Future;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//
|
||||
//@ check-pass
|
||||
//@ revisions: some none
|
||||
//@ rustc-env:CARGO=/usr/bin/cargo
|
||||
//@ rustc-env:CARGO_CRATE_NAME=foo
|
||||
//@ compile-flags: -Z unstable-options
|
||||
//@ [none]compile-flags: --check-cfg=cfg(feature,values())
|
||||
//@ [some]compile-flags: --check-cfg=cfg(feature,values("bitcode"))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//@ check-pass
|
||||
//@ revisions: cargo rustc
|
||||
//@ [rustc]unset-rustc-env:CARGO
|
||||
//@ [cargo]rustc-env:CARGO=/usr/bin/cargo
|
||||
//@ [rustc]unset-rustc-env:CARGO_CRATE_NAME
|
||||
//@ [cargo]rustc-env:CARGO_CRATE_NAME=foo
|
||||
//@ compile-flags: --check-cfg=cfg(feature,values("foo")) --check-cfg=cfg(no_values) -Z unstable-options
|
||||
|
||||
#[cfg(featur)]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//@ compile-flags: --target x86_64-unknown-uefi
|
||||
//@ needs-llvm-components: x86
|
||||
//@ rustc-env:CARGO=/usr/bin/cargo
|
||||
//@ rustc-env:CARGO_CRATE_NAME=foo
|
||||
#![feature(no_core)]
|
||||
#![no_core]
|
||||
extern crate core;
|
||||
|
|
|
@ -11,6 +11,11 @@ note: required by a bound in `check_bound`
|
|||
|
|
||||
LL | fn check_bound<T:Copy>(_: T) {}
|
||||
| ^^^^ required by this bound in `check_bound`
|
||||
help: consider removing this method call, as the receiver has type `&'static str` and `&'static str: Copy` trivially holds
|
||||
|
|
||||
LL - check_bound("nocopy".to_string());
|
||||
LL + check_bound("nocopy");
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
|
@ -11,6 +11,11 @@ note: required by a bound in `f_copy`
|
|||
|
|
||||
LL | fn f_copy<T: Copy>(t: T) {}
|
||||
| ^^^^ required by this bound in `f_copy`
|
||||
help: consider removing this method call, as the receiver has type `&'static str` and `&'static str: Copy` trivially holds
|
||||
|
|
||||
LL - f_copy("".to_string());
|
||||
LL + f_copy("");
|
||||
|
|
||||
|
||||
error[E0277]: the trait bound `S: Clone` is not satisfied
|
||||
--> $DIR/issue-84973-blacklist.rs:16:13
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
struct Foo;
|
||||
struct Bar;
|
||||
impl From<Bar> for Foo {
|
||||
fn from(_: Bar) -> Self { Foo }
|
||||
}
|
||||
fn qux(_: impl From<Bar>) {}
|
||||
fn main() {
|
||||
qux(Bar.into()); //~ ERROR type annotations needed
|
||||
//~| HELP try using a fully qualified path to specify the expected types
|
||||
//~| HELP consider removing this method call, as the receiver has type `Bar` and `Bar: From<Bar>` trivially holds
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
error[E0283]: type annotations needed
|
||||
--> $DIR/argument-with-unnecessary-method-call.rs:8:13
|
||||
|
|
||||
LL | qux(Bar.into());
|
||||
| --- ^^^^
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= note: cannot satisfy `_: From<Bar>`
|
||||
note: required by a bound in `qux`
|
||||
--> $DIR/argument-with-unnecessary-method-call.rs:6:16
|
||||
|
|
||||
LL | fn qux(_: impl From<Bar>) {}
|
||||
| ^^^^^^^^^ required by this bound in `qux`
|
||||
help: try using a fully qualified path to specify the expected types
|
||||
|
|
||||
LL | qux(<Bar as Into<T>>::into(Bar));
|
||||
| +++++++++++++++++++++++ ~
|
||||
help: consider removing this method call, as the receiver has type `Bar` and `Bar: From<Bar>` trivially holds
|
||||
|
|
||||
LL - qux(Bar.into());
|
||||
LL + qux(Bar);
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0283`.
|
18
tests/ui/union/unnamed-fields/auxiliary/dep.rs
Normal file
18
tests/ui/union/unnamed-fields/auxiliary/dep.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
#[repr(C)]
|
||||
pub struct GoodStruct(());
|
||||
|
||||
pub struct BadStruct(());
|
||||
|
||||
pub enum BadEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub enum BadEnum2 {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
pub type GoodAlias = GoodStruct;
|
||||
pub type BadAlias = i32;
|
44
tests/ui/union/unnamed-fields/restrict_type_hir.rs
Normal file
44
tests/ui/union/unnamed-fields/restrict_type_hir.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
//@ aux-build:dep.rs
|
||||
|
||||
// test for #121151
|
||||
|
||||
#![allow(incomplete_features)]
|
||||
#![feature(unnamed_fields)]
|
||||
|
||||
extern crate dep;
|
||||
|
||||
#[repr(C)]
|
||||
struct A {
|
||||
a: u8,
|
||||
}
|
||||
|
||||
enum BadEnum {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
enum BadEnum2 {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
type MyStruct = A;
|
||||
type MyI32 = i32;
|
||||
|
||||
#[repr(C)]
|
||||
struct L {
|
||||
_: i32, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: MyI32, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: BadEnum, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: BadEnum2, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: MyStruct,
|
||||
_: dep::BadStruct, //~ ERROR named type of unnamed field must have `#[repr(C)]` representation
|
||||
_: dep::BadEnum, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: dep::BadEnum2, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: dep::BadAlias, //~ ERROR unnamed fields can only have struct or union types
|
||||
_: dep::GoodAlias,
|
||||
_: dep::GoodStruct,
|
||||
}
|
||||
|
||||
fn main() {}
|
62
tests/ui/union/unnamed-fields/restrict_type_hir.stderr
Normal file
62
tests/ui/union/unnamed-fields/restrict_type_hir.stderr
Normal file
|
@ -0,0 +1,62 @@
|
|||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:31:5
|
||||
|
|
||||
LL | _: i32,
|
||||
| ^^^^^^
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:32:5
|
||||
|
|
||||
LL | _: MyI32,
|
||||
| ^^^^^^^^
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:33:5
|
||||
|
|
||||
LL | _: BadEnum,
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:34:5
|
||||
|
|
||||
LL | _: BadEnum2,
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
error: named type of unnamed field must have `#[repr(C)]` representation
|
||||
--> $DIR/restrict_type_hir.rs:36:5
|
||||
|
|
||||
LL | _: dep::BadStruct,
|
||||
| ^^^^^^^^^^^^^^^^^ unnamed field defined here
|
||||
|
|
||||
::: $DIR/auxiliary/dep.rs:4:1
|
||||
|
|
||||
LL | pub struct BadStruct(());
|
||||
| -------------------- `BadStruct` defined here
|
||||
|
|
||||
help: add `#[repr(C)]` to this struct
|
||||
--> $DIR/auxiliary/dep.rs:4:1
|
||||
|
|
||||
LL + #[repr(C)]
|
||||
LL | pub struct BadStruct(());
|
||||
|
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:37:5
|
||||
|
|
||||
LL | _: dep::BadEnum,
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:38:5
|
||||
|
|
||||
LL | _: dep::BadEnum2,
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unnamed fields can only have struct or union types
|
||||
--> $DIR/restrict_type_hir.rs:39:5
|
||||
|
|
||||
LL | _: dep::BadAlias,
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue