Skip to content

Commit cec087a

Browse files
authored
Rollup merge of #102496 - compiler-errors:into-suggestion, r=davidtwco
Suggest `.into()` when all other coercion suggestions fail Also removes some bogus suggestions because we now short-circuit when offering coercion suggestions(instead of, for example, suggesting every one that could possibly apply) Fixes #102415
2 parents 814b827 + 28eda9b commit cec087a

File tree

9 files changed

+207
-66
lines changed

9 files changed

+207
-66
lines changed

Diff for: compiler/rustc_hir_analysis/src/check/demand.rs

+27-19
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3232
error: Option<TypeError<'tcx>>,
3333
) {
3434
self.annotate_expected_due_to_let_ty(err, expr, error);
35-
self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
36-
self.suggest_compatible_variants(err, expr, expected, expr_ty);
37-
self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
38-
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
39-
return;
40-
}
41-
self.suggest_no_capture_closure(err, expected, expr_ty);
42-
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
43-
self.suggest_missing_parentheses(err, expr);
44-
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
45-
self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
35+
36+
// Use `||` to give these suggestions a precedence
37+
let _ = self.suggest_missing_parentheses(err, expr)
38+
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
39+
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)
40+
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
41+
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
42+
|| self.suggest_no_capture_closure(err, expected, expr_ty)
43+
|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
44+
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
45+
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
46+
|| self.suggest_into(err, expr, expr_ty, expected);
47+
4648
self.note_type_is_not_clone(err, expected, expr_ty, expr);
4749
self.note_need_for_fn_pointer(err, expected, expr_ty);
4850
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
@@ -286,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
286288
expr: &hir::Expr<'_>,
287289
expected: Ty<'tcx>,
288290
expr_ty: Ty<'tcx>,
289-
) {
291+
) -> bool {
290292
if let ty::Adt(expected_adt, substs) = expected.kind() {
291293
if let hir::ExprKind::Field(base, ident) = expr.kind {
292294
let base_ty = self.typeck_results.borrow().expr_ty(base);
@@ -299,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
299301
"",
300302
Applicability::MaybeIncorrect,
301303
);
302-
return
304+
return true;
303305
}
304306
}
305307

@@ -338,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
338340
} else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
339341
vec!["None", "Some(())"]
340342
} else {
341-
return;
343+
return false;
342344
};
343345
if let Some(indent) =
344346
self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
@@ -358,7 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
358360
Applicability::MaybeIncorrect,
359361
);
360362
}
361-
return;
363+
return true;
362364
}
363365
}
364366
}
@@ -445,6 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
445447
suggestions_for(&**variant, *ctor_kind, *field_name),
446448
Applicability::MaybeIncorrect,
447449
);
450+
return true;
448451
}
449452
_ => {
450453
// More than one matching variant.
@@ -460,9 +463,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
460463
),
461464
Applicability::MaybeIncorrect,
462465
);
466+
return true;
463467
}
464468
}
465469
}
470+
471+
false
466472
}
467473

468474
fn suggest_non_zero_new_unwrap(
@@ -471,19 +477,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
471477
expr: &hir::Expr<'_>,
472478
expected: Ty<'tcx>,
473479
expr_ty: Ty<'tcx>,
474-
) {
480+
) -> bool {
475481
let tcx = self.tcx;
476482
let (adt, unwrap) = match expected.kind() {
477483
// In case Option<NonZero*> is wanted, but * is provided, suggest calling new
478484
ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
479485
// Unwrap option
480-
let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
486+
let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; };
481487

482488
(adt, "")
483489
}
484490
// In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
485491
ty::Adt(adt, _) => (adt, ".unwrap()"),
486-
_ => return,
492+
_ => return false,
487493
};
488494

489495
let map = [
@@ -502,7 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
502508
let Some((s, _)) = map
503509
.iter()
504510
.find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
505-
else { return };
511+
else { return false; };
506512

507513
let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
508514

@@ -514,6 +520,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
514520
],
515521
Applicability::MaybeIncorrect,
516522
);
523+
524+
true
517525
}
518526

519527
pub fn get_conversion_methods(

Diff for: compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs

+92-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::astconv::AstConv;
33
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
44

55
use hir::def_id::DefId;
6-
use rustc_ast::util::parser::ExprPrecedence;
6+
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
77
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
88
use rustc_hir as hir;
99
use rustc_hir::def::{CtorOf, DefKind};
@@ -327,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
327327
expected: Ty<'tcx>,
328328
found: Ty<'tcx>,
329329
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
330-
) {
330+
) -> bool {
331331
let expr = expr.peel_blocks();
332332
if let Some((sp, msg, suggestion, applicability, verbose)) =
333333
self.check_ref(expr, found, expected)
@@ -337,14 +337,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
337337
} else {
338338
err.span_suggestion(sp, &msg, suggestion, applicability);
339339
}
340+
return true;
340341
} else if self.suggest_else_fn_with_closure(err, expr, found, expected)
341342
{
343+
return true;
342344
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
343345
&& let ty::FnDef(def_id, ..) = &found.kind()
344346
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
345347
{
346348
err.span_label(sp, format!("{found} defined here"));
347-
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
349+
return true;
350+
} else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
351+
return true;
352+
} else {
348353
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
349354
if !methods.is_empty() {
350355
let mut suggestions = methods.iter()
@@ -395,6 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
395400
suggestions,
396401
Applicability::MaybeIncorrect,
397402
);
403+
return true;
398404
}
399405
} else if let ty::Adt(found_adt, found_substs) = found.kind()
400406
&& self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
@@ -419,9 +425,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
419425
format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
420426
Applicability::MaybeIncorrect,
421427
);
428+
return true;
422429
}
423430
}
424431
}
432+
433+
false
425434
}
426435

