Skip to content

Commit e5fde96

Browse files
authored
Rollup merge of rust-lang#105523 - estebank:suggest-collect-vec, r=compiler-errors
Suggest `collect`ing into `Vec<_>` Fix rust-lang#105510.
2 parents c8fd654 + 40a6275 commit e5fde96

31 files changed

+168
-151
lines changed

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

+66-38
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
2020
use rustc_middle::ty::{self, DefIdTree, InferConst};
2121
use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef};
2222
use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
23-
use rustc_span::symbol::{kw, Ident};
23+
use rustc_span::symbol::{kw, sym, Ident};
2424
use rustc_span::{BytePos, Span};
2525
use std::borrow::Cow;
2626
use std::iter;
@@ -79,7 +79,7 @@ impl InferenceDiagnosticsData {
7979

8080
fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
8181
if in_type.is_ty_infer() {
82-
"empty"
82+
""
8383
} else if self.name == "_" {
8484
// FIXME: Consider specializing this message if there is a single `_`
8585
// in the type.
@@ -183,13 +183,24 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte
183183
printer
184184
}
185185

186-
fn ty_to_string<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
186+
fn ty_to_string<'tcx>(
187+
infcx: &InferCtxt<'tcx>,
188+
ty: Ty<'tcx>,
189+
called_method_def_id: Option<DefId>,
190+
) -> String {
187191
let printer = fmt_printer(infcx, Namespace::TypeNS);
188192
let ty = infcx.resolve_vars_if_possible(ty);
189-
match ty.kind() {
193+
match (ty.kind(), called_method_def_id) {
190194
// We don't want the regular output for `fn`s because it includes its path in
191195
// invalid pseudo-syntax, we want the `fn`-pointer output instead.
192-
ty::FnDef(..) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
196+
(ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
197+
(_, Some(def_id))
198+
if ty.is_ty_infer()
199+
&& infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
200+
{
201+
"Vec<_>".to_string()
202+
}
203+
_ if ty.is_ty_infer() => "/* Type */".to_string(),
193204
// FIXME: The same thing for closures, but this only works when the closure
194205
// does not capture anything.
195206
//
@@ -213,15 +224,15 @@ fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
213224
.map(|args| {
214225
args.tuple_fields()
215226
.iter()
216-
.map(|arg| ty_to_string(infcx, arg))
227+
.map(|arg| ty_to_string(infcx, arg, None))
217228
.collect::<Vec<_>>()
218229
.join(", ")
219230
})
220231
.unwrap_or_default();
221232
let ret = if fn_sig.output().skip_binder().is_unit() {
222233
String::new()
223234
} else {
224-
format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder()))
235+
format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None))
225236
};
226237
format!("fn({}){}", args, ret)
227238
}
@@ -368,6 +379,7 @@ impl<'tcx> InferCtxt<'tcx> {
368379
}
369380

370381
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
382+
#[instrument(level = "debug", skip(self, error_code))]
371383
pub fn emit_inference_failure_err(
372384
&self,
373385
body_id: Option<hir::BodyId>,
@@ -406,7 +418,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
406418
let mut infer_subdiags = Vec::new();
407419
let mut multi_suggestions = Vec::new();
408420
match kind {
409-
InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
421+
InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => {
410422
infer_subdiags.push(SourceKindSubdiag::LetLike {
411423
span: insert_span,
412424
name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
@@ -415,7 +427,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
415427
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
416428
arg_name: arg_data.name,
417429
kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
418-
type_name: ty_to_string(self, ty),
430+
type_name: ty_to_string(self, ty, def_id),
419431
});
420432
}
421433
InferSourceKind::ClosureArg { insert_span, ty } => {
@@ -427,7 +439,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
427439
prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
428440
arg_name: arg_data.name,
429441
kind: "closure",
430-
type_name: ty_to_string(self, ty),
442+
type_name: ty_to_string(self, ty, None),
431443
});
432444
}
433445
InferSourceKind::GenericArg {
@@ -456,33 +468,39 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
456468
parent_name,
457469
});
458470

459-
let args = fmt_printer(self, Namespace::TypeNS)
460-
.comma_sep(generic_args.iter().copied().map(|arg| {
461-
if arg.is_suggestable(self.tcx, true) {
462-
return arg;
463-
}
471+
let args = if self.infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn)
472+
== Some(generics_def_id)
473+
{
474+
"Vec<_>".to_string()
475+
} else {
476+
fmt_printer(self, Namespace::TypeNS)
477+
.comma_sep(generic_args.iter().copied().map(|arg| {
478+
if arg.is_suggestable(self.tcx, true) {
479+
return arg;
480+
}
464481

465-
match arg.unpack() {
466-
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
467-
GenericArgKind::Type(_) => self
468-
.next_ty_var(TypeVariableOrigin {
469-
span: rustc_span::DUMMY_SP,
470-
kind: TypeVariableOriginKind::MiscVariable,
471-
})
472-
.into(),
473-
GenericArgKind::Const(arg) => self
474-
.next_const_var(
475-
arg.ty(),
476-
ConstVariableOrigin {
482+
match arg.unpack() {
483+
GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
484+
GenericArgKind::Type(_) => self
485+
.next_ty_var(TypeVariableOrigin {
477486
span: rustc_span::DUMMY_SP,
478-
kind: ConstVariableOriginKind::MiscVariable,
479-
},
480-
)
481-
.into(),
482-
}
483-
}))
484-
.unwrap()
485-
.into_buffer();
487+
kind: TypeVariableOriginKind::MiscVariable,
488+
})
489+
.into(),
490+
GenericArgKind::Const(arg) => self
491+
.next_const_var(
492+
arg.ty(),
493+
ConstVariableOrigin {
494+
span: rustc_span::DUMMY_SP,
495+
kind: ConstVariableOriginKind::MiscVariable,
496+
},
497+
)
498+
.into(),
499+
}
500+
}))
501+
.unwrap()
502+
.into_buffer()
503+
};
486504

