summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs)199
1 files changed, 155 insertions, 44 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index ee0ad7b5d..4db9c56f9 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,9 +1,7 @@
use super::FnCtxt;
-use crate::astconv::AstConv;
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
-use hir::def_id::DefId;
-use rustc_ast::util::parser::ExprPrecedence;
+use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
@@ -11,13 +9,16 @@ use rustc_hir::lang_items::LangItem;
use rustc_hir::{
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
+use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer::{self, TyCtxtInferExt};
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
+use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
+use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -64,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
/// When encountering an fn-like type, try accessing the output of the type
- /// // and suggesting calling it if it satisfies a predicate (i.e. if the
+ /// and suggesting calling it if it satisfies a predicate (i.e. if the
/// output has a method or a field):
/// ```compile_fail,E0308
/// fn foo(x: usize) -> usize { x }
@@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
} else {
- "".to_string()
+ "/* value */".to_string()
}
})
.collect::<Vec<_>>()
@@ -101,10 +102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let msg = match def_id_or_name {
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
- DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
- DefKind::Ctor(CtorOf::Variant, _) => {
- "instantiate this tuple variant".to_string()
- }
+ DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
+ DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
kind => format!("call this {}", kind.descr(def_id)),
},
DefIdOrName::Name(name) => format!("call this {name}"),
@@ -138,7 +137,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg,
applicability,
);
-
return true;
}
false
@@ -327,9 +325,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Ty<'tcx>,
found: Ty<'tcx>,
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) {
+ ) -> bool {
let expr = expr.peel_blocks();
- if let Some((sp, msg, suggestion, applicability, verbose)) =
+ if let Some((sp, msg, suggestion, applicability, verbose, annotation)) =
self.check_ref(expr, found, expected)
{
if verbose {
@@ -337,12 +335,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.span_suggestion(sp, &msg, suggestion, applicability);
}
+ if annotation {
+ let suggest_annotation = match expr.peel_drop_temps().kind {
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&",
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ",
+ _ => return true,
+ };
+ let mut tuple_indexes = Vec::new();
+ let mut expr_id = expr.hir_id;
+ for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) {
+ match node {
+ Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
+ tuple_indexes.push(
+ subs.iter()
+ .enumerate()
+ .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
+ .unwrap()
+ .0,
+ );
+ expr_id = parent_id;
+ }
+ Node::Local(local) => {
+ if let Some(mut ty) = local.ty {
+ while let Some(index) = tuple_indexes.pop() {
+ match ty.kind {
+ TyKind::Tup(tys) => ty = &tys[index],
+ _ => return true,
+ }
+ }
+ let annotation_span = ty.span;
+ err.span_suggestion(
+ annotation_span.with_hi(annotation_span.lo()),
+ format!("alternatively, consider changing the type annotation"),
+ suggest_annotation,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ break;
+ }
+ _ => break,
+ }
+ }
+ }
+ return true;
+ } else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
+ return true;
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
&& let ty::FnDef(def_id, ..) = &found.kind()
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
{
err.span_label(sp, format!("{found} defined here"));
- } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+ return true;
+ } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+ return true;
+ } else {
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
if !methods.is_empty() {
let mut suggestions = methods.iter()
@@ -393,6 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
suggestions,
Applicability::MaybeIncorrect,
);
+ return true;
}
} else if let ty::Adt(found_adt, found_substs) = found.kind()
&& self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
@@ -417,9 +464,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
Applicability::MaybeIncorrect,
);
+ return true;
}
}
}
+
+ false
}
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
@@ -430,13 +480,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
- ) {
+ ) -> bool {
if self.tcx.hir().is_inside_const_context(expr.hir_id) {
// Do not suggest `Box::new` in const context.
- return;
+ return false;
}
if !expected.is_box() || found.is_box() {
- return;
+ return false;
}
let boxed_found = self.tcx.mk_box(found);
if self.can_coerce(boxed_found, expected) {
@@ -454,6 +504,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
https://doc.rust-lang.org/rust-by-example/std/box.html, and \
https://doc.rust-lang.org/std/boxed/index.html",
);
+ true
+ } else {
+ false
}
}
@@ -464,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err: &mut Diagnostic,
expected: Ty<'tcx>,
found: Ty<'tcx>,
- ) {
+ ) -> bool {
if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
// Report upto four upvars being captured to reduce the amount error messages
@@ -488,8 +541,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
multi_span,
"closures can only be coerced to `fn` types if they do not capture any variables"
);
+ return true;
}
}
+ false
}
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
@@ -860,18 +915,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
let ty = self.normalize_associated_types_in(expr.span, ty);
let ty = match self.tcx.asyncness(fn_id.owner) {
- hir::IsAsync::Async => self
- .tcx
- .infer_ctxt()
- .enter(|infcx| {
- infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+ hir::IsAsync::Async => {
+ let infcx = self.tcx.infer_ctxt().build();
+ infcx
+ .get_impl_future_output_ty(ty)
+ .unwrap_or_else(|| {
span_bug!(
fn_decl.output.span(),
"failed to get output type of async function"
)
})
- })
- .skip_binder(),
+ .skip_binder()
+ }
hir::IsAsync::NotAsync => ty,
};
if self.can_coerce(found, ty) {
@@ -891,11 +946,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
- ) {
+ ) -> bool {
let sp = self.tcx.sess.source_map().start_point(expr.span);
if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
// `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp);
+ err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
+ true
+ } else {
+ false
}
}
@@ -908,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
mut expr: &hir::Expr<'_>,
mut expr_ty: Ty<'tcx>,
mut expected_ty: Ty<'tcx>,
- ) {
+ ) -> bool {
loop {
match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
(
@@ -922,9 +980,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
(hir::ExprKind::Block(blk, _), _, _) => {
self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
- break;
+ break true;
}
- _ => break,
+ _ => break false,
}
}
}
@@ -935,11 +993,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &hir::Expr<'_>,
expr_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
- ) {
- let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
- let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
+ ) -> bool {
+ let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; };
+ let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; };
if adt_def != expected_adt_def {
- return;
+ return false;
}
let mut suggest_copied_or_cloned = || {
@@ -958,6 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
".copied()",
Applicability::MachineApplicable,
);
+ return true;
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
@@ -975,8 +1034,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
".cloned()",
Applicability::MachineApplicable,
);
+ return true;
}
}
+ false
};
if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
@@ -984,12 +1045,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Check that the error types are equal
&& self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
{
- suggest_copied_or_cloned();
+ return suggest_copied_or_cloned();
} else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
&& adt_def.did() == option_did
{
- suggest_copied_or_cloned();
+ return suggest_copied_or_cloned();
}
+
+ false
+ }
+
+ pub(crate) fn suggest_into(
+ &self,
+ diag: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expr_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ let expr = expr.peel_blocks();
+
+ // We have better suggestions for scalar interconversions...
+ if expr_ty.is_scalar() && expected_ty.is_scalar() {
+ return false;
+ }
+
+ // Don't suggest turning a block into another type (e.g. `{}.into()`)
+ if matches!(expr.kind, hir::ExprKind::Block(..)) {
+ return false;
+ }
+
+ // We'll later suggest `.as_ref` when noting the type error,
+ // so skip if we will suggest that instead.
+ if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
+ return false;
+ }
+
+ if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
+ && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
+ self.misc(expr.span),
+ self.param_env,
+ ty::Binder::dummy(ty::TraitRef {
+ def_id: into_def_id,
+ substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]),
+ })
+ .to_poly_trait_predicate()
+ .to_predicate(self.tcx),
+ ))
+ {
+ let sugg = if expr.precedence().order() >= PREC_POSTFIX {
+ vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
+ } else {
+ vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
+ };
+ diag.multipart_suggestion(
+ format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
+ sugg,
+ Applicability::MaybeIncorrect
+ );
+ return true;
+ }
+
+ false
}
/// Suggest wrapping the block in square brackets instead of curly braces
@@ -1110,7 +1226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected_ty: Ty<'tcx>,
err: &mut Diagnostic,
) -> bool {
- if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+ if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
if let StatementAsExpression::NeedsBoxing = boxed {
err.span_suggestion_verbose(
span_semi,
@@ -1121,7 +1237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.span_suggestion_short(
span_semi,
- "remove this semicolon",
+ "remove this semicolon to return this value",
"",
Applicability::MachineApplicable,
);
@@ -1132,8 +1248,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
-
-pub enum DefIdOrName {
- DefId(DefId),
- Name(&'static str),
-}