Rollup merge of #94746 - notriddle:notriddle/method-rustc-on-unimplemented, r=davidtwco
diagnostics: use rustc_on_unimplemented to recommend `[].iter()` To make this work, the `#[rustc_on_unimplemented]` data needs to be used to report method resolution errors, which is most of what this commit does. Fixes #94581
This commit is contained in:
commit
e7281d08de
@ -1886,6 +1886,15 @@ pub fn is_slice(self) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_array_slice(self) -> bool {
|
||||
match self.kind() {
|
||||
Slice(_) => true,
|
||||
RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_array(self) -> bool {
|
||||
matches!(self.kind(), Array(..))
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
use super::InferCtxtPrivExt;
|
||||
|
||||
crate trait InferCtxtExt<'tcx> {
|
||||
pub trait InferCtxtExt<'tcx> {
|
||||
/*private*/
|
||||
fn impl_similar_to(
|
||||
&self,
|
||||
@ -204,6 +204,10 @@ fn on_unimplemented_note(
|
||||
flags.push((sym::_Self, Some("{integral}".to_owned())));
|
||||
}
|
||||
|
||||
if self_ty.is_array_slice() {
|
||||
flags.push((sym::_Self, Some("&[]".to_owned())));
|
||||
}
|
||||
|
||||
if let ty::Array(aty, len) = self_ty.kind() {
|
||||
flags.push((sym::_Self, Some("[]".to_owned())));
|
||||
flags.push((sym::_Self, Some(format!("[{}]", aty))));
|
||||
|
@ -19,9 +19,10 @@
|
||||
use rustc_span::lev_distance;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::{source_map, FileName, MultiSpan, Span};
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{
|
||||
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
|
||||
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
|
||||
};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
@ -483,150 +484,6 @@ pub fn report_method_error(
|
||||
}
|
||||
}
|
||||
|
||||
let mut label_span_not_found = || {
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
|
||||
let is_string_or_ref_str = match actual.kind() {
|
||||
ty::Ref(_, ty, _) => {
|
||||
ty.is_str()
|
||||
|| matches!(
|
||||
ty.kind(),
|
||||
ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did)
|
||||
)
|
||||
}
|
||||
ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did),
|
||||
_ => false,
|
||||
};
|
||||
if is_string_or_ref_str && item_name.name == sym::iter {
|
||||
err.span_suggestion_verbose(
|
||||
item_name.span,
|
||||
"because of the in-memory representation of `&str`, to obtain \
|
||||
an `Iterator` over each of its codepoint use method `chars`",
|
||||
String::from("chars"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let ty::Adt(adt, _) = rcvr_ty.kind() {
|
||||
let mut inherent_impls_candidate = self
|
||||
.tcx
|
||||
.inherent_impls(adt.did)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|def_id| {
|
||||
if let Some(assoc) = self.associated_value(*def_id, item_name) {
|
||||
// Check for both mode is the same so we avoid suggesting
|
||||
// incorrect associated item.
|
||||
match (mode, assoc.fn_has_self_parameter, source) {
|
||||
(Mode::MethodCall, true, SelfSource::MethodCall(_)) => {
|
||||
// We check that the suggest type is actually
|
||||
// different from the received one
|
||||
// So we avoid suggestion method with Box<Self>
|
||||
// for instance
|
||||
self.tcx.at(span).type_of(*def_id) != actual
|
||||
&& self.tcx.at(span).type_of(*def_id) != rcvr_ty
|
||||
}
|
||||
(Mode::Path, false, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !inherent_impls_candidate.is_empty() {
|
||||
inherent_impls_candidate.sort();
|
||||
inherent_impls_candidate.dedup();
|
||||
|
||||
// number of type to shows at most.
|
||||
let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
|
||||
let type_candidates = inherent_impls_candidate
|
||||
.iter()
|
||||
.take(limit)
|
||||
.map(|impl_item| {
|
||||
format!("- `{}`", self.tcx.at(span).type_of(*impl_item))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let additional_types = if inherent_impls_candidate.len() > limit {
|
||||
format!(
|
||||
"\nand {} more types",
|
||||
inherent_impls_candidate.len() - limit
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
err.note(&format!(
|
||||
"the {item_kind} was found for\n{}{}",
|
||||
type_candidates, additional_types
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
|
||||
}
|
||||
};
|
||||
|
||||
// If the method name is the name of a field with a function or closure type,
|
||||
// give a helping note that it has to be called as `(x.f)(...)`.
|
||||
if let SelfSource::MethodCall(expr) = source {
|
||||
let field_receiver =
|
||||
self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
|
||||
ty::Adt(def, substs) if !def.is_enum() => {
|
||||
let variant = &def.non_enum_variant();
|
||||
self.tcx.find_field_index(item_name, variant).map(|index| {
|
||||
let field = &variant.fields[index];
|
||||
let field_ty = field.ty(tcx, substs);
|
||||
(field, field_ty)
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some((field, field_ty)) = field_receiver {
|
||||
let scope = self.tcx.parent_module(self.body_id).to_def_id();
|
||||
let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
|
||||
|
||||
if is_accessible {
|
||||
if self.is_fn_ty(field_ty, span) {
|
||||
let expr_span = expr.span.to(item_name.span);
|
||||
err.multipart_suggestion(
|
||||
&format!(
|
||||
"to call the function stored in `{}`, \
|
||||
surround the field access with parentheses",
|
||||
item_name,
|
||||
),
|
||||
vec![
|
||||
(expr_span.shrink_to_lo(), '('.to_string()),
|
||||
(expr_span.shrink_to_hi(), ')'.to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
let call_expr = self
|
||||
.tcx
|
||||
.hir()
|
||||
.expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
|
||||
|
||||
if let Some(span) = call_expr.span.trim_start(item_name.span) {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"remove the arguments",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let field_kind = if is_accessible { "field" } else { "private field" };
|
||||
err.span_label(item_name.span, format!("{}, not a method", field_kind));
|
||||
} else if lev_candidate.is_none() && static_sources.is_empty() {
|
||||
label_span_not_found();
|
||||
}
|
||||
} else {
|
||||
label_span_not_found();
|
||||
}
|
||||
|
||||
if self.is_fn_ty(rcvr_ty, span) {
|
||||
fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
|
||||
err.note(
|
||||
@ -645,12 +502,15 @@ fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
|
||||
}
|
||||
}
|
||||
|
||||
let mut custom_span_label = false;
|
||||
|
||||
if !static_sources.is_empty() {
|
||||
err.note(
|
||||
"found the following associated functions; to be used as methods, \
|
||||
functions must have a `self` parameter",
|
||||
);
|
||||
err.span_label(span, "this is an associated function, not a method");
|
||||
custom_span_label = true;
|
||||
}
|
||||
if static_sources.len() == 1 {
|
||||
let ty_str = if let Some(CandidateSource::ImplSource(impl_did)) =
|
||||
@ -686,6 +546,7 @@ fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
|
||||
report_candidates(span, &mut err, static_sources, sugg_span);
|
||||
}
|
||||
|
||||
let mut bound_spans = vec![];
|
||||
let mut restrict_type_params = false;
|
||||
let mut unsatisfied_bounds = false;
|
||||
if item_name.name == sym::count && self.is_slice_ty(actual, span) {
|
||||
@ -709,7 +570,31 @@ fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
|
||||
self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id))
|
||||
};
|
||||
let mut type_params = FxHashMap::default();
|
||||
let mut bound_spans = vec![];
|
||||
|
||||
// Pick out the list of unimplemented traits on the receiver.
|
||||
// This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute.
|
||||
let mut unimplemented_traits = FxHashMap::default();
|
||||
for (predicate, _parent_pred, cause) in &unsatisfied_predicates {
|
||||
if let (ty::PredicateKind::Trait(p), Some(cause)) =
|
||||
(predicate.kind().skip_binder(), cause.as_ref())
|
||||
{
|
||||
if p.trait_ref.self_ty() != rcvr_ty {
|
||||
// This is necessary, not just to keep the errors clean, but also
|
||||
// because our derived obligations can wind up with a trait ref that
|
||||
// requires a different param_env to be correctly compared.
|
||||
continue;
|
||||
}
|
||||
unimplemented_traits.entry(p.trait_ref.def_id).or_insert((
|
||||
predicate.kind().rebind(p.trait_ref),
|
||||
Obligation {
|
||||
cause: cause.clone(),
|
||||
param_env: self.param_env,
|
||||
predicate: predicate.clone(),
|
||||
recursion_depth: 0,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut collect_type_param_suggestions =
|
||||
|self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
|
||||
@ -945,11 +830,7 @@ trait bound{s}",
|
||||
bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
|
||||
bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677
|
||||
bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order.
|
||||
bound_spans.sort();
|
||||
bound_spans.dedup();
|
||||
for (span, msg) in bound_spans.into_iter() {
|
||||
err.span_label(span, &msg);
|
||||
}
|
||||
|
||||
if !bound_list.is_empty() || !skip_list.is_empty() {
|
||||
let bound_list = bound_list
|
||||
.into_iter()
|
||||
@ -957,9 +838,34 @@ trait bound{s}",
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let actual_prefix = actual.prefix_string(self.tcx);
|
||||
err.set_primary_message(&format!(
|
||||
info!("unimplemented_traits.len() == {}", unimplemented_traits.len());
|
||||
let (primary_message, label) = if unimplemented_traits.len() == 1 {
|
||||
unimplemented_traits
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|(_, (trait_ref, obligation))| {
|
||||
if trait_ref.self_ty().references_error()
|
||||
|| actual.references_error()
|
||||
{
|
||||
// Avoid crashing.
|
||||
return (None, None);
|
||||
}
|
||||
let OnUnimplementedNote { message, label, .. } =
|
||||
self.infcx.on_unimplemented_note(trait_ref, &obligation);
|
||||
(message, label)
|
||||
})
|
||||
.unwrap_or((None, None))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let primary_message = primary_message.unwrap_or_else(|| format!(
|
||||
"the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied"
|
||||
));
|
||||
err.set_primary_message(&primary_message);
|
||||
if let Some(label) = label {
|
||||
custom_span_label = true;
|
||||
err.span_label(span, label);
|
||||
}
|
||||
if !bound_list.is_empty() {
|
||||
err.note(&format!(
|
||||
"the following trait bounds were not satisfied:\n{bound_list}"
|
||||
@ -971,6 +877,156 @@ trait bound{s}",
|
||||
}
|
||||
}
|
||||
|
||||
let mut label_span_not_found = || {
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
|
||||
let is_string_or_ref_str = match actual.kind() {
|
||||
ty::Ref(_, ty, _) => {
|
||||
ty.is_str()
|
||||
|| matches!(
|
||||
ty.kind(),
|
||||
ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did)
|
||||
)
|
||||
}
|
||||
ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did),
|
||||
_ => false,
|
||||
};
|
||||
if is_string_or_ref_str && item_name.name == sym::iter {
|
||||
err.span_suggestion_verbose(
|
||||
item_name.span,
|
||||
"because of the in-memory representation of `&str`, to obtain \
|
||||
an `Iterator` over each of its codepoint use method `chars`",
|
||||
String::from("chars"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if let ty::Adt(adt, _) = rcvr_ty.kind() {
|
||||
let mut inherent_impls_candidate = self
|
||||
.tcx
|
||||
.inherent_impls(adt.did)
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|def_id| {
|
||||
if let Some(assoc) = self.associated_value(*def_id, item_name) {
|
||||
// Check for both mode is the same so we avoid suggesting
|
||||
// incorrect associated item.
|
||||
match (mode, assoc.fn_has_self_parameter, source) {
|
||||
(Mode::MethodCall, true, SelfSource::MethodCall(_)) => {
|
||||
// We check that the suggest type is actually
|
||||
// different from the received one
|
||||
// So we avoid suggestion method with Box<Self>
|
||||
// for instance
|
||||
self.tcx.at(span).type_of(*def_id) != actual
|
||||
&& self.tcx.at(span).type_of(*def_id) != rcvr_ty
|
||||
}
|
||||
(Mode::Path, false, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !inherent_impls_candidate.is_empty() {
|
||||
inherent_impls_candidate.sort();
|
||||
inherent_impls_candidate.dedup();
|
||||
|
||||
// number of type to shows at most.
|
||||
let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
|
||||
let type_candidates = inherent_impls_candidate
|
||||
.iter()
|
||||
.take(limit)
|
||||
.map(|impl_item| {
|
||||
format!("- `{}`", self.tcx.at(span).type_of(*impl_item))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let additional_types = if inherent_impls_candidate.len() > limit {
|
||||
format!(
|
||||
"\nand {} more types",
|
||||
inherent_impls_candidate.len() - limit
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
err.note(&format!(
|
||||
"the {item_kind} was found for\n{}{}",
|
||||
type_candidates, additional_types
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
|
||||
}
|
||||
};
|
||||
|
||||
// If the method name is the name of a field with a function or closure type,
|
||||
// give a helping note that it has to be called as `(x.f)(...)`.
|
||||
if let SelfSource::MethodCall(expr) = source {
|
||||
let field_receiver =
|
||||
self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
|
||||
ty::Adt(def, substs) if !def.is_enum() => {
|
||||
let variant = &def.non_enum_variant();
|
||||
self.tcx.find_field_index(item_name, variant).map(|index| {
|
||||
let field = &variant.fields[index];
|
||||
let field_ty = field.ty(tcx, substs);
|
||||
(field, field_ty)
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
if let Some((field, field_ty)) = field_receiver {
|
||||
let scope = self.tcx.parent_module(self.body_id).to_def_id();
|
||||
let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
|
||||
|
||||
if is_accessible {
|
||||
if self.is_fn_ty(field_ty, span) {
|
||||
let expr_span = expr.span.to(item_name.span);
|
||||
err.multipart_suggestion(
|
||||
&format!(
|
||||
"to call the function stored in `{}`, \
|
||||
surround the field access with parentheses",
|
||||
item_name,
|
||||
),
|
||||
vec![
|
||||
(expr_span.shrink_to_lo(), '('.to_string()),
|
||||
(expr_span.shrink_to_hi(), ')'.to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
let call_expr = self
|
||||
.tcx
|
||||
.hir()
|
||||
.expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
|
||||
|
||||
if let Some(span) = call_expr.span.trim_start(item_name.span) {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"remove the arguments",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let field_kind = if is_accessible { "field" } else { "private field" };
|
||||
err.span_label(item_name.span, format!("{}, not a method", field_kind));
|
||||
} else if lev_candidate.is_none() && !custom_span_label {
|
||||
label_span_not_found();
|
||||
}
|
||||
} else if !custom_span_label {
|
||||
label_span_not_found();
|
||||
}
|
||||
|
||||
bound_spans.sort();
|
||||
bound_spans.dedup();
|
||||
for (span, msg) in bound_spans.into_iter() {
|
||||
err.span_label(span, &msg);
|
||||
}
|
||||
|
||||
if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
|
||||
} else {
|
||||
self.suggest_traits_to_import(
|
||||
|
@ -34,6 +34,11 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item = ()>) {}
|
||||
note = "`..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant \
|
||||
to have a bounded `RangeInclusive`: `0..=end`"
|
||||
),
|
||||
on(
|
||||
_Self = "[]",
|
||||
label = "`{Self}` is not an iterator; try calling `.into_iter()` or `.iter()`"
|
||||
),
|
||||
on(_Self = "&[]", label = "`{Self}` is not an iterator; try calling `.iter()`"),
|
||||
on(
|
||||
_Self = "&str",
|
||||
label = "`{Self}` is not an iterator; try calling `.chars()` or `.bytes()`"
|
||||
|
@ -1,8 +1,8 @@
|
||||
error[E0599]: the method `to_string` exists for raw pointer `*const u8`, but its trait bounds were not satisfied
|
||||
error[E0599]: `*const u8` doesn't implement `std::fmt::Display`
|
||||
--> $DIR/issue-21596.rs:4:22
|
||||
|
|
||||
LL | println!("{}", z.to_string());
|
||||
| ^^^^^^^^^ method cannot be called on `*const u8` due to unsatisfied trait bounds
|
||||
| ^^^^^^^^^ `*const u8` cannot be formatted with the default formatter
|
||||
|
|
||||
= note: try using `<*const T>::as_ref()` to get a reference to the type behind the pointer: https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref
|
||||
= note: using `<*const T>::as_ref()` on a pointer which is unaligned or points to invalid or uninitialized memory is undefined behavior
|
||||
|
7
src/test/ui/methods/issues/issue-94581.rs
Normal file
7
src/test/ui/methods/issues/issue-94581.rs
Normal file
@ -0,0 +1,7 @@
|
||||
fn get_slice() -> &'static [i32] {
|
||||
&[1, 2, 3, 4]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let sqsum = get_slice().map(|i| i * i).sum(); //~ ERROR [E0599]
|
||||
}
|
15
src/test/ui/methods/issues/issue-94581.stderr
Normal file
15
src/test/ui/methods/issues/issue-94581.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0599]: `&'static [i32]` is not an iterator
|
||||
--> $DIR/issue-94581.rs:6:29
|
||||
|
|
||||
LL | let sqsum = get_slice().map(|i| i * i).sum();
|
||||
| ^^^ `&'static [i32]` is not an iterator; try calling `.iter()`
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`&'static [i32]: Iterator`
|
||||
which is required by `&mut &'static [i32]: Iterator`
|
||||
`[i32]: Iterator`
|
||||
which is required by `&mut [i32]: Iterator`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
@ -16,7 +16,7 @@ fn main() {
|
||||
|
||||
let y = Foo;
|
||||
y.zero()
|
||||
.take() //~ ERROR the method
|
||||
.take() //~ ERROR not an iterator
|
||||
.one(0);
|
||||
y.three::<usize>(); //~ ERROR this function takes 3 arguments but 0 arguments were supplied
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ note: associated function defined here
|
||||
LL | fn two(self, _: isize, _: isize) -> Foo { self }
|
||||
| ^^^ ---- -------- --------
|
||||
|
||||
error[E0599]: the method `take` exists for struct `Foo`, but its trait bounds were not satisfied
|
||||
error[E0599]: `Foo` is not an iterator
|
||||
--> $DIR/method-call-err-msg.rs:19:7
|
||||
|
|
||||
LL | pub struct Foo;
|
||||
@ -50,7 +50,7 @@ LL | pub struct Foo;
|
||||
| doesn't satisfy `Foo: Iterator`
|
||||
...
|
||||
LL | .take()
|
||||
| ^^^^ method cannot be called on `Foo` due to unsatisfied trait bounds
|
||||
| ^^^^ `Foo` is not an iterator
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`Foo: Iterator`
|
||||
|
@ -5,6 +5,6 @@
|
||||
use std::iter::once;
|
||||
fn main() {
|
||||
once::<&str>("str").fuse().filter(|a: &str| true).count();
|
||||
//~^ ERROR the method
|
||||
//~^ ERROR not an iterator
|
||||
//~| ERROR type mismatch in closure arguments
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ note: required by a bound in `filter`
|
||||
LL | P: FnMut(&Self::Item) -> bool,
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `filter`
|
||||
|
||||
error[E0599]: the method `count` exists for struct `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>`, but its trait bounds were not satisfied
|
||||
error[E0599]: `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` is not an iterator
|
||||
--> $DIR/issue-36053-2.rs:7:55
|
||||
|
|
||||
LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
|
||||
| -------------- ^^^^^ method cannot be called on `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` due to unsatisfied trait bounds
|
||||
| -------------- ^^^^^ `Filter<Fuse<std::iter::Once<&str>>, [closure@$DIR/issue-36053-2.rs:7:39: 7:53]>` is not an iterator
|
||||
| |
|
||||
| doesn't satisfy `<_ as FnOnce<(&&str,)>>::Output = bool`
|
||||
| doesn't satisfy `_: FnMut<(&&str,)>`
|
||||
|
Loading…
Reference in New Issue
Block a user