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.
|
// which by now we have no way to translate.
|
||||||
if contains_undefined_ref {
|
if contains_undefined_ref {
|
||||||
diag.note(fluent::codegen_ssa_extern_funcs_not_found)
|
diag.note(fluent::codegen_ssa_extern_funcs_not_found)
|
||||||
.note(fluent::codegen_ssa_specify_libraries_to_link)
|
.note(fluent::codegen_ssa_specify_libraries_to_link);
|
||||||
.note(fluent::codegen_ssa_use_cargo_directive);
|
|
||||||
|
if rustc_session::utils::was_invoked_from_cargo() {
|
||||||
|
diag.note(fluent::codegen_ssa_use_cargo_directive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
diag
|
diag
|
||||||
}
|
}
|
||||||
|
|
|
@ -2998,6 +2998,12 @@ impl<'hir> Item<'hir> {
|
||||||
ItemId { owner_id: self.owner_id }
|
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_methods_self_kind! {
|
||||||
expect_extern_crate, Option<Symbol>, ItemKind::ExternCrate(s), *s;
|
expect_extern_crate, Option<Symbol>, ItemKind::ExternCrate(s), *s;
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,8 @@ hir_analysis_invalid_union_field =
|
||||||
hir_analysis_invalid_union_field_sugg =
|
hir_analysis_invalid_union_field_sugg =
|
||||||
wrap the field type in `ManuallyDrop<...>`
|
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
|
hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
|
||||||
.label = const parameter declared here
|
.label = const parameter declared here
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,7 @@ use rustc_span::{ErrorGuaranteed, Span};
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::astconv::{
|
use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
|
||||||
AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter,
|
|
||||||
};
|
|
||||||
use crate::bounds::Bounds;
|
use crate::bounds::Bounds;
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
|
|
||||||
|
@ -238,7 +236,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
&self,
|
&self,
|
||||||
hir_ref_id: hir::HirId,
|
hir_ref_id: hir::HirId,
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
binding: &ConvertedBinding<'_, 'tcx>,
|
binding: &hir::TypeBinding<'tcx>,
|
||||||
bounds: &mut Bounds<'tcx>,
|
bounds: &mut Bounds<'tcx>,
|
||||||
speculative: bool,
|
speculative: bool,
|
||||||
dup_bindings: &mut FxIndexMap<DefId, Span>,
|
dup_bindings: &mut FxIndexMap<DefId, Span>,
|
||||||
|
@ -263,21 +261,20 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
|
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
|
|
||||||
let assoc_kind =
|
let assoc_kind = if binding.gen_args.parenthesized
|
||||||
if binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation {
|
== hir::GenericArgsParentheses::ReturnTypeNotation
|
||||||
ty::AssocKind::Fn
|
{
|
||||||
} else if let ConvertedBindingKind::Equality(term) = binding.kind
|
ty::AssocKind::Fn
|
||||||
&& let ty::TermKind::Const(_) = term.node.unpack()
|
} else if let hir::TypeBindingKind::Equality { term: hir::Term::Const(_) } = binding.kind {
|
||||||
{
|
ty::AssocKind::Const
|
||||||
ty::AssocKind::Const
|
} else {
|
||||||
} else {
|
ty::AssocKind::Type
|
||||||
ty::AssocKind::Type
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let candidate = if self.trait_defines_associated_item_named(
|
let candidate = if self.trait_defines_associated_item_named(
|
||||||
trait_ref.def_id(),
|
trait_ref.def_id(),
|
||||||
assoc_kind,
|
assoc_kind,
|
||||||
binding.item_name,
|
binding.ident,
|
||||||
) {
|
) {
|
||||||
// Simple case: The assoc item is defined in the current trait.
|
// Simple case: The assoc item is defined in the current trait.
|
||||||
trait_ref
|
trait_ref
|
||||||
|
@ -289,14 +286,14 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
trait_ref.skip_binder().print_only_trait_name(),
|
trait_ref.skip_binder().print_only_trait_name(),
|
||||||
None,
|
None,
|
||||||
assoc_kind,
|
assoc_kind,
|
||||||
binding.item_name,
|
binding.ident,
|
||||||
path_span,
|
path_span,
|
||||||
Some(&binding),
|
Some(binding),
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let (assoc_ident, def_scope) =
|
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()`
|
// 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
|
// instead of calling `filter_by_name_and_kind` which would needlessly normalize the
|
||||||
|
@ -312,7 +309,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
.dcx()
|
.dcx()
|
||||||
.struct_span_err(
|
.struct_span_err(
|
||||||
binding.span,
|
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))
|
.with_span_label(binding.span, format!("private {}", assoc_item.kind))
|
||||||
.emit();
|
.emit();
|
||||||
|
@ -327,7 +324,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
|
tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
|
||||||
span: binding.span,
|
span: binding.span,
|
||||||
prev_span: *prev_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)),
|
def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
@ -390,14 +387,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
{
|
{
|
||||||
alias_ty
|
alias_ty
|
||||||
} else {
|
} else {
|
||||||
return Err(self.tcx().dcx().emit_err(
|
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
|
||||||
crate::errors::ReturnTypeNotationOnNonRpitit {
|
span: binding.span,
|
||||||
span: binding.span,
|
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
|
||||||
ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
|
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
|
||||||
fn_span: tcx.hir().span_if_local(assoc_item.def_id),
|
note: (),
|
||||||
note: (),
|
}));
|
||||||
},
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Finally, move the fn return type's bound vars over to account for the early bound
|
// 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);
|
let bound_vars = tcx.late_bound_vars(binding.hir_id);
|
||||||
ty::Binder::bind_with_vars(instantiation_output, bound_vars)
|
ty::Binder::bind_with_vars(instantiation_output, bound_vars)
|
||||||
} else {
|
} 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| {
|
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 {
|
let item_segment = hir::PathSegment {
|
||||||
ident,
|
ident,
|
||||||
hir_id: binding.hir_id,
|
hir_id: binding.hir_id,
|
||||||
|
@ -421,77 +418,82 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
||||||
infer_args: false,
|
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,
|
path_span,
|
||||||
assoc_item.def_id,
|
assoc_item.def_id,
|
||||||
&item_segment,
|
&item_segment,
|
||||||
trait_ref.args,
|
trait_ref.args,
|
||||||
);
|
);
|
||||||
|
debug!(?alias_args);
|
||||||
|
|
||||||
debug!(?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
|
||||||
ty::AliasTy::new(tcx, assoc_item.def_id, args_trait_ref_and_assoc_item)
|
// 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 {
|
match binding.kind {
|
||||||
ConvertedBindingKind::Equality(..) if let ty::AssocKind::Fn = assoc_kind => {
|
hir::TypeBindingKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => {
|
||||||
return Err(self.tcx().dcx().emit_err(
|
return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
|
||||||
crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
|
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
|
// "Desugar" a constraint like `T: Iterator<Item = u32>` this to
|
||||||
// the "projection predicate" for:
|
// the "projection predicate" for:
|
||||||
//
|
//
|
||||||
// `<T as Iterator>::Item = u32`
|
// `<T as Iterator>::Item = u32`
|
||||||
bounds.push_projection_bound(
|
bounds.push_projection_bound(
|
||||||
tcx,
|
tcx,
|
||||||
projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
|
projection_ty
|
||||||
projection_ty,
|
.map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
|
||||||
term: term.node,
|
|
||||||
}),
|
|
||||||
binding.span,
|
binding.span,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ConvertedBindingKind::Constraint(ast_bounds) => {
|
hir::TypeBindingKind::Constraint { bounds: ast_bounds } => {
|
||||||
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
|
// "Desugar" a constraint like `T: Iterator<Item: Debug>` to
|
||||||
//
|
//
|
||||||
// `<T as Iterator>::Item: Debug`
|
// `<T as Iterator>::Item: Debug`
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::astconv::{AstConv, ConvertedBindingKind};
|
use crate::astconv::AstConv;
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
|
self, AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
|
||||||
ParenthesizedFnTraitExpansion,
|
ParenthesizedFnTraitExpansion,
|
||||||
|
@ -111,7 +111,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
assoc_kind: ty::AssocKind,
|
assoc_kind: ty::AssocKind,
|
||||||
assoc_name: Ident,
|
assoc_name: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||||
) -> ErrorGuaranteed
|
) -> ErrorGuaranteed
|
||||||
where
|
where
|
||||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||||
|
@ -243,7 +243,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
None,
|
None,
|
||||||
) && suggested_name != assoc_name.name
|
) && 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.
|
// was also not an exact match, so we also suggest changing it.
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
assoc_name.span,
|
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.
|
// suggests using it.
|
||||||
if let [candidate_name] = all_candidate_names.as_slice() {
|
if let [candidate_name] = all_candidate_names.as_slice() {
|
||||||
// this should still compile, except on `#![feature(associated_type_defaults)]`
|
// This should still compile, except on `#![feature(associated_type_defaults)]`
|
||||||
// where it could suggests `type A = Self::A`, thus recursing infinitely
|
// where it could suggests `type A = Self::A`, thus recursing infinitely.
|
||||||
let applicability = if tcx.features().associated_type_defaults {
|
let applicability =
|
||||||
Applicability::Unspecified
|
if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
|
||||||
} else {
|
Applicability::Unspecified
|
||||||
Applicability::MaybeIncorrect
|
} else {
|
||||||
};
|
Applicability::MaybeIncorrect
|
||||||
|
};
|
||||||
|
|
||||||
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
|
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
|
||||||
span: assoc_name.span,
|
span: assoc_name.span,
|
||||||
|
@ -289,13 +290,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
assoc_kind: ty::AssocKind,
|
assoc_kind: ty::AssocKind,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
binding: Option<&super::ConvertedBinding<'_, 'tcx>>,
|
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||||
) -> ErrorGuaranteed {
|
) -> ErrorGuaranteed {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
|
|
||||||
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
|
let bound_on_assoc_const_label = if let ty::AssocKind::Const = assoc_item.kind
|
||||||
&& let Some(binding) = binding
|
&& 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() {
|
let lo = if binding.gen_args.span_ext.is_dummy() {
|
||||||
ident.span
|
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.
|
// FIXME(associated_const_equality): This has quite a few false positives and negatives.
|
||||||
let wrap_in_braces_sugg = if let Some(binding) = binding
|
let wrap_in_braces_sugg = if let Some(binding) = binding
|
||||||
&& let ConvertedBindingKind::Equality(term) = binding.kind
|
&& let hir::TypeBindingKind::Equality { term: hir::Term::Ty(hir_ty) } = binding.kind
|
||||||
&& let ty::TermKind::Ty(ty) = term.node.unpack()
|
&& let ty = self.ast_ty_to_ty(hir_ty)
|
||||||
&& (ty.is_enum() || ty.references_error())
|
&& (ty.is_enum() || ty.references_error())
|
||||||
&& tcx.features().associated_const_equality
|
&& tcx.features().associated_const_equality
|
||||||
{
|
{
|
||||||
Some(errors::AssocKindMismatchWrapInBracesSugg {
|
Some(errors::AssocKindMismatchWrapInBracesSugg {
|
||||||
lo: term.span.shrink_to_lo(),
|
lo: hir_ty.span.shrink_to_lo(),
|
||||||
hi: term.span.shrink_to_hi(),
|
hi: hir_ty.span.shrink_to_hi(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// For equality bounds, we want to blame the term (RHS) instead of the item (LHS) since
|
// 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 (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 {
|
} else {
|
||||||
(ident.span, None, assoc_kind, assoc_item.kind)
|
(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_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
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::symbol::{kw, Ident, Symbol};
|
||||||
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
|
use rustc_span::{sym, BytePos, Span, DUMMY_SP};
|
||||||
use rustc_target::spec::abi;
|
use rustc_target::spec::abi;
|
||||||
|
@ -151,21 +150,6 @@ pub trait AstConv<'tcx> {
|
||||||
fn infcx(&self) -> Option<&InferCtxt<'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
|
/// New-typed boolean indicating whether explicit late-bound lifetimes
|
||||||
/// are present in a set of generic arguments.
|
/// 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
|
/// Given the type/lifetime/const arguments provided to some path (along with
|
||||||
/// an implicit `Self`, if this is a trait reference), returns the complete
|
/// an implicit `Self`, if this is a trait reference), returns the complete
|
||||||
/// set of generic arguments. This may involve applying defaulted type parameters.
|
/// 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:
|
/// 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`,
|
/// 2. The path in question is the path to the trait `std::ops::Index`,
|
||||||
/// which will have been resolved to a `def_id`
|
/// which will have been resolved to a `def_id`
|
||||||
/// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
|
/// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
|
||||||
/// parameters are returned in the `GenericArgsRef`, the associated type bindings like
|
/// parameters are returned in the `GenericArgsRef`
|
||||||
/// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`.
|
/// 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.
|
/// 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)
|
(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(
|
pub fn create_args_for_associated_item(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
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);
|
let bound_vars = tcx.late_bound_vars(trait_ref.hir_ref_id);
|
||||||
debug!(?bound_vars);
|
debug!(?bound_vars);
|
||||||
|
|
||||||
let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
|
|
||||||
|
|
||||||
let poly_trait_ref = ty::Binder::bind_with_vars(
|
let poly_trait_ref = ty::Binder::bind_with_vars(
|
||||||
ty::TraitRef::new(tcx, trait_def_id, generic_args),
|
ty::TraitRef::new(tcx, trait_def_id, generic_args),
|
||||||
bound_vars,
|
bound_vars,
|
||||||
);
|
);
|
||||||
|
|
||||||
debug!(?poly_trait_ref, ?assoc_bindings);
|
debug!(?poly_trait_ref);
|
||||||
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
|
bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
|
||||||
|
|
||||||
let mut dup_bindings = FxIndexMap::default();
|
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,
|
// Don't register additional associated type bounds for negative bounds,
|
||||||
// since we should have emitten an error for them earlier, and they will
|
// since we should have emitten an error for them earlier, and they will
|
||||||
// not be well-formed!
|
// not be well-formed!
|
||||||
|
@ -1029,7 +967,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
assoc_kind: ty::AssocKind,
|
assoc_kind: ty::AssocKind,
|
||||||
assoc_name: Ident,
|
assoc_name: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
binding: Option<&ConvertedBinding<'_, 'tcx>>,
|
binding: Option<&hir::TypeBinding<'tcx>>,
|
||||||
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
|
) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
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.
|
// Provide a more specific error code index entry for equality bindings.
|
||||||
err.code(
|
err.code(
|
||||||
if let Some(binding) = binding
|
if let Some(binding) = binding
|
||||||
&& let ConvertedBindingKind::Equality(_) = binding.kind
|
&& let hir::TypeBindingKind::Equality { .. } = binding.kind
|
||||||
{
|
{
|
||||||
E0222
|
E0222
|
||||||
} else {
|
} else {
|
||||||
|
@ -1094,16 +1032,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||||
);
|
);
|
||||||
if let Some(binding) = binding {
|
if let Some(binding) = binding {
|
||||||
match binding.kind {
|
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!
|
// FIXME(#97583): This isn't syntactically well-formed!
|
||||||
where_bounds.push(format!(
|
where_bounds.push(format!(
|
||||||
" T: {trait}::{assoc_name} = {term}",
|
" T: {trait}::{assoc_name} = {term}",
|
||||||
trait = bound.print_only_trait_path(),
|
trait = bound.print_only_trait_path(),
|
||||||
term = term.node,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// FIXME: Provide a suggestion.
|
// FIXME: Provide a suggestion.
|
||||||
ConvertedBindingKind::Constraint(_bounds) => {}
|
hir::TypeBindingKind::Constraint { bounds: _ } => {}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err.span_suggestion_verbose(
|
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()) {
|
for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
|
||||||
let field_ty = tcx.type_of(field.did).instantiate_identity();
|
let field_ty = tcx.type_of(field.did).instantiate_identity();
|
||||||
if let Some(adt) = field_ty.ty_adt_def()
|
if let Some(adt) = field_ty.ty_adt_def()
|
||||||
&& !adt.is_anonymous()
|
&& !adt.is_enum()
|
||||||
&& !adt.repr().c()
|
|
||||||
{
|
{
|
||||||
let field_ty_span = tcx.def_span(adt.did());
|
if !adt.is_anonymous() && !adt.repr().c() {
|
||||||
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
|
let field_ty_span = tcx.def_span(adt.did());
|
||||||
span: tcx.def_span(field.did),
|
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
|
||||||
field_ty_span,
|
span: tcx.def_span(field.did),
|
||||||
field_ty,
|
field_ty_span,
|
||||||
field_adt_kind: adt.descr(),
|
field_ty,
|
||||||
sugg_span: field_ty_span.shrink_to_lo(),
|
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, .. })) => {
|
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
|
// 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)
|
// has any type kind other than an anonymous adt or a named adt)
|
||||||
|
|
|
@ -661,6 +661,13 @@ pub(crate) struct InvalidUnionField {
|
||||||
pub note: (),
|
pub note: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(hir_analysis_invalid_unnamed_field_ty)]
|
||||||
|
pub struct InvalidUnnamedFieldTy {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(hir_analysis_return_type_notation_on_non_rpitit)]
|
#[diag(hir_analysis_return_type_notation_on_non_rpitit)]
|
||||||
pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
|
pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
|
||||||
|
|
|
@ -293,7 +293,7 @@ pub enum HelpUseLatestEdition {
|
||||||
impl HelpUseLatestEdition {
|
impl HelpUseLatestEdition {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let edition = LATEST_STABLE_EDITION;
|
let edition = LATEST_STABLE_EDITION;
|
||||||
if std::env::var_os("CARGO").is_some() {
|
if rustc_session::utils::was_invoked_from_cargo() {
|
||||||
Self::Cargo { edition }
|
Self::Cargo { edition }
|
||||||
} else {
|
} else {
|
||||||
Self::Standalone { edition }
|
Self::Standalone { edition }
|
||||||
|
|
|
@ -492,7 +492,7 @@ fn lock_directory(
|
||||||
lock_err,
|
lock_err,
|
||||||
session_dir,
|
session_dir,
|
||||||
is_unsupported_lock,
|
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()
|
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;
|
let mut is_feature_cfg = name == sym::feature;
|
||||||
|
|
||||||
if is_feature_cfg && is_from_cargo {
|
if is_feature_cfg && is_from_cargo {
|
||||||
|
@ -340,7 +340,7 @@ pub(super) fn builtin(
|
||||||
.copied()
|
.copied()
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.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
|
// 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
|
// for names as the possibilities could be very long
|
||||||
|
|
|
@ -2545,7 +2545,7 @@ pub enum HelpUseLatestEdition {
|
||||||
impl HelpUseLatestEdition {
|
impl HelpUseLatestEdition {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let edition = LATEST_STABLE_EDITION;
|
let edition = LATEST_STABLE_EDITION;
|
||||||
if std::env::var_os("CARGO").is_some() {
|
if rustc_session::utils::was_invoked_from_cargo() {
|
||||||
Self::Cargo { edition }
|
Self::Cargo { edition }
|
||||||
} else {
|
} else {
|
||||||
Self::Standalone { edition }
|
Self::Standalone { edition }
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use crate::session::Session;
|
use crate::session::Session;
|
||||||
use rustc_data_structures::profiling::VerboseTimingGuard;
|
use rustc_data_structures::profiling::VerboseTimingGuard;
|
||||||
use rustc_fs_util::try_canonicalize;
|
use rustc_fs_util::try_canonicalize;
|
||||||
use std::path::{Path, PathBuf};
|
use std::{
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::OnceLock,
|
||||||
|
};
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> {
|
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 }
|
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)
|
if let Node::Expr(expr) = tcx.hir_node(arg_hir_id)
|
||||||
&& let Some(typeck_results) = &self.typeck_results
|
&& 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 {
|
if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr {
|
||||||
let inner_expr = expr.peel_blocks();
|
let inner_expr = expr.peel_blocks();
|
||||||
let ty = typeck_results
|
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, .. }, _)
|
if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
|
||||||
| hir::ExprKind::MethodCall(
|
| hir::ExprKind::MethodCall(
|
||||||
hir::PathSegment { ident: Ident { span, .. }, .. },
|
hir::PathSegment { ident: Ident { span, .. }, .. },
|
||||||
|
|
|
@ -443,11 +443,13 @@ pub(crate) fn build_impl(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stab) = tcx.lookup_stability(did)
|
if !tcx.features().rustc_private && !cx.render_options.force_unstable_if_unmarked {
|
||||||
&& stab.is_unstable()
|
if let Some(stab) = tcx.lookup_stability(did)
|
||||||
&& stab.feature == sym::rustc_private
|
&& stab.is_unstable()
|
||||||
{
|
&& stab.feature == sym::rustc_private
|
||||||
return;
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,8 +479,11 @@ pub(crate) fn build_impl(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stab) = tcx.lookup_stability(did) {
|
if !tcx.features().rustc_private && !cx.render_options.force_unstable_if_unmarked {
|
||||||
if stab.is_unstable() && stab.feature == sym::rustc_private {
|
if let Some(stab) = tcx.lookup_stability(did)
|
||||||
|
&& stab.is_unstable()
|
||||||
|
&& stab.feature == sym::rustc_private
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,6 +281,8 @@ pub(crate) struct RenderOptions {
|
||||||
pub(crate) no_emit_shared: bool,
|
pub(crate) no_emit_shared: bool,
|
||||||
/// If `true`, HTML source code pages won't be generated.
|
/// If `true`, HTML source code pages won't be generated.
|
||||||
pub(crate) html_no_source: bool,
|
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)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -347,6 +349,7 @@ impl Options {
|
||||||
|
|
||||||
let codegen_options = CodegenOptions::build(early_dcx, matches);
|
let codegen_options = CodegenOptions::build(early_dcx, matches);
|
||||||
let unstable_opts = UnstableOptions::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);
|
let dcx = new_dcx(error_format, None, diagnostic_width, &unstable_opts);
|
||||||
|
|
||||||
|
@ -760,6 +763,7 @@ impl Options {
|
||||||
call_locations,
|
call_locations,
|
||||||
no_emit_shared: false,
|
no_emit_shared: false,
|
||||||
html_no_source,
|
html_no_source,
|
||||||
|
force_unstable_if_unmarked,
|
||||||
};
|
};
|
||||||
Some((options, render_options))
|
Some((options, render_options))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! HTML formatting module
|
//! 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`.
|
//! various types in `rustdoc::clean`.
|
||||||
//!
|
//!
|
||||||
//! These implementations all emit HTML. As an internal implementation detail,
|
//! These implementations all emit HTML. As an internal implementation detail,
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Display, Write};
|
||||||
use std::iter::{self, once};
|
use std::iter::{self, once};
|
||||||
|
|
||||||
use rustc_ast as ast;
|
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>,
|
items: impl Iterator<Item = T>,
|
||||||
space_after_comma: bool,
|
space_after_comma: bool,
|
||||||
) -> impl fmt::Display {
|
) -> impl Display {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
for (i, item) in items.enumerate() {
|
for (i, item) in items.enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
|
write!(f, ",{}", if space_after_comma { " " } else { "" })?;
|
||||||
}
|
}
|
||||||
fmt::Display::fmt(&item, f)?;
|
item.fmt(f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -168,7 +168,7 @@ pub(crate) fn comma_sep<T: fmt::Display>(
|
||||||
pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
|
pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
|
||||||
bounds: &'a [clean::GenericBound],
|
bounds: &'a [clean::GenericBound],
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
let mut bounds_dup = FxHashSet::default();
|
let mut bounds_dup = FxHashSet::default();
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ pub(crate) fn print_generic_bounds<'a, 'tcx: 'a>(
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
f.write_str(" + ")?;
|
f.write_str(" + ")?;
|
||||||
}
|
}
|
||||||
fmt::Display::fmt(&bound.print(cx), f)?;
|
bound.print(cx).fmt(f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -186,7 +186,7 @@ impl clean::GenericParamDef {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match &self.kind {
|
display_fn(move |f| match &self.kind {
|
||||||
clean::GenericParamDefKind::Lifetime { outlives } => {
|
clean::GenericParamDefKind::Lifetime { outlives } => {
|
||||||
write!(f, "{}", self.name)?;
|
write!(f, "{}", self.name)?;
|
||||||
|
@ -207,35 +207,27 @@ impl clean::GenericParamDef {
|
||||||
f.write_str(self.name.as_str())?;
|
f.write_str(self.name.as_str())?;
|
||||||
|
|
||||||
if !bounds.is_empty() {
|
if !bounds.is_empty() {
|
||||||
if f.alternate() {
|
f.write_str(": ")?;
|
||||||
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
|
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, ": {}", print_generic_bounds(bounds, cx))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref ty) = default {
|
if let Some(ref ty) = default {
|
||||||
if f.alternate() {
|
f.write_str(" = ")?;
|
||||||
write!(f, " = {:#}", ty.print(cx))?;
|
ty.print(cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, " = {}", ty.print(cx))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
clean::GenericParamDefKind::Const { ty, default, .. } => {
|
clean::GenericParamDefKind::Const { ty, default, .. } => {
|
||||||
if f.alternate() {
|
write!(f, "const {}: ", self.name)?;
|
||||||
write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
|
ty.print(cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, "const {}: {}", self.name, ty.print(cx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(default) = default {
|
if let Some(default) = default {
|
||||||
|
f.write_str(" = ")?;
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
write!(f, " = {default:#}")?;
|
write!(f, "{default}")?;
|
||||||
} else {
|
} else {
|
||||||
write!(f, " = {default}")?;
|
write!(f, "{}", Escape(default))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,7 +241,7 @@ impl clean::Generics {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
|
let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable();
|
||||||
if real_params.peek().is_none() {
|
if real_params.peek().is_none() {
|
||||||
|
@ -279,63 +271,50 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
ending: Ending,
|
ending: Ending,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
|
let mut where_predicates = gens
|
||||||
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
|
.where_predicates
|
||||||
}).map(|pred| {
|
.iter()
|
||||||
display_fn(move |f| {
|
.map(|pred| {
|
||||||
if f.alternate() {
|
display_fn(move |f| {
|
||||||
f.write_str(" ")?;
|
if f.alternate() {
|
||||||
} else {
|
f.write_str(" ")?;
|
||||||
f.write_str("\n")?;
|
} else {
|
||||||
}
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
|
||||||
match pred {
|
match pred {
|
||||||
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
|
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
|
||||||
let ty_cx = ty.print(cx);
|
print_higher_ranked_params_with_space(bound_params, cx).fmt(f)?;
|
||||||
let generic_bounds = print_generic_bounds(bounds, cx);
|
ty.print(cx).fmt(f)?;
|
||||||
|
f.write_str(":")?;
|
||||||
if bound_params.is_empty() {
|
if !bounds.is_empty() {
|
||||||
if f.alternate() {
|
f.write_str(" ")?;
|
||||||
write!(f, "{ty_cx:#}: {generic_bounds:#}")
|
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, "{ty_cx}: {generic_bounds}")
|
|
||||||
}
|
}
|
||||||
} 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() {
|
if f.alternate() {
|
||||||
write!(
|
write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
|
||||||
f,
|
|
||||||
"for<{:#}> {ty_cx:#}: {generic_bounds:#}",
|
|
||||||
comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
write!(
|
write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
|
||||||
f,
|
|
||||||
"for<{}> {ty_cx}: {generic_bounds}",
|
|
||||||
comma_sep(bound_params.iter().map(|lt| lt.print(cx)), true)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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() {
|
if where_predicates.peek().is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -392,13 +371,13 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl clean::Lifetime {
|
impl clean::Lifetime {
|
||||||
pub(crate) fn print(&self) -> impl fmt::Display + '_ {
|
pub(crate) fn print(&self) -> impl Display + '_ {
|
||||||
self.0.as_str()
|
self.0.as_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl clean::Constant {
|
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);
|
let expr = self.expr(tcx);
|
||||||
display_fn(
|
display_fn(
|
||||||
move |f| {
|
move |f| {
|
||||||
|
@ -409,31 +388,10 @@ impl clean::Constant {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl clean::PolyTrait {
|
impl clean::PolyTrait {
|
||||||
fn print<'a, 'tcx: 'a>(
|
fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
|
||||||
&'a self,
|
|
||||||
cx: &'a Context<'tcx>,
|
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
if !self.generic_params.is_empty() {
|
print_higher_ranked_params_with_space(&self.generic_params, cx).fmt(f)?;
|
||||||
if f.alternate() {
|
self.trait_.print(cx).fmt(f)
|
||||||
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))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -442,32 +400,25 @@ impl clean::GenericBound {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match self {
|
display_fn(move |f| match self {
|
||||||
clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
|
clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
|
||||||
clean::GenericBound::TraitBound(ty, modifier) => {
|
clean::GenericBound::TraitBound(ty, modifier) => {
|
||||||
let modifier_str = match modifier {
|
f.write_str(match modifier {
|
||||||
hir::TraitBoundModifier::None => "",
|
hir::TraitBoundModifier::None => "",
|
||||||
hir::TraitBoundModifier::Maybe => "?",
|
hir::TraitBoundModifier::Maybe => "?",
|
||||||
hir::TraitBoundModifier::Negative => "!",
|
hir::TraitBoundModifier::Negative => "!",
|
||||||
// `const` and `~const` trait bounds are experimental; don't render them.
|
// `const` and `~const` trait bounds are experimental; don't render them.
|
||||||
hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "",
|
hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "",
|
||||||
};
|
})?;
|
||||||
if f.alternate() {
|
ty.print(cx).fmt(f)
|
||||||
write!(f, "{modifier_str}{ty:#}", ty = ty.print(cx))
|
|
||||||
} else {
|
|
||||||
write!(f, "{modifier_str}{ty}", ty = ty.print(cx))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl clean::GenericArgs {
|
impl clean::GenericArgs {
|
||||||
fn print<'a, 'tcx: 'a>(
|
fn print<'a, 'tcx: 'a>(&'a self, cx: &'a Context<'tcx>) -> impl Display + 'a + Captures<'tcx> {
|
||||||
&'a self,
|
|
||||||
cx: &'a Context<'tcx>,
|
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
match self {
|
match self {
|
||||||
clean::GenericArgs::AngleBracketed { args, bindings } => {
|
clean::GenericArgs::AngleBracketed { args, bindings } => {
|
||||||
|
@ -515,11 +466,7 @@ impl clean::GenericArgs {
|
||||||
f.write_str(", ")?;
|
f.write_str(", ")?;
|
||||||
}
|
}
|
||||||
comma = true;
|
comma = true;
|
||||||
if f.alternate() {
|
ty.print(cx).fmt(f)?;
|
||||||
write!(f, "{:#}", ty.print(cx))?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", ty.print(cx))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
f.write_str(")")?;
|
f.write_str(")")?;
|
||||||
if let Some(ref ty) = *output {
|
if let Some(ref ty) = *output {
|
||||||
|
@ -973,31 +920,43 @@ fn primitive_link_fragment(
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::fmt::Display::fmt(&name, f)?;
|
Display::fmt(&name, f)?;
|
||||||
if needs_termination {
|
if needs_termination {
|
||||||
write!(f, "</a>")?;
|
write!(f, "</a>")?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to render type parameters
|
|
||||||
fn tybounds<'a, 'tcx: 'a>(
|
fn tybounds<'a, 'tcx: 'a>(
|
||||||
bounds: &'a [clean::PolyTrait],
|
bounds: &'a [clean::PolyTrait],
|
||||||
lt: &'a Option<clean::Lifetime>,
|
lt: &'a Option<clean::Lifetime>,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
for (i, bound) in bounds.iter().enumerate() {
|
for (i, bound) in bounds.iter().enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
write!(f, " + ")?;
|
write!(f, " + ")?;
|
||||||
}
|
}
|
||||||
|
bound.print(cx).fmt(f)?;
|
||||||
fmt::Display::fmt(&bound.print(cx), f)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(lt) = lt {
|
if let Some(lt) = lt {
|
||||||
write!(f, " + ")?;
|
// We don't need to check `alternate` since we can be certain that
|
||||||
fmt::Display::fmt(<.print(), f)?;
|
// 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(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
@ -1007,7 +966,7 @@ pub(crate) fn anchor<'a, 'cx: 'a>(
|
||||||
did: DefId,
|
did: DefId,
|
||||||
text: Symbol,
|
text: Symbol,
|
||||||
cx: &'cx Context<'_>,
|
cx: &'cx Context<'_>,
|
||||||
) -> impl fmt::Display + 'a {
|
) -> impl Display + 'a {
|
||||||
let parts = href(did, cx);
|
let parts = href(did, cx);
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
if let Ok((url, short_ty, fqp)) = parts {
|
if let Ok((url, short_ty, fqp)) = parts {
|
||||||
|
@ -1039,7 +998,7 @@ fn fmt_type<'cx>(
|
||||||
}
|
}
|
||||||
clean::DynTrait(ref bounds, ref lt) => {
|
clean::DynTrait(ref bounds, ref lt) => {
|
||||||
f.write_str("dyn ")?;
|
f.write_str("dyn ")?;
|
||||||
fmt::Display::fmt(&tybounds(bounds, lt, cx), f)
|
tybounds(bounds, lt, cx).fmt(f)
|
||||||
}
|
}
|
||||||
clean::Infer => write!(f, "_"),
|
clean::Infer => write!(f, "_"),
|
||||||
clean::Primitive(clean::PrimitiveType::Never) => {
|
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)
|
primitive_link(f, prim, format_args!("{}", prim.as_sym().as_str()), cx)
|
||||||
}
|
}
|
||||||
clean::BareFunction(ref decl) => {
|
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() {
|
if f.alternate() {
|
||||||
write!(
|
f.write_str("fn")?;
|
||||||
f,
|
|
||||||
"{:#}{}{:#}fn{:#}",
|
|
||||||
decl.print_hrtb_with_space(cx),
|
|
||||||
decl.unsafety.print_with_space(),
|
|
||||||
print_abi_with_space(decl.abi),
|
|
||||||
decl.decl.print(cx),
|
|
||||||
)
|
|
||||||
} else {
|
} 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)?;
|
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) => {
|
clean::Tuple(ref typs) => match &typs[..] {
|
||||||
match &typs[..] {
|
&[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
|
||||||
&[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx),
|
[one] => {
|
||||||
[one] => {
|
if let clean::Generic(name) = one {
|
||||||
if let clean::Generic(name) = one {
|
primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
|
||||||
primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx)
|
} else {
|
||||||
} else {
|
write!(f, "(")?;
|
||||||
write!(f, "(")?;
|
one.print(cx).fmt(f)?;
|
||||||
// Carry `f.alternate()` into this display w/o branching manually.
|
write!(f, ",)")
|
||||||
fmt::Display::fmt(&one.print(cx), f)?;
|
|
||||||
write!(f, ",)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
many => {
|
}
|
||||||
let generic_names: Vec<Symbol> = many
|
many => {
|
||||||
.iter()
|
let generic_names: Vec<Symbol> = many
|
||||||
.filter_map(|t| match t {
|
.iter()
|
||||||
clean::Generic(name) => Some(*name),
|
.filter_map(|t| match t {
|
||||||
_ => None,
|
clean::Generic(name) => Some(*name),
|
||||||
})
|
_ => None,
|
||||||
.collect();
|
})
|
||||||
let is_generic = generic_names.len() == many.len();
|
.collect();
|
||||||
if is_generic {
|
let is_generic = generic_names.len() == many.len();
|
||||||
primitive_link(
|
if is_generic {
|
||||||
f,
|
primitive_link(
|
||||||
PrimitiveType::Tuple,
|
f,
|
||||||
format_args!(
|
PrimitiveType::Tuple,
|
||||||
"({})",
|
format_args!("({})", generic_names.iter().map(|s| s.as_str()).join(", ")),
|
||||||
generic_names.iter().map(|s| s.as_str()).join(", ")
|
cx,
|
||||||
),
|
)
|
||||||
cx,
|
} else {
|
||||||
)
|
write!(f, "(")?;
|
||||||
} else {
|
for (i, item) in many.iter().enumerate() {
|
||||||
write!(f, "(")?;
|
if i != 0 {
|
||||||
for (i, item) in many.iter().enumerate() {
|
write!(f, ", ")?;
|
||||||
if i != 0 {
|
|
||||||
write!(f, ", ")?;
|
|
||||||
}
|
|
||||||
// Carry `f.alternate()` into this display w/o branching manually.
|
|
||||||
fmt::Display::fmt(&item.print(cx), f)?;
|
|
||||||
}
|
}
|
||||||
write!(f, ")")
|
item.print(cx).fmt(f)?;
|
||||||
}
|
}
|
||||||
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
clean::Slice(ref t) => match **t {
|
clean::Slice(ref t) => match **t {
|
||||||
clean::Generic(name) => {
|
clean::Generic(name) => {
|
||||||
primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
|
primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
fmt::Display::fmt(&t.print(cx), f)?;
|
t.print(cx).fmt(f)?;
|
||||||
write!(f, "]")
|
write!(f, "]")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1135,7 +1076,7 @@ fn fmt_type<'cx>(
|
||||||
),
|
),
|
||||||
_ => {
|
_ => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
fmt::Display::fmt(&t.print(cx), f)?;
|
t.print(cx).fmt(f)?;
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
write!(f, "; {n}")?;
|
write!(f, "; {n}")?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1175,7 +1116,7 @@ fn fmt_type<'cx>(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?;
|
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 } => {
|
clean::BorrowedRef { lifetime: ref l, mutability, type_: ref ty } => {
|
||||||
|
@ -1216,11 +1157,8 @@ fn fmt_type<'cx>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
clean::ImplTrait(ref bounds) => {
|
clean::ImplTrait(ref bounds) => {
|
||||||
if f.alternate() {
|
f.write_str("impl ")?;
|
||||||
write!(f, "impl {:#}", print_generic_bounds(bounds, cx))
|
print_generic_bounds(bounds, cx).fmt(f)
|
||||||
} else {
|
|
||||||
write!(f, "impl {}", print_generic_bounds(bounds, cx))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
clean::QPath(box clean::QPathData {
|
clean::QPath(box clean::QPathData {
|
||||||
ref assoc,
|
ref assoc,
|
||||||
|
@ -1292,8 +1230,7 @@ fn fmt_type<'cx>(
|
||||||
write!(f, "{}", assoc.name)
|
write!(f, "{}", assoc.name)
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
// Carry `f.alternate()` into this display w/o branching manually.
|
assoc.args.print(cx).fmt(f)
|
||||||
fmt::Display::fmt(&assoc.args.print(cx), f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1302,7 +1239,7 @@ impl clean::Type {
|
||||||
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
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))
|
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>(
|
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
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))
|
display_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1321,20 +1258,18 @@ impl clean::Impl {
|
||||||
&'a self,
|
&'a self,
|
||||||
use_absolute: bool,
|
use_absolute: bool,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
if f.alternate() {
|
f.write_str("impl")?;
|
||||||
write!(f, "impl{:#} ", self.generics.print(cx))?;
|
self.generics.print(cx).fmt(f)?;
|
||||||
} else {
|
f.write_str(" ")?;
|
||||||
write!(f, "impl{} ", self.generics.print(cx))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref ty) = self.trait_ {
|
if let Some(ref ty) = self.trait_ {
|
||||||
match self.polarity {
|
match self.polarity {
|
||||||
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
|
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {}
|
||||||
ty::ImplPolarity::Negative => write!(f, "!")?,
|
ty::ImplPolarity::Negative => write!(f, "!")?,
|
||||||
}
|
}
|
||||||
fmt::Display::fmt(&ty.print(cx), f)?;
|
ty.print(cx).fmt(f)?;
|
||||||
write!(f, " for ")?;
|
write!(f, " for ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1359,14 +1294,9 @@ impl clean::Impl {
|
||||||
// Hardcoded anchor library/core/src/primitive_docs.rs
|
// Hardcoded anchor library/core/src/primitive_docs.rs
|
||||||
// Link should match `# Trait implementations`
|
// Link should match `# Trait implementations`
|
||||||
|
|
||||||
let hrtb = bare_fn.print_hrtb_with_space(cx);
|
print_higher_ranked_params_with_space(&bare_fn.generic_params, cx).fmt(f)?;
|
||||||
let unsafety = bare_fn.unsafety.print_with_space();
|
bare_fn.unsafety.print_with_space().fmt(f)?;
|
||||||
let abi = print_abi_with_space(bare_fn.abi);
|
print_abi_with_space(bare_fn.abi).fmt(f)?;
|
||||||
if f.alternate() {
|
|
||||||
write!(f, "{hrtb:#}{unsafety}{abi:#}",)?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{hrtb}{unsafety}{abi}",)?;
|
|
||||||
}
|
|
||||||
let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
|
let ellipsis = if bare_fn.decl.c_variadic { ", ..." } else { "" };
|
||||||
primitive_link_fragment(
|
primitive_link_fragment(
|
||||||
f,
|
f,
|
||||||
|
@ -1386,8 +1316,7 @@ impl clean::Impl {
|
||||||
fmt_type(&self.for_, f, use_absolute, cx)?;
|
fmt_type(&self.for_, f, use_absolute, cx)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::Display::fmt(&print_where_clause(&self.generics, cx, 0, Ending::Newline), f)?;
|
print_where_clause(&self.generics, cx, 0, Ending::Newline).fmt(f)
|
||||||
Ok(())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1396,16 +1325,11 @@ impl clean::Arguments {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
for (i, input) in self.values.iter().enumerate() {
|
for (i, input) in self.values.iter().enumerate() {
|
||||||
write!(f, "{}: ", input.name)?;
|
write!(f, "{}: ", input.name)?;
|
||||||
|
input.type_.print(cx).fmt(f)?;
|
||||||
if f.alternate() {
|
|
||||||
write!(f, "{:#}", input.type_.print(cx))?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", input.type_.print(cx))?;
|
|
||||||
}
|
|
||||||
if i + 1 < self.values.len() {
|
if i + 1 < self.values.len() {
|
||||||
write!(f, ", ")?;
|
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".
|
// Implements Write but only counts the bytes "written".
|
||||||
struct WriteCounter(usize);
|
struct WriteCounter(usize);
|
||||||
|
|
||||||
|
@ -1447,7 +1352,7 @@ impl std::fmt::Write for WriteCounter {
|
||||||
// Implements Display by emitting the given number of spaces.
|
// Implements Display by emitting the given number of spaces.
|
||||||
struct Indent(usize);
|
struct Indent(usize);
|
||||||
|
|
||||||
impl fmt::Display for Indent {
|
impl Display for Indent {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
(0..self.0).for_each(|_| {
|
(0..self.0).for_each(|_| {
|
||||||
f.write_char(' ').unwrap();
|
f.write_char(' ').unwrap();
|
||||||
|
@ -1460,7 +1365,7 @@ impl clean::FnDecl {
|
||||||
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
pub(crate) fn print<'b, 'a: 'b, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'b + Captures<'tcx> {
|
) -> impl Display + 'b + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
let ellipsis = if self.c_variadic { ", ..." } else { "" };
|
let ellipsis = if self.c_variadic { ", ..." } else { "" };
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
|
@ -1494,7 +1399,7 @@ impl clean::FnDecl {
|
||||||
header_len: usize,
|
header_len: usize,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
// First, generate the text form of the declaration, with no line wrapping, and count the bytes.
|
// First, generate the text form of the declaration, with no line wrapping, and count the bytes.
|
||||||
let mut counter = WriteCounter(0);
|
let mut counter = WriteCounter(0);
|
||||||
|
@ -1554,7 +1459,7 @@ impl clean::FnDecl {
|
||||||
}
|
}
|
||||||
clean::SelfExplicit(ref typ) => {
|
clean::SelfExplicit(ref typ) => {
|
||||||
write!(f, "self: ")?;
|
write!(f, "self: ")?;
|
||||||
fmt::Display::fmt(&typ.print(cx), f)?;
|
typ.print(cx).fmt(f)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1562,7 +1467,7 @@ impl clean::FnDecl {
|
||||||
write!(f, "const ")?;
|
write!(f, "const ")?;
|
||||||
}
|
}
|
||||||
write!(f, "{}: ", input.name)?;
|
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))?,
|
Some(n) => write!(f, "\n{})", Indent(n))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
fmt::Display::fmt(&self.print_output(cx), f)?;
|
self.print_output(cx).fmt(f)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_output<'a, 'tcx: 'a>(
|
fn print_output<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match &self.output {
|
display_fn(move |f| match &self.output {
|
||||||
clean::Tuple(tys) if tys.is_empty() => Ok(()),
|
clean::Tuple(tys) if tys.is_empty() => Ok(()),
|
||||||
ty if f.alternate() => {
|
ty if f.alternate() => {
|
||||||
|
@ -1600,7 +1504,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>(
|
||||||
visibility: Option<ty::Visibility<DefId>>,
|
visibility: Option<ty::Visibility<DefId>>,
|
||||||
item_did: ItemId,
|
item_did: ItemId,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
|
|
||||||
let to_print: Cow<'static, str> = match visibility {
|
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>>,
|
visibility: Option<ty::Visibility<DefId>>,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
item_did: DefId,
|
item_did: DefId,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
let to_print: Cow<'static, str> = match visibility {
|
let to_print: Cow<'static, str> = match visibility {
|
||||||
None => "".into(),
|
None => "".into(),
|
||||||
Some(ty::Visibility::Public) => "pub ".into(),
|
Some(ty::Visibility::Public) => "pub ".into(),
|
||||||
|
@ -1727,7 +1631,7 @@ impl clean::Import {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match self.kind {
|
display_fn(move |f| match self.kind {
|
||||||
clean::ImportKind::Simple(name) => {
|
clean::ImportKind::Simple(name) => {
|
||||||
if name == self.source.path.last() {
|
if name == self.source.path.last() {
|
||||||
|
@ -1751,7 +1655,7 @@ impl clean::ImportSource {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match self.did {
|
display_fn(move |f| match self.did {
|
||||||
Some(did) => resolved_path(f, did, &self.path, true, false, cx),
|
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>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
f.write_str(self.assoc.name.as_str())?;
|
f.write_str(self.assoc.name.as_str())?;
|
||||||
if f.alternate() {
|
self.assoc.args.print(cx).fmt(f)?;
|
||||||
write!(f, "{:#}", self.assoc.args.print(cx))?;
|
|
||||||
} else {
|
|
||||||
write!(f, "{}", self.assoc.args.print(cx))?;
|
|
||||||
}
|
|
||||||
match self.kind {
|
match self.kind {
|
||||||
clean::TypeBindingKind::Equality { ref term } => {
|
clean::TypeBindingKind::Equality { ref term } => {
|
||||||
if f.alternate() {
|
f.write_str(" = ")?;
|
||||||
write!(f, " = {:#}", term.print(cx))?;
|
term.print(cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, " = {}", term.print(cx))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
clean::TypeBindingKind::Constraint { ref bounds } => {
|
clean::TypeBindingKind::Constraint { ref bounds } => {
|
||||||
if !bounds.is_empty() {
|
if !bounds.is_empty() {
|
||||||
if f.alternate() {
|
f.write_str(": ")?;
|
||||||
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
|
print_generic_bounds(bounds, cx).fmt(f)?;
|
||||||
} else {
|
|
||||||
write!(f, ": {}", print_generic_bounds(bounds, cx))?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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| {
|
display_fn(move |f| {
|
||||||
let quot = if f.alternate() { "\"" } else { """ };
|
let quot = if f.alternate() { "\"" } else { """ };
|
||||||
match abi {
|
match abi {
|
||||||
|
@ -1828,34 +1722,32 @@ impl clean::GenericArg {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match self {
|
display_fn(move |f| match self {
|
||||||
clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(<.print(), f),
|
clean::GenericArg::Lifetime(lt) => lt.print().fmt(f),
|
||||||
clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
|
clean::GenericArg::Type(ty) => ty.print(cx).fmt(f),
|
||||||
clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
|
clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f),
|
||||||
clean::GenericArg::Infer => fmt::Display::fmt("_", f),
|
clean::GenericArg::Infer => Display::fmt("_", f),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl clean::types::Term {
|
impl clean::Term {
|
||||||
pub(crate) fn print<'a, 'tcx: 'a>(
|
pub(crate) fn print<'a, 'tcx: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
cx: &'a Context<'tcx>,
|
cx: &'a Context<'tcx>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
) -> impl Display + 'a + Captures<'tcx> {
|
||||||
display_fn(move |f| match self {
|
display_fn(move |f| match self {
|
||||||
clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
|
clean::Term::Type(ty) => ty.print(cx).fmt(f),
|
||||||
clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
|
clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn display_fn(
|
pub(crate) fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Display {
|
||||||
f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
|
|
||||||
) -> impl fmt::Display {
|
|
||||||
struct WithFormatter<F>(Cell<Option<F>>);
|
struct WithFormatter<F>(Cell<Option<F>>);
|
||||||
|
|
||||||
impl<F> fmt::Display for WithFormatter<F>
|
impl<F> Display for WithFormatter<F>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
|
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
|
pixels to avoid overflowing the topbar when the user sets a bigger
|
||||||
font size. */
|
font size. */
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-topbar h2 a {
|
|
||||||
display: block;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-topbar .logo-container > img {
|
.mobile-topbar .logo-container > img {
|
||||||
|
|
|
@ -45,7 +45,7 @@ function setMobileTopbar() {
|
||||||
const mobileTitle = document.createElement("h2");
|
const mobileTitle = document.createElement("h2");
|
||||||
mobileTitle.className = "location";
|
mobileTitle.className = "location";
|
||||||
if (hasClass(document.querySelector(".rustdoc"), "crate")) {
|
if (hasClass(document.querySelector(".rustdoc"), "crate")) {
|
||||||
mobileTitle.innerText = `Crate ${window.currentCrate}`;
|
mobileTitle.innerHTML = `Crate <a href="#">${window.currentCrate}</a>`;
|
||||||
} else if (locationTitle) {
|
} else if (locationTitle) {
|
||||||
mobileTitle.innerHTML = locationTitle.innerHTML;
|
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)
|
set-window-size: (600, 600)
|
||||||
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
|
go-to: "file://" + |DOC_PATH| + "/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html"
|
||||||
// It shouldn't have an overflow in the topbar either.
|
// It shouldn't have an overflow in the topbar either.
|
||||||
store-property: (".mobile-topbar h2", {"scrollWidth": scrollWidth})
|
store-property: (".mobile-topbar", {"scrollWidth": scrollWidth})
|
||||||
assert-property: (".mobile-topbar h2", {"clientWidth": |scrollWidth|})
|
assert-property: (".mobile-topbar", {"clientWidth": |scrollWidth|})
|
||||||
assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
|
assert-css: (".mobile-topbar h2", {"overflow-x": "hidden"})
|
||||||
|
|
||||||
// Check wrapping for top main-heading h1 and out-of-band.
|
// Check wrapping for top main-heading h1 and out-of-band.
|
||||||
|
|
|
@ -18,3 +18,15 @@ pub trait T2 {
|
||||||
fn f<T: Eq>()
|
fn f<T: Eq>()
|
||||||
where Self: Eq, Self: Eq2, T: Eq2;
|
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)]
|
#![allow(incomplete_features)]
|
||||||
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
|
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
|
||||||
// @has foo/struct.Ice.html '//pre[@class="rust item-decl"]' \
|
// @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]:;
|
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`
|
note: required by a bound in `File::open`
|
||||||
--> $SRC_DIR/std/src/fs.rs:LL:COL
|
--> $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
|
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::pin::Pin;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
//
|
//
|
||||||
//@ check-pass
|
//@ check-pass
|
||||||
//@ revisions: some none
|
//@ revisions: some none
|
||||||
//@ rustc-env:CARGO=/usr/bin/cargo
|
//@ rustc-env:CARGO_CRATE_NAME=foo
|
||||||
//@ compile-flags: -Z unstable-options
|
//@ compile-flags: -Z unstable-options
|
||||||
//@ [none]compile-flags: --check-cfg=cfg(feature,values())
|
//@ [none]compile-flags: --check-cfg=cfg(feature,values())
|
||||||
//@ [some]compile-flags: --check-cfg=cfg(feature,values("bitcode"))
|
//@ [some]compile-flags: --check-cfg=cfg(feature,values("bitcode"))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//@ check-pass
|
//@ check-pass
|
||||||
//@ revisions: cargo rustc
|
//@ revisions: cargo rustc
|
||||||
//@ [rustc]unset-rustc-env:CARGO
|
//@ [rustc]unset-rustc-env:CARGO_CRATE_NAME
|
||||||
//@ [cargo]rustc-env:CARGO=/usr/bin/cargo
|
//@ [cargo]rustc-env:CARGO_CRATE_NAME=foo
|
||||||
//@ compile-flags: --check-cfg=cfg(feature,values("foo")) --check-cfg=cfg(no_values) -Z unstable-options
|
//@ compile-flags: --check-cfg=cfg(feature,values("foo")) --check-cfg=cfg(no_values) -Z unstable-options
|
||||||
|
|
||||||
#[cfg(featur)]
|
#[cfg(featur)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//@ compile-flags: --target x86_64-unknown-uefi
|
//@ compile-flags: --target x86_64-unknown-uefi
|
||||||
//@ needs-llvm-components: x86
|
//@ needs-llvm-components: x86
|
||||||
//@ rustc-env:CARGO=/usr/bin/cargo
|
//@ rustc-env:CARGO_CRATE_NAME=foo
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
#![no_core]
|
#![no_core]
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
|
@ -11,6 +11,11 @@ note: required by a bound in `check_bound`
|
||||||
|
|
|
|
||||||
LL | fn check_bound<T:Copy>(_: T) {}
|
LL | fn check_bound<T:Copy>(_: T) {}
|
||||||
| ^^^^ required by this bound in `check_bound`
|
| ^^^^ 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
|
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) {}
|
LL | fn f_copy<T: Copy>(t: T) {}
|
||||||
| ^^^^ required by this bound in `f_copy`
|
| ^^^^ 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
|
error[E0277]: the trait bound `S: Clone` is not satisfied
|
||||||
--> $DIR/issue-84973-blacklist.rs:16:13
|
--> $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