summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs')
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs149
1 files changed, 119 insertions, 30 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 4db9c56f9..4f92477b5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -10,24 +10,35 @@ 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::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
+use rustc_middle::ty::{self, Binder, DefIdTree, 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 _;
+use rustc_trait_selection::traits::NormalizeExt;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
+ self.typeck_results
+ .borrow()
+ .liberated_fn_sigs()
+ .get(self.tcx.hir().get_parent_node(self.body_id))
+ .copied()
+ }
+
pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
+ // This suggestion is incorrect for
+ // fn foo() -> bool { match () { () => true } || match () { () => true } }
err.span_suggestion_short(
span.shrink_to_hi(),
"consider using a semicolon here",
";",
- Applicability::MachineApplicable,
+ Applicability::MaybeIncorrect,
);
}
@@ -165,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
ty::Opaque(def_id, substs) => {
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
@@ -200,7 +211,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Param(param) => {
let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
- if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be substs[1]
@@ -237,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// implied by wf, but also because that would possibly result in
// erroneous errors later on.
let infer::InferOk { value: output, obligations: _ } =
- self.normalize_associated_types_in_as_infer_ok(expr.span, output);
+ self.at(&self.misc(expr.span), self.param_env).normalize(output);
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
}
@@ -337,8 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
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 ",
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
_ => return true,
};
let mut tuple_indexes = Vec::new();
@@ -366,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let annotation_span = ty.span;
err.span_suggestion(
annotation_span.with_hi(annotation_span.lo()),
- format!("alternatively, consider changing the type annotation"),
+ "alternatively, consider changing the type annotation",
suggest_annotation,
Applicability::MaybeIncorrect,
);
@@ -456,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ref_cnt += 1;
}
if let ty::Adt(adt, _) = peeled.kind()
- && self.tcx.is_diagnostic_item(sym::String, adt.did())
+ && Some(adt.did()) == self.tcx.lang_items().string()
{
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
@@ -752,7 +762,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("suggest_missing_return_type: expected type {:?}", ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
let ty = Binder::bind_with_vars(ty, bound_vars);
- let ty = self.normalize_associated_types_in(span, ty);
+ let ty = self.normalize(span, ty);
let ty = self.tcx.erase_late_bound_regions(ty);
if self.can_coerce(expected, ty) {
err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
@@ -913,22 +923,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
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 => {
- 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()
- }
+ hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+ span_bug!(fn_decl.output.span(), "failed to get output type of async function")
+ }),
hir::IsAsync::NotAsync => ty,
};
+ let ty = self.normalize(expr.span, ty);
if self.can_coerce(found, ty) {
err.multipart_suggestion(
"you might have meant to return this value",
@@ -1082,14 +1083,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
&& self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
+ self.tcx,
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),
+ ty::Binder::dummy(self.tcx.mk_trait_ref(
+ into_def_id,
+ [expr_ty, expected_ty]
+ )),
))
{
let sugg = if expr.precedence().order() >= PREC_POSTFIX {
@@ -1108,6 +1108,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
+ /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
+ pub(crate) fn suggest_option_to_bool(
+ &self,
+ diag: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expr_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ if !expected_ty.is_bool() {
+ return false;
+ }
+
+ let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; };
+ if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
+ return false;
+ }
+
+ let hir = self.tcx.hir();
+ let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
+ matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
+ }).next();
+ // Don't suggest:
+ // `let Some(_) = a.is_some() && b`
+ // ++++++++++
+ // since the user probably just misunderstood how `let else`
+ // and `&&` work together.
+ if let Some((_, hir::Node::Local(local))) = cond_parent
+ && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
+ && let hir::QPath::Resolved(None, path) = qpath
+ && let Some(did) = path.res.opt_def_id()
+ .and_then(|did| self.tcx.opt_parent(did))
+ .and_then(|did| self.tcx.opt_parent(did))
+ && self.tcx.is_diagnostic_item(sym::Option, did)
+ {
+ return false;
+ }
+
+ diag.span_suggestion(
+ expr.span.shrink_to_hi(),
+ "use `Option::is_some` to test if the `Option` has a value",
+ ".is_some()",
+ Applicability::MachineApplicable,
+ );
+
+ true
+ }
+
/// Suggest wrapping the block in square brackets instead of curly braces
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
pub(crate) fn suggest_block_to_brackets(
@@ -1149,6 +1196,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ #[instrument(skip(self, err))]
+ pub(crate) fn suggest_floating_point_literal(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ if !expected_ty.is_floating_point() {
+ return false;
+ }
+ match expr.kind {
+ ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
+ err.span_suggestion_verbose(
+ start.span.shrink_to_hi().with_hi(end.span.lo()),
+ "remove the unnecessary `.` operator for a floating point literal",
+ '.',
+ Applicability::MaybeIncorrect,
+ );
+ true
+ }
+ ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
+ err.span_suggestion_verbose(
+ expr.span.with_lo(start.span.hi()),
+ "remove the unnecessary `.` operator for a floating point literal",
+ '.',
+ Applicability::MaybeIncorrect,
+ );
+ true
+ }
+ ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
+ err.span_suggestion_verbose(
+ expr.span.until(end.span),
+ "remove the unnecessary `.` operator and add an integer part for a floating point literal",
+ "0.",
+ Applicability::MaybeIncorrect,
+ );
+ true
+ }
+ _ => false,
+ }
+ }
+
fn is_loop(&self, id: hir::HirId) -> bool {
let node = self.tcx.hir().get(id);
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))