427436
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
@@ -432,13 +441,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
432441
expr: &hir::Expr<'_>,
433442
expected: Ty<'tcx>,
434443
found: Ty<'tcx>,
435-
) {
444+
) -> bool {
436445
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
437446
// Do not suggest `Box::new` in const context.
438-
return;
447+
return false;
439448
}
440449
if !expected.is_box() || found.is_box() {
441-
return;
450+
return false;
442451
}
443452
let boxed_found = self.tcx.mk_box(found);
444453
if self.can_coerce(boxed_found, expected) {
@@ -456,6 +465,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
456465
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
457466
https://doc.rust-lang.org/std/boxed/index.html",
458467
);
468+
true
469+
} else {
470+
false
459471
}
460472
}
461473

@@ -466,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
466478
err: &mut Diagnostic,
467479
expected: Ty<'tcx>,
468480
found: Ty<'tcx>,
469-
) {
481+
) -> bool {
470482
if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
471483
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
472484
// Report upto four upvars being captured to reduce the amount error messages
@@ -490,8 +502,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
490502
multi_span,
491503
"closures can only be coerced to `fn` types if they do not capture any variables"
492504
);
505+
return true;
493506
}
494507
}
508+
false
495509
}
496510

497511
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
@@ -893,11 +907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
893907
&self,
894908
err: &mut Diagnostic,
895909
expr: &hir::Expr<'_>,
896-
) {
910+
) -> bool {
897911
let sp = self.tcx.sess.source_map().start_point(expr.span);
898912
if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
899913
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
900914
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
915+
true
916+
} else {
917+
false
901918
}
902919
}
903920

@@ -910,7 +927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
910927
mut expr: &hir::Expr<'_>,
911928
mut expr_ty: Ty<'tcx>,
912929
mut expected_ty: Ty<'tcx>,
913-
) {
930+
) -> bool {
914931
loop {
915932
match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
916933
(
@@ -924,9 +941,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
924941
}
925942
(hir::ExprKind::Block(blk, _), _, _) => {
926943
self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
927-
break;
944+
break true;
928945
}
929-
_ => break,
946+
_ => break false,
930947
}
931948
}
932949
}
@@ -937,11 +954,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
937954
expr: &hir::Expr<'_>,
938955
expr_ty: Ty<'tcx>,
939956
expected_ty: Ty<'tcx>,
940-
) {
941-
let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
942-
let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
957+
) -> bool {
958+
let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; };
959+
let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; };
943960
if adt_def != expected_adt_def {
944-
return;
961+
return false;
945962
}
946963

947964
let mut suggest_copied_or_cloned = || {
@@ -960,6 +977,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
960977
".copied()",
961978
Applicability::MachineApplicable,
962979
);
980+
return true;
963981
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
964982
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
965983
self,
@@ -977,21 +995,78 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
977995
".cloned()",
978996
Applicability::MachineApplicable,
979997
);
998+
return true;
980999
}
9811000
}
1001+
false
9821002
};
9831003

9841004
if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
9851005
&& adt_def.did() == result_did
9861006
// Check that the error types are equal
9871007
&& self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
9881008
{
989-
suggest_copied_or_cloned();
1009+
return suggest_copied_or_cloned();
9901010
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
9911011
&& adt_def.did() == option_did
9921012
{
993-
suggest_copied_or_cloned();
1013+
return suggest_copied_or_cloned();
1014+
}
1015+
1016+
false
1017+
}
1018+
1019+
pub(crate) fn suggest_into(
1020+
&self,
1021+
diag: &mut Diagnostic,
1022+
expr: &hir::Expr<'_>,
1023+
expr_ty: Ty<'tcx>,
1024+
expected_ty: Ty<'tcx>,
1025+
) -> bool {
1026+
let expr = expr.peel_blocks();
1027+
1028+
// We have better suggestions for scalar interconversions...
1029+
if expr_ty.is_scalar() && expected_ty.is_scalar() {
1030+
return false;
9941031
}
1032+
1033+
// Don't suggest turning a block into another type (e.g. `{}.into()`)
1034+
if matches!(expr.kind, hir::ExprKind::Block(..)) {
1035+
return false;
1036+
}
1037+
1038+
// We'll later suggest `.as_ref` when noting the type error,
1039+
// so skip if we will suggest that instead.
1040+
if self.should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1041+
return false;
1042+
}
1043+
1044+
if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1045+
&& self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1046+
self.misc(expr.span),
1047+
self.param_env,
1048+
ty::Binder::dummy(ty::TraitRef {
1049+
def_id: into_def_id,
1050+
substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]),
1051+
})
1052+
.to_poly_trait_predicate()
1053+
.to_predicate(self.tcx),
1054+
))
1055+
{
1056+
let sugg = if expr.precedence().order() >= PREC_POSTFIX {
1057+
vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
1058+
} else {
1059+
vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
1060+
};
1061+
diag.multipart_suggestion(
1062+
format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1063+
sugg,
1064+
Applicability::MaybeIncorrect
1065+
);
1066+
return true;
1067+
}
1068+
1069+
false
9951070
}
9961071

9971072
/// Suggest wrapping the block in square brackets instead of curly braces

0 commit comments

Comments
 (0)