mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #97778 - compiler-errors:misc-diagnostics-tidy, r=cjgillot
Tidy up miscellaneous bounds suggestions Just some small fixes to suggestions - Generalizes `Ty::is_suggestable` into a `TypeVisitor`, so that it can be called on things other than `Ty` - Makes `impl Trait` in arg position no longer suggestible (generalizing the fix in #97640) - Fixes `impl Trait` not being replaced with fresh type param when it's deeply nested in function signature (fixes #97760) - Fixes some poor handling of `where` clauses with no predicates (also #97760) - Uses `InferCtxt::resolve_numeric_literals_with_default` so we suggest `i32` instead of `{integer}` (fixes #97677) Sorry there aren't many tests the fixes. Most of them would just be duplicates of other tests with empty `where` clauses or `impl Trait` in arg position instead of generic params. Let me know if you'd want more test coverage.
This commit is contained in:
commit
37a42258ff
@ -1377,7 +1377,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
let mut params: SmallVec<[hir::GenericParam<'hir>; 4]> =
|
||||
self.lower_generic_params_mut(&generics.params).collect();
|
||||
let has_where_clause = !generics.where_clause.predicates.is_empty();
|
||||
let has_where_clause_predicates = !generics.where_clause.predicates.is_empty();
|
||||
let where_clause_span = self.lower_span(generics.where_clause.span);
|
||||
let span = self.lower_span(generics.span);
|
||||
let res = f(self);
|
||||
@ -1395,7 +1395,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let lowered_generics = self.arena.alloc(hir::Generics {
|
||||
params: self.arena.alloc_from_iter(params),
|
||||
predicates: self.arena.alloc_from_iter(predicates),
|
||||
has_where_clause,
|
||||
has_where_clause_predicates,
|
||||
where_clause_span,
|
||||
span,
|
||||
});
|
||||
|
@ -1315,7 +1315,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
generics: self.arena.alloc(hir::Generics {
|
||||
params: lifetime_defs,
|
||||
predicates: &[],
|
||||
has_where_clause: false,
|
||||
has_where_clause_predicates: false,
|
||||
where_clause_span: lctx.lower_span(span),
|
||||
span: lctx.lower_span(span),
|
||||
}),
|
||||
@ -1637,7 +1637,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
generics: this.arena.alloc(hir::Generics {
|
||||
params: generic_params,
|
||||
predicates: &[],
|
||||
has_where_clause: false,
|
||||
has_where_clause_predicates: false,
|
||||
where_clause_span: this.lower_span(span),
|
||||
span: this.lower_span(span),
|
||||
}),
|
||||
|
@ -535,7 +535,7 @@ pub struct GenericParamCount {
|
||||
pub struct Generics<'hir> {
|
||||
pub params: &'hir [GenericParam<'hir>],
|
||||
pub predicates: &'hir [WherePredicate<'hir>],
|
||||
pub has_where_clause: bool,
|
||||
pub has_where_clause_predicates: bool,
|
||||
pub where_clause_span: Span,
|
||||
pub span: Span,
|
||||
}
|
||||
@ -545,7 +545,7 @@ impl<'hir> Generics<'hir> {
|
||||
const NOPE: Generics<'_> = Generics {
|
||||
params: &[],
|
||||
predicates: &[],
|
||||
has_where_clause: false,
|
||||
has_where_clause_predicates: false,
|
||||
where_clause_span: DUMMY_SP,
|
||||
span: DUMMY_SP,
|
||||
};
|
||||
@ -581,21 +581,11 @@ impl<'hir> Generics<'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn where_clause_span(&self) -> Option<Span> {
|
||||
if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
|
||||
}
|
||||
|
||||
/// The `where_span` under normal circumstances points at either the predicates or the empty
|
||||
/// space where the `where` clause should be. Only of use for diagnostic suggestions.
|
||||
pub fn span_for_predicates_or_empty_place(&self) -> Span {
|
||||
self.where_clause_span
|
||||
}
|
||||
|
||||
/// `Span` where further predicates would be suggested, accounting for trailing commas, like
|
||||
/// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
|
||||
pub fn tail_span_for_predicate_suggestion(&self) -> Span {
|
||||
let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
|
||||
if self.has_where_clause {
|
||||
let end = self.where_clause_span.shrink_to_hi();
|
||||
if self.has_where_clause_predicates {
|
||||
self.predicates
|
||||
.iter()
|
||||
.filter(|p| p.in_where_clause())
|
||||
@ -608,6 +598,17 @@ impl<'hir> Generics<'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_where_or_trailing_comma(&self) -> &'static str {
|
||||
if self.has_where_clause_predicates {
|
||||
","
|
||||
} else if self.where_clause_span.is_empty() {
|
||||
" where"
|
||||
} else {
|
||||
// No where clause predicates, but we have `where` token
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bounds_for_param(
|
||||
&self,
|
||||
param_def_id: LocalDefId,
|
||||
|
@ -2509,11 +2509,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
labeled_user_string
|
||||
);
|
||||
let pred = format!("{}: {}", bound_kind, sub);
|
||||
let suggestion = format!(
|
||||
"{} {}",
|
||||
if !generics.predicates.is_empty() { "," } else { " where" },
|
||||
pred,
|
||||
);
|
||||
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,);
|
||||
err.span_suggestion(
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
"consider adding a where clause",
|
||||
|
@ -367,17 +367,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
.collect();
|
||||
|
||||
if !clauses.is_empty() {
|
||||
let where_clause_span = self
|
||||
.tcx
|
||||
.hir()
|
||||
.get_generics(impl_item_def_id)
|
||||
.unwrap()
|
||||
.where_clause_span
|
||||
.shrink_to_hi();
|
||||
let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
|
||||
let where_clause_span = generics.tail_span_for_predicate_suggestion();
|
||||
|
||||
let suggestion = format!(
|
||||
"{} {}",
|
||||
if !impl_predicates.is_empty() { "," } else { " where" },
|
||||
generics.add_where_or_trailing_comma(),
|
||||
clauses.join(", "),
|
||||
);
|
||||
err.span_suggestion(
|
||||
|
@ -2293,10 +2293,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
|
||||
|
||||
// If all predicates are inferable, drop the entire clause
|
||||
// (including the `where`)
|
||||
if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
|
||||
let where_span = hir_generics
|
||||
.where_clause_span()
|
||||
.expect("span of (nonempty) where clause should exist");
|
||||
if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates
|
||||
{
|
||||
let where_span = hir_generics.where_clause_span;
|
||||
// Extend the where clause back to the closing `>` of the
|
||||
// generics, except for tuple struct, which have the `where`
|
||||
// after the fields of the struct.
|
||||
|
@ -1,9 +1,10 @@
|
||||
//! Diagnostics related methods for `Ty`.
|
||||
|
||||
use crate::ty::subst::{GenericArg, GenericArgKind};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use crate::ty::{
|
||||
ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
|
||||
InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
|
||||
fold::TypeFoldable, Const, ConstKind, DefIdTree, ExistentialPredicate, InferTy,
|
||||
PolyTraitPredicate, Ty, TyCtxt, TypeSuperFoldable, TypeVisitor,
|
||||
};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
@ -72,103 +73,55 @@ impl<'tcx> Ty<'tcx> {
|
||||
_ => self.is_simple_ty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the type can be safely suggested during error recovery.
|
||||
pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
|
||||
fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
|
||||
match arg.unpack() {
|
||||
GenericArgKind::Type(ty) => ty.is_suggestable(tcx),
|
||||
GenericArgKind::Const(c) => const_is_suggestable(c.val()),
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
pub trait IsSuggestable<'tcx> {
|
||||
/// Whether this makes sense to suggest in a diagnostic.
|
||||
///
|
||||
/// We filter out certain types and constants since they don't provide
|
||||
/// meaningful rendered suggestions when pretty-printed. We leave some
|
||||
/// nonsense, such as region vars, since those render as `'_` and are
|
||||
/// usually okay to reinterpret as elided lifetimes.
|
||||
fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool;
|
||||
}
|
||||
|
||||
fn const_is_suggestable(kind: ConstKind<'_>) -> bool {
|
||||
match kind {
|
||||
ConstKind::Infer(..)
|
||||
| ConstKind::Bound(..)
|
||||
| ConstKind::Placeholder(..)
|
||||
| ConstKind::Error(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(compiler-errors): Some types are still not good to suggest,
|
||||
// specifically references with lifetimes within the function. Not
|
||||
//sure we have enough information to resolve whether a region is
|
||||
// temporary, so I'll leave this as a fixme.
|
||||
|
||||
match self.kind() {
|
||||
FnDef(..)
|
||||
| Closure(..)
|
||||
| Infer(..)
|
||||
| Generator(..)
|
||||
| GeneratorWitness(..)
|
||||
| Bound(_, _)
|
||||
| Placeholder(_)
|
||||
| Error(_) => false,
|
||||
Opaque(did, substs) => {
|
||||
let parent = tcx.parent(*did);
|
||||
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
|
||||
&& let Opaque(parent_did, _) = tcx.type_of(parent).kind()
|
||||
&& parent_did == did
|
||||
{
|
||||
substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
|
||||
ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
|
||||
substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
|
||||
}
|
||||
ExistentialPredicate::Projection(ExistentialProjection {
|
||||
substs, term, ..
|
||||
}) => {
|
||||
let term_is_suggestable = match term {
|
||||
Term::Ty(ty) => ty.is_suggestable(tcx),
|
||||
Term::Const(c) => const_is_suggestable(c.val()),
|
||||
};
|
||||
term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
|
||||
}
|
||||
_ => true,
|
||||
}),
|
||||
Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
|
||||
args.iter().all(|a| generic_arg_is_suggestible(a, tcx))
|
||||
}
|
||||
Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)),
|
||||
Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx),
|
||||
Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()),
|
||||
_ => true,
|
||||
}
|
||||
impl<'tcx, T> IsSuggestable<'tcx> for T
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
|
||||
self.visit_with(&mut IsSuggestableVisitor { tcx }).is_continue()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suggest_arbitrary_trait_bound(
|
||||
pub fn suggest_arbitrary_trait_bound<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generics: &hir::Generics<'_>,
|
||||
err: &mut Diagnostic,
|
||||
param_name: &str,
|
||||
constraint: &str,
|
||||
trait_pred: PolyTraitPredicate<'tcx>,
|
||||
) -> bool {
|
||||
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
||||
match (param, param_name) {
|
||||
(Some(_), "Self") => return false,
|
||||
_ => {}
|
||||
if !trait_pred.is_suggestable(tcx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let param_name = trait_pred.skip_binder().self_ty().to_string();
|
||||
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
|
||||
let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name);
|
||||
|
||||
// Skip, there is a param named Self
|
||||
if param.is_some() && param_name == "Self" {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suggest a where clause bound for a non-type parameter.
|
||||
let (action, prefix) = if generics.has_where_clause {
|
||||
("extending the", ", ")
|
||||
} else {
|
||||
("introducing a", " where ")
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
&format!(
|
||||
"consider {} `where` bound, but there might be an alternative better way to express \
|
||||
"consider {} `where` clause, but there might be an alternative better way to express \
|
||||
this requirement",
|
||||
action,
|
||||
if generics.where_clause_span.is_empty() { "introducing a" } else { "extending the" },
|
||||
),
|
||||
format!("{}{}: {}", prefix, param_name, constraint),
|
||||
format!("{} {}: {}", generics.add_where_or_trailing_comma(), param_name, constraint),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
true
|
||||
@ -321,7 +274,7 @@ pub fn suggest_constraining_type_params<'a>(
|
||||
continue;
|
||||
}
|
||||
|
||||
if generics.has_where_clause {
|
||||
if generics.has_where_clause_predicates {
|
||||
// This part is a bit tricky, because using the `where` clause user can
|
||||
// provide zero, one or many bounds for the same type parameter, so we
|
||||
// have following cases to consider:
|
||||
@ -463,3 +416,78 @@ impl<'v> hir::intravisit::Visitor<'v> for StaticLifetimeVisitor<'v> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IsSuggestableVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> {
|
||||
type BreakTy = ();
|
||||
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match t.kind() {
|
||||
FnDef(..)
|
||||
| Closure(..)
|
||||
| Infer(..)
|
||||
| Generator(..)
|
||||
| GeneratorWitness(..)
|
||||
| Bound(_, _)
|
||||
| Placeholder(_)
|
||||
| Error(_) => {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
Opaque(did, _) => {
|
||||
let parent = self.tcx.parent(*did);
|
||||
if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
|
||||
&& let Opaque(parent_did, _) = self.tcx.type_of(parent).kind()
|
||||
&& parent_did == did
|
||||
{
|
||||
// Okay
|
||||
} else {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
Dynamic(dty, _) => {
|
||||
for pred in *dty {
|
||||
match pred.skip_binder() {
|
||||
ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => {
|
||||
// Okay
|
||||
}
|
||||
_ => return ControlFlow::Break(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Param(param) => {
|
||||
// FIXME: It would be nice to make this not use string manipulation,
|
||||
// but it's pretty hard to do this, since `ty::ParamTy` is missing
|
||||
// sufficient info to determine if it is synthetic, and we don't
|
||||
// always have a convenient way of getting `ty::Generics` at the call
|
||||
// sites we invoke `IsSuggestable::is_suggestable`.
|
||||
if param.name.as_str().starts_with("impl ") {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
|
||||
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match c.val() {
|
||||
ConstKind::Infer(..)
|
||||
| ConstKind::Bound(..)
|
||||
| ConstKind::Placeholder(..)
|
||||
| ConstKind::Error(..) => {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
c.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,13 @@ impl GenericParamDefKind {
|
||||
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_synthetic(&self) -> bool {
|
||||
match self {
|
||||
GenericParamDefKind::Type { synthetic, .. } => *synthetic,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
|
@ -7,6 +7,7 @@ use crate::autoderef::Autoderef;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::normalize_to;
|
||||
|
||||
use hir::HirId;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_errors::{
|
||||
@ -21,11 +22,9 @@ use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
|
||||
use rustc_middle::hir::map;
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
subst::{GenericArgKind, SubstsRef},
|
||||
suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
|
||||
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, ToPredicate, Ty, TyCtxt,
|
||||
TypeFoldable,
|
||||
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
|
||||
GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
|
||||
ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
};
|
||||
use rustc_middle::ty::{TypeAndMut, TypeckResults};
|
||||
use rustc_session::Limit;
|
||||
@ -324,7 +323,7 @@ pub trait InferCtxtExt<'tcx> {
|
||||
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
|
||||
(
|
||||
generics.tail_span_for_predicate_suggestion(),
|
||||
format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
|
||||
format!("{} {}", generics.add_where_or_trailing_comma(), pred),
|
||||
)
|
||||
}
|
||||
|
||||
@ -333,35 +332,53 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, St
|
||||
/// param for cleaner code.
|
||||
fn suggest_restriction<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generics: &hir::Generics<'tcx>,
|
||||
hir_id: HirId,
|
||||
hir_generics: &hir::Generics<'tcx>,
|
||||
msg: &str,
|
||||
err: &mut Diagnostic,
|
||||
fn_sig: Option<&hir::FnSig<'_>>,
|
||||
projection: Option<&ty::ProjectionTy<'_>>,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
|
||||
) {
|
||||
// When we are dealing with a trait, `super_traits` will be `Some`:
|
||||
// Given `trait T: A + B + C {}`
|
||||
// - ^^^^^^^^^ GenericBounds
|
||||
// |
|
||||
// &Ident
|
||||
let span = generics.span_for_predicates_or_empty_place();
|
||||
if span.from_expansion() || span.desugaring_kind().is_some() {
|
||||
super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,
|
||||
) {
|
||||
if hir_generics.where_clause_span.from_expansion()
|
||||
|| hir_generics.where_clause_span.desugaring_kind().is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
let Some(item_id) = hir_id.as_owner() else { return; };
|
||||
let generics = tcx.generics_of(item_id);
|
||||
// Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
|
||||
if let Some((bound_str, fn_sig)) =
|
||||
if let Some((param, bound_str, fn_sig)) =
|
||||
fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() {
|
||||
// Shenanigans to get the `Trait` from the `impl Trait`.
|
||||
ty::Param(param) => {
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^^^^^ get this string
|
||||
param.name.as_str().strip_prefix("impl").map(|s| (s.trim_start().to_string(), sig))
|
||||
let param_def = generics.type_param(param, tcx);
|
||||
if param_def.kind.is_synthetic() {
|
||||
let bound_str =
|
||||
param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string();
|
||||
return Some((param_def, bound_str, sig));
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str));
|
||||
let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder {
|
||||
tcx,
|
||||
param,
|
||||
replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))
|
||||
.to_ty(tcx),
|
||||
});
|
||||
if !trait_pred.is_suggestable(tcx) {
|
||||
return;
|
||||
}
|
||||
// We know we have an `impl Trait` that doesn't satisfy a required projection.
|
||||
|
||||
// Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'
|
||||
@ -370,40 +387,22 @@ fn suggest_restriction<'tcx>(
|
||||
// but instead we choose to suggest replacing all instances of `impl Trait` with `T`
|
||||
// where `T: Trait`.
|
||||
let mut ty_spans = vec![];
|
||||
let impl_trait_str = format!("impl {}", bound_str);
|
||||
for input in fn_sig.decl.inputs {
|
||||
if let hir::TyKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
hir::Path { segments: [segment], .. },
|
||||
)) = input.kind
|
||||
{
|
||||
if segment.ident.as_str() == impl_trait_str.as_str() {
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^^^^^^^^^^ get this to suggest `T` instead
|
||||
|
||||
// There might be more than one `impl Trait`.
|
||||
ty_spans.push(input.span);
|
||||
}
|
||||
}
|
||||
ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }
|
||||
.visit_ty(input);
|
||||
}
|
||||
|
||||
let type_param_name = generics.params.next_type_param_name(Some(&bound_str));
|
||||
// The type param `T: Trait` we will suggest to introduce.
|
||||
let type_param = format!("{}: {}", type_param_name, bound_str);
|
||||
|
||||
// FIXME: modify the `trait_pred` instead of string shenanigans.
|
||||
// Turn `<impl Trait as Foo>::Bar: Qux` into `<T as Foo>::Bar: Qux`.
|
||||
let pred = trait_pred.to_predicate(tcx).to_string();
|
||||
let pred = pred.replace(&impl_trait_str, &type_param_name);
|
||||
let mut sugg = vec![
|
||||
if let Some(span) = generics.span_for_param_suggestion() {
|
||||
if let Some(span) = hir_generics.span_for_param_suggestion() {
|
||||
(span, format!(", {}", type_param))
|
||||
} else {
|
||||
(generics.span, format!("<{}>", type_param))
|
||||
(hir_generics.span, format!("<{}>", type_param))
|
||||
},
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^ suggest `where <T as Trait>::A: Bound`
|
||||
predicate_constraint(generics, pred),
|
||||
predicate_constraint(hir_generics, trait_pred.to_predicate(tcx).to_string()),
|
||||
];
|
||||
sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));
|
||||
|
||||
@ -416,15 +415,20 @@ fn suggest_restriction<'tcx>(
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
if !trait_pred.is_suggestable(tcx) {
|
||||
return;
|
||||
}
|
||||
// Trivial case: `T` needs an extra bound: `T: Bound`.
|
||||
let (sp, suggestion) = match (
|
||||
generics
|
||||
hir_generics
|
||||
.params
|
||||
.iter()
|
||||
.find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })),
|
||||
super_traits,
|
||||
) {
|
||||
(_, None) => predicate_constraint(generics, trait_pred.to_predicate(tcx).to_string()),
|
||||
(_, None) => {
|
||||
predicate_constraint(hir_generics, trait_pred.to_predicate(tcx).to_string())
|
||||
}
|
||||
(None, Some((ident, []))) => (
|
||||
ident.span.shrink_to_hi(),
|
||||
format!(": {}", trait_pred.print_modifiers_and_trait_path()),
|
||||
@ -434,7 +438,7 @@ fn suggest_restriction<'tcx>(
|
||||
format!(" + {}", trait_pred.print_modifiers_and_trait_path()),
|
||||
),
|
||||
(Some(_), Some((_, []))) => (
|
||||
generics.span.shrink_to_hi(),
|
||||
hir_generics.span.shrink_to_hi(),
|
||||
format!(": {}", trait_pred.print_modifiers_and_trait_path()),
|
||||
),
|
||||
};
|
||||
@ -455,6 +459,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
body_id: hir::HirId,
|
||||
) {
|
||||
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
|
||||
|
||||
let self_ty = trait_pred.skip_binder().self_ty();
|
||||
let (param_ty, projection) = match self_ty.kind() {
|
||||
ty::Param(_) => (true, None),
|
||||
@ -462,16 +468,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
_ => (false, None),
|
||||
};
|
||||
|
||||
let generic_args_have_impl_trait = |args: SubstsRef<'tcx>| -> bool {
|
||||
args.iter().any(|arg| match arg.unpack() {
|
||||
GenericArgKind::Type(ty) => match ty.kind() {
|
||||
ty::Param(param) => param.name.as_str().starts_with("impl"),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
};
|
||||
|
||||
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
|
||||
// don't suggest `T: Sized + ?Sized`.
|
||||
let mut hir_id = body_id;
|
||||
@ -486,6 +482,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
// Restricting `Self` for a single method.
|
||||
suggest_restriction(
|
||||
self.tcx,
|
||||
hir_id,
|
||||
&generics,
|
||||
"`Self`",
|
||||
err,
|
||||
@ -505,7 +502,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
assert!(param_ty);
|
||||
// Restricting `Self` for a single method.
|
||||
suggest_restriction(
|
||||
self.tcx, &generics, "`Self`", err, None, projection, trait_pred, None,
|
||||
self.tcx, hir_id, &generics, "`Self`", err, None, projection, trait_pred,
|
||||
None,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -526,6 +524,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
// Missing restriction on associated type of type parameter (unmet projection).
|
||||
suggest_restriction(
|
||||
self.tcx,
|
||||
hir_id,
|
||||
&generics,
|
||||
"the associated type",
|
||||
err,
|
||||
@ -545,6 +544,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
// Missing restriction on associated type of type parameter (unmet projection).
|
||||
suggest_restriction(
|
||||
self.tcx,
|
||||
hir_id,
|
||||
&generics,
|
||||
"the associated type",
|
||||
err,
|
||||
@ -573,6 +573,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
| hir::Node::ImplItem(hir::ImplItem { generics, .. })
|
||||
if param_ty =>
|
||||
{
|
||||
// We skip the 0'th subst (self) because we do not want
|
||||
// to consider the predicate as not suggestible if the
|
||||
// self type is an arg position `impl Trait` -- instead,
|
||||
// we handle that by adding ` + Bound` below.
|
||||
// FIXME(compiler-errors): It would be nice to do the same
|
||||
// this that we do in `suggest_restriction` and pull the
|
||||
// `impl Trait` into a new generic if it shows up somewhere
|
||||
// else in the predicate.
|
||||
if !trait_pred.skip_binder().trait_ref.substs[1..]
|
||||
.iter()
|
||||
.all(|g| g.is_suggestable(self.tcx))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Missing generic type parameter bound.
|
||||
let param_name = self_ty.to_string();
|
||||
let constraint = with_no_trimmed_paths!(
|
||||
@ -602,13 +616,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
| hir::ItemKind::TraitAlias(generics, _)
|
||||
| hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
|
||||
..
|
||||
}) if !param_ty
|
||||
&& !generic_args_have_impl_trait(trait_pred.skip_binder().trait_ref.substs) =>
|
||||
{
|
||||
}) if !param_ty => {
|
||||
// Missing generic type parameter bound.
|
||||
let param_name = self_ty.to_string();
|
||||
let constraint = trait_pred.print_modifiers_and_trait_path().to_string();
|
||||
if suggest_arbitrary_trait_bound(generics, &mut err, ¶m_name, &constraint) {
|
||||
if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -2982,3 +2992,52 @@ fn suggest_trait_object_return_type_alternatives(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect the spans that we see the generic param `param_did`
|
||||
struct ReplaceImplTraitVisitor<'a> {
|
||||
ty_spans: &'a mut Vec<Span>,
|
||||
param_did: DefId,
|
||||
}
|
||||
|
||||
impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> {
|
||||
fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) {
|
||||
if let hir::TyKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
hir::Path { res: hir::def::Res::Def(_, segment_did), .. },
|
||||
)) = t.kind
|
||||
{
|
||||
if self.param_did == *segment_did {
|
||||
// `fn foo(t: impl Trait)`
|
||||
// ^^^^^^^^^^ get this to suggest `T` instead
|
||||
|
||||
// There might be more than one `impl Trait`.
|
||||
self.ty_spans.push(t.span);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hir::intravisit::walk_ty(self, t);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace `param` with `replace_ty`
|
||||
struct ReplaceImplTraitFolder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param: &'tcx ty::GenericParamDef,
|
||||
replace_ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<'tcx> for ReplaceImplTraitFolder<'tcx> {
|
||||
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let ty::Param(ty::ParamTy { index, .. }) = t.kind() {
|
||||
if self.param.index == *index {
|
||||
return self.replace_ty;
|
||||
}
|
||||
}
|
||||
t.super_fold_with(self)
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::GenericArg;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_middle::ty::{
|
||||
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt,
|
||||
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
|
||||
};
|
||||
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
|
||||
use rustc_span::{symbol::kw, Span};
|
||||
|
@ -27,7 +27,9 @@ use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
|
||||
use rustc_middle::middle::stability::AllowUnstable;
|
||||
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef};
|
||||
use rustc_middle::ty::GenericParamDefKind;
|
||||
use rustc_middle::ty::{self, Const, DefIdTree, EarlyBinder, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_middle::ty::{
|
||||
self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeFoldable,
|
||||
};
|
||||
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::lev_distance::find_best_match_for_name;
|
||||
|
@ -15,7 +15,7 @@ use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||
use rustc_infer::traits;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
|
||||
use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
@ -538,10 +538,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
|
||||
if let Some(g) = kind.generics() {
|
||||
let key = match g.predicates {
|
||||
[.., pred] => (pred.span().shrink_to_hi(), false),
|
||||
[] => (g.span_for_predicates_or_empty_place(), true),
|
||||
};
|
||||
let key = (
|
||||
g.tail_span_for_predicate_suggestion(),
|
||||
g.add_where_or_trailing_comma(),
|
||||
);
|
||||
type_params
|
||||
.entry(key)
|
||||
.or_insert_with(FxHashSet::default)
|
||||
@ -805,7 +805,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.enumerate()
|
||||
.collect::<Vec<(usize, String)>>();
|
||||
|
||||
for ((span, empty_where), obligations) in type_params.into_iter() {
|
||||
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
|
||||
restrict_type_params = true;
|
||||
// #74886: Sort here so that the output is always the same.
|
||||
let mut obligations = obligations.into_iter().collect::<Vec<_>>();
|
||||
@ -817,11 +817,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
trait bound{s}",
|
||||
s = pluralize!(obligations.len())
|
||||
),
|
||||
format!(
|
||||
"{} {}",
|
||||
if empty_where { " where" } else { "," },
|
||||
obligations.join(", ")
|
||||
),
|
||||
format!("{} {}", add_where_or_comma, obligations.join(", ")),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
@ -421,7 +421,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
|
||||
|
||||
let suggestion = format!(
|
||||
"{} {}",
|
||||
if !gat_item_hir.generics.predicates.is_empty() { "," } else { " where" },
|
||||
gat_item_hir.generics.add_where_or_trailing_comma(),
|
||||
unsatisfied_bounds.join(", "),
|
||||
);
|
||||
err.span_suggestion(
|
||||
|
@ -39,7 +39,7 @@ use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::subst::InternalSubsts;
|
||||
use rustc_middle::ty::util::Discr;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ReprOptions, ToPredicate};
|
||||
use rustc_session::lint;
|
||||
use rustc_session::parse::feature_err;
|
||||
|
@ -211,7 +211,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
|
||||
match tcx.hir().find(hir_id) {
|
||||
Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
|
||||
generics.where_clause_span()
|
||||
Some(generics.where_clause_span)
|
||||
}
|
||||
_ => {
|
||||
span_bug!(tcx.def_span(def_id), "main has a non-function type");
|
||||
@ -401,14 +401,17 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
|
||||
.emit();
|
||||
error = true;
|
||||
}
|
||||
if let Some(sp) = generics.where_clause_span() {
|
||||
if generics.has_where_clause_predicates {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
sp,
|
||||
generics.where_clause_span,
|
||||
E0647,
|
||||
"start function is not allowed to have a `where` clause"
|
||||
)
|
||||
.span_label(sp, "start function cannot have a `where` clause")
|
||||
.span_label(
|
||||
generics.where_clause_span,
|
||||
"start function cannot have a `where` clause",
|
||||
)
|
||||
.emit();
|
||||
error = true;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ LL | a.iter().map(|a| a*a)
|
||||
| |
|
||||
| &T
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
|
||||
| +++++++++++++++++
|
||||
|
@ -2,6 +2,11 @@ fn foo<T: PartialEq>(a: &T, b: T) {
|
||||
a == b; //~ ERROR E0277
|
||||
}
|
||||
|
||||
fn foo2<T: PartialEq>(a: &T, b: T) where {
|
||||
a == b; //~ ERROR E0277
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo(&1, 1);
|
||||
foo2(&1, 1);
|
||||
}
|
||||
|
@ -5,11 +5,23 @@ LL | a == b;
|
||||
| ^^ no implementation for `&T == T`
|
||||
|
|
||||
= help: the trait `PartialEq<T>` is not implemented for `&T`
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn foo<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
||||
| ++++++++++++++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0277]: can't compare `&T` with `T`
|
||||
--> $DIR/partialeq_help.rs:6:7
|
||||
|
|
||||
LL | a == b;
|
||||
| ^^ no implementation for `&T == T`
|
||||
|
|
||||
= help: the trait `PartialEq<T>` is not implemented for `&T`
|
||||
help: consider extending the `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn foo2<T: PartialEq>(a: &T, b: T) where &T: PartialEq<T> {
|
||||
| ++++++++++++++++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
@ -15,7 +15,7 @@ note: required by a bound in `Foo::Bar`
|
||||
|
|
||||
LL | type Bar: ~const std::ops::Add;
|
||||
| ^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::Bar`
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | impl const Foo for NonConstAdd where NonConstAdd: ~const Add {
|
||||
| +++++++++++++++++++++++++++++
|
||||
|
@ -14,7 +14,7 @@ note: required by a bound in `foo`
|
||||
|
|
||||
LL | const fn foo<T>() where T: ~const Tr {}
|
||||
| ^^^^^^^^^ required by this bound in `foo`
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | pub trait Foo where (): ~const Tr {
|
||||
| +++++++++++++++++++
|
||||
|
@ -20,7 +20,7 @@ note: required by a bound in `X::U`
|
||||
|
|
||||
LL | type U: PartialEq<T>;
|
||||
| ^^^^^^^^^^^^ required by this bound in `X::U`
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
|
||||
| ++++++++++++++++++++++++++++++
|
||||
|
@ -13,7 +13,7 @@ help: consider annotating `a::Inner<T>` with `#[derive(Debug)]`
|
||||
|
|
||||
LL | #[derive(Debug)]
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | struct Outer<T>(Inner<T>) where a::Inner<T>: Debug;
|
||||
| ++++++++++++++++++++++++
|
||||
|
@ -15,7 +15,7 @@ help: consider annotating `S<T>` with `#[derive(PartialEq)]`
|
||||
|
|
||||
LL | #[derive(PartialEq)]
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
|
||||
| +++++++++++++++++++++
|
||||
|
6
src/test/ui/suggestions/issue-97677.rs
Normal file
6
src/test/ui/suggestions/issue-97677.rs
Normal file
@ -0,0 +1,6 @@
|
||||
fn add_ten<N>(n: N) -> N {
|
||||
n + 10
|
||||
//~^ ERROR cannot add `{integer}` to `N`
|
||||
}
|
||||
|
||||
fn main() {}
|
16
src/test/ui/suggestions/issue-97677.stderr
Normal file
16
src/test/ui/suggestions/issue-97677.stderr
Normal file
@ -0,0 +1,16 @@
|
||||
error[E0369]: cannot add `{integer}` to `N`
|
||||
--> $DIR/issue-97677.rs:2:7
|
||||
|
|
||||
LL | n + 10
|
||||
| - ^ -- {integer}
|
||||
| |
|
||||
| N
|
||||
|
|
||||
help: consider restricting type parameter `N`
|
||||
|
|
||||
LL | fn add_ten<N: std::ops::Add<i32>>(n: N) -> N {
|
||||
| ++++++++++++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0369`.
|
9
src/test/ui/suggestions/issue-97760.rs
Normal file
9
src/test/ui/suggestions/issue-97760.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub fn print_values(values: &impl IntoIterator)
|
||||
where {
|
||||
for x in values.into_iter() {
|
||||
println!("{x}");
|
||||
//~^ ERROR <impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
18
src/test/ui/suggestions/issue-97760.stderr
Normal file
18
src/test/ui/suggestions/issue-97760.stderr
Normal file
@ -0,0 +1,18 @@
|
||||
error[E0277]: `<impl IntoIterator as IntoIterator>::Item` doesn't implement `std::fmt::Display`
|
||||
--> $DIR/issue-97760.rs:4:20
|
||||
|
|
||||
LL | println!("{x}");
|
||||
| ^ `<impl IntoIterator as IntoIterator>::Item` cannot be formatted with the default formatter
|
||||
|
|
||||
= help: the trait `std::fmt::Display` is not implemented for `<impl IntoIterator as IntoIterator>::Item`
|
||||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
|
||||
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
||||
|
|
||||
LL ~ pub fn print_values<I: IntoIterator>(values: &I)
|
||||
LL ~ where <I as IntoIterator>::Item: std::fmt::Display {
|
||||
|
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
|
||||
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` bound
|
||||
fn issue_81421<T: Read + Write>(mut stream: T) { //~ HELP consider introducing a `where` clause
|
||||
let initial_message = format!("Hello world");
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
let bytes_written = stream.write_all(initial_message.as_bytes());
|
||||
|
@ -16,7 +16,7 @@ help: consider removing the leading `&`-reference
|
||||
LL - let mut stream_reader = BufReader::new(&stream);
|
||||
LL + let mut stream_reader = BufReader::new(stream);
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn issue_81421<T: Read + Write>(mut stream: T) where &T: std::io::Read {
|
||||
| +++++++++++++++++++++++
|
||||
|
@ -6,7 +6,7 @@ LL | a * b
|
||||
| |
|
||||
| &T
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
|
||||
| ++++++++++++++++++
|
||||
|
@ -49,7 +49,7 @@ error[E0277]: the trait bound `u64: From<T>` is not satisfied
|
||||
LL | <u64 as From<T>>::from;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ the trait `From<T>` is not implemented for `u64`
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<T> {
|
||||
| ++++++++++++++++++
|
||||
@ -60,7 +60,7 @@ error[E0277]: the trait bound `u64: From<<T as Iterator>::Item>` is not satisfie
|
||||
LL | <u64 as From<<T as Iterator>::Item>>::from;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<<T as Iterator>::Item>` is not implemented for `u64`
|
||||
|
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn check<T: Iterator, U: ?Sized>() where u64: From<<T as Iterator>::Item> {
|
||||
| ++++++++++++++++++++++++++++++++++++++
|
||||
|
@ -5,7 +5,7 @@ LL | (a, a)
|
||||
| ^ the trait `From<&A>` is not implemented for `&'static B`
|
||||
|
|
||||
= note: required because of the requirements on the impl of `Into<&'static B>` for `&A`
|
||||
help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | fn f<A, B: 'static>(a: &'static A, b: B) -> (X<A, B>, X<B, A>) where &'static B: From<&A> {
|
||||
| ++++++++++++++++++++++++++
|
||||
|
Loading…
Reference in New Issue
Block a user