487505
if !have_turbofish {
488506
infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
@@ -520,7 +538,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
520538
));
521539
}
522540
InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
523-
let ty_info = ty_to_string(self, ty);
541+
let ty_info = ty_to_string(self, ty, None);
524542
multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return(
525543
ty_info,
526544
data,
@@ -608,6 +626,7 @@ enum InferSourceKind<'tcx> {
608626
insert_span: Span,
609627
pattern_name: Option<Ident>,
610628
ty: Ty<'tcx>,
629+
def_id: Option<DefId>,
611630
},
612631
ClosureArg {
613632
insert_span: Span,
@@ -662,7 +681,7 @@ impl<'tcx> InferSourceKind<'tcx> {
662681
if ty.is_closure() {
663682
("closure", closure_as_fn_str(infcx, ty))
664683
} else if !ty.is_ty_infer() {
665-
("normal", ty_to_string(infcx, ty))
684+
("normal", ty_to_string(infcx, ty, None))
666685
} else {
667686
("other", String::new())
668687
}
@@ -788,10 +807,18 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
788807
/// Uses `fn source_cost` to determine whether this inference source is preferable to
789808
/// previous sources. We generally prefer earlier sources.
790809
#[instrument(level = "debug", skip(self))]
791-
fn update_infer_source(&mut self, new_source: InferSource<'tcx>) {
810+
fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) {
792811
let cost = self.source_cost(&new_source) + self.attempt;
793812
debug!(?cost);
794813
self.attempt += 1;
814+
if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, ..}, .. }) = self.infer_source
815+
&& let InferSourceKind::LetBinding { ref ty, ref mut def_id, ..} = new_source.kind
816+
&& ty.is_ty_infer()
817+
{
818+
// Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of
819+
// `let x: _ = iter.collect();`, as this is a very common case.
820+
*def_id = Some(did);
821+
}
795822
if cost < self.infer_source_cost {
796823
self.infer_source_cost = cost;
797824
self.infer_source = Some(new_source);
@@ -1092,6 +1119,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
10921119
insert_span: local.pat.span.shrink_to_hi(),
10931120
pattern_name: local.pat.simple_ident(),
10941121
ty,
1122+
def_id: None,
10951123
},
10961124
})
10971125
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ symbols! {
827827
item_like_imports,
828828
iter,
829829
iter_repeat,
830+
iterator_collect_fn,
830831
kcfi,
831832
keyword,
832833
kind,

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+23-56
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_middle::ty::{
4242
};
4343
use rustc_session::Limit;
4444
use rustc_span::def_id::LOCAL_CRATE;
45-
use rustc_span::symbol::{kw, sym};
45+
use rustc_span::symbol::sym;
4646
use rustc_span::{ExpnKind, Span, DUMMY_SP};
4747
use std::fmt;
4848
use std::iter;
@@ -980,6 +980,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
980980
trait_ref,
981981
obligation.cause.body_id,
982982
&mut err,
983+
true,
983984
) {
984985
// This is *almost* equivalent to
985986
// `obligation.cause.code().peel_derives()`, but it gives us the
@@ -1015,6 +1016,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
10151016
trait_ref,
10161017
obligation.cause.body_id,
10171018
&mut err,
1019+
true,
10181020
);
10191021
}
10201022
}
@@ -1434,6 +1436,7 @@ trait InferCtxtPrivExt<'tcx> {
14341436
trait_ref: ty::PolyTraitRef<'tcx>,
14351437
body_id: hir::HirId,
14361438
err: &mut Diagnostic,
1439+
other: bool,
14371440
) -> bool;
14381441

14391442
/// Gets the parent trait chain start
@@ -1888,7 +1891,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
18881891
trait_ref: ty::PolyTraitRef<'tcx>,
18891892
body_id: hir::HirId,
18901893
err: &mut Diagnostic,
1894+
other: bool,
18911895
) -> bool {
1896+
let other = if other { "other " } else { "" };
18921897
let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
18931898
candidates.sort();
18941899
candidates.dedup();
@@ -1939,7 +1944,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
19391944
candidates.dedup();
19401945
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
19411946
err.help(&format!(
1942-
"the following other types implement trait `{}`:{}{}",
1947+
"the following {other}types implement trait `{}`:{}{}",
19431948
trait_ref.print_only_trait_path(),
19441949
candidates[..end].join(""),
19451950
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
@@ -2180,14 +2185,26 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
21802185
trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
21812186
// It doesn't make sense to talk about applicable impls if there are more
21822187
// than a handful of them.
2183-
if impls.len() > 1 && impls.len() < 5 && has_non_region_infer {
2188+
if impls.len() > 1 && impls.len() < 10 && has_non_region_infer {
21842189
self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
21852190
} else {
21862191
if self.tainted_by_errors().is_some() {
21872192
err.cancel();
21882193
return;
21892194
}
21902195
err.note(&format!("cannot satisfy `{}`", predicate));
2196+
let impl_candidates = self.find_similar_impl_candidates(
2197+
predicate.to_opt_poly_trait_pred().unwrap(),
2198+
);
2199+
if impl_candidates.len() < 10 {
2200+
self.report_similar_impl_candidates(
2201+
impl_candidates,
2202+
trait_ref,
2203+
body_id.map(|id| id.hir_id).unwrap_or(obligation.cause.body_id),
2204+
&mut err,
2205+
false,
2206+
);
2207+
}
21912208
}
21922209
}
21932210
_ => {
@@ -2199,60 +2216,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
21992216
}
22002217
}
22012218

2202-
if let ObligationCauseCode::ItemObligation(def_id) | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() {
2203-
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
2204-
} else if let Ok(snippet) = &self.tcx.sess.source_map().span_to_snippet(span)
2205-
&& let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..)
2206-
= *obligation.cause.code()
2219+
if let ObligationCauseCode::ItemObligation(def_id)
2220+
| ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code()
22072221
{
2208-
let generics = self.tcx.generics_of(def_id);
2209-
if generics.params.iter().any(|p| p.name != kw::SelfUpper)
2210-
&& !snippet.ends_with('>')
2211-
&& !generics.has_impl_trait()
2212-
&& !self.tcx.is_fn_trait(def_id)
2213-
{
2214-
// FIXME: To avoid spurious suggestions in functions where type arguments
2215-
// where already supplied, we check the snippet to make sure it doesn't
2216-
// end with a turbofish. Ideally we would have access to a `PathSegment`
2217-
// instead. Otherwise we would produce the following output:
2218-
//
2219-
// error[E0283]: type annotations needed
2220-
// --> $DIR/issue-54954.rs:3:24
2221-
// |
2222-
// LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>();
2223-
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^
2224-
// | |
2225-
// | cannot infer type
2226-
// | help: consider specifying the type argument
2227-
// | in the function call:
2228-
// | `Tt::const_val::<[i8; 123]>::<T>`
2229-
// ...
2230-
// LL | const fn const_val<T: Sized>() -> usize {
2231-
// | - required by this bound in `Tt::const_val`
2232-
// |
2233-
// = note: cannot satisfy `_: Tt`
2234-
2235-
// Clear any more general suggestions in favor of our specific one
2236-
err.clear_suggestions();
2237-
2238-
err.span_suggestion_verbose(
2239-
span.shrink_to_hi(),
2240-
&format!(
2241-
"consider specifying the type argument{} in the function call",
2242-
pluralize!(generics.params.len()),
2243-
),
2244-
format!(
2245-
"::<{}>",
2246-
generics
2247-
.params
2248-
.iter()
2249-
.map(|p| p.name.to_string())
2250-
.collect::<Vec<String>>()
2251-
.join(", ")
2252-
),
2253-
Applicability::HasPlaceholders,
2254-
);
2255-
}
2222+
self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
22562223
}
22572224

22582225
if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =

library/core/src/iter/traits/iterator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,7 @@ pub trait Iterator {
18291829
#[inline]
18301830
#[stable(feature = "rust1", since = "1.0.0")]
18311831
#[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"]
1832+
#[cfg_attr(not(test), rustc_diagnostic_item = "iterator_collect_fn")]
18321833
fn collect<B: FromIterator<Self::Item>>(self) -> B
18331834
where
18341835
Self: Sized,

src/test/ui/array-slice-vec/infer_array_len.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ LL | let [_, _] = a.into();
66
|
77
help: consider giving this pattern a type
88
|
9-
LL | let [_, _]: _ = a.into();
10-
| +++
9+
LL | let [_, _]: /* Type */ = a.into();
10+
| ++++++++++++
1111

1212
error: aborting due to previous error
1313

src/test/ui/closure-expected-type/expect-two-infer-vars-supply-ty-with-bound-region.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ LL | with_closure(|x: u32, y| {});
66
|
77
help: consider giving this closure parameter an explicit type
88
|
9-
LL | with_closure(|x: u32, y: _| {});
10-
| +++
9+
LL | with_closure(|x: u32, y: /* Type */| {});
10+
| ++++++++++++
1111

1212
error: aborting due to previous error
1313

0 commit comments

Comments
 (0)