use super::FnCtxt;
use crate::astconv::AstConv;
+use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use rustc_ast::util::parser::ExprPrecedence;
-use rustc_span::{self, MultiSpan, Span};
-
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
WherePredicate,
};
use rustc_infer::infer::{self, TyCtxtInferExt};
-
+use rustc_infer::traits;
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Binder, Ty};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, Binder, ToPredicate, Ty};
use rustc_span::symbol::{kw, sym};
+use rustc_span::Span;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
-use rustc_middle::ty::subst::GenericArgKind;
use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
/// the ctor would successfully solve the type mismatch and if so, suggest it:
- /// ```
+ /// ```compile_fail,E0308
/// fn foo(x: usize) -> usize { x }
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
/// ```
}
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
- match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
+ match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) {
Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
msg = "instantiate this tuple variant";
}
/// A common error is to forget to add a semicolon at the end of a block, e.g.,
///
- /// ```
+ /// ```compile_fail,E0308
+ /// # fn bar_that_returns_u32() -> u32 { 4 }
/// fn foo() {
/// bar_that_returns_u32()
/// }
/// A possible error is to forget to add a return type that is needed:
///
- /// ```
+ /// ```compile_fail,E0308
+ /// # fn bar_that_returns_u32() -> u32 { 4 }
/// fn foo() {
/// bar_that_returns_u32()
/// }
self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
// Only suggest changing the return type for methods that
// haven't set a return type at all (and aren't `fn main()` or an impl).
- match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+ match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
(&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.span_suggestion(
- span,
- "try adding a return type",
- format!("-> {} ", found),
- Applicability::MachineApplicable,
- );
+ err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
true
}
(&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
// FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
// that.
- err.span_suggestion(
- span,
- "a return type might be missing here",
- "-> _ ".to_string(),
- Applicability::HasPlaceholders,
- );
+ err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
true
}
(&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
// `fn main()` must return `()`, do not suggest changing return type
- err.span_label(span, "expected `()` because of default return type");
+ err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
true
}
// expectation was caused by something else, not the default return
// Only point to return type if the expected type is the return type, as if they
// are not, the expectation must have been caused by something else.
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let sp = ty.span;
+ let span = ty.span;
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
debug!("suggest_missing_return_type: return type {:?}", ty);
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(sp, ty);
+ let ty = self.normalize_associated_types_in(span, ty);
let ty = self.tcx.erase_late_bound_regions(ty);
if self.can_coerce(expected, ty) {
- err.span_label(sp, format!("expected `{}` because of return type", expected));
+ err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
return true;
}
/// check whether the return type is a generic type with a trait bound
/// only suggest this if the generic param is not present in the arguments
/// if this is true, hint them towards changing the return type to `impl Trait`
- /// ```
+ /// ```compile_fail,E0308
/// fn cant_name_it<T: Fn() -> u32>() -> T {
/// || 3
/// }
kind:
hir::ItemKind::Fn(
hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
- hir::Generics { params, where_clause, .. },
+ hir::Generics { params, predicates, .. },
_body_id,
),
..
})) = fn_node else { return };
- let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return };
+ if params.get(expected_ty_as_param.index as usize).is_none() {
+ return;
+ };
// get all where BoundPredicates here, because they are used in to cases below
- let where_predicates = where_clause
- .predicates
+ let where_predicates = predicates
.iter()
.filter_map(|p| match p {
WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
// now get all predicates in the same types as the where bounds, so we can chain them
let predicates_from_where =
- where_predicates.iter().flatten().map(|bounds| bounds.iter()).flatten();
+ where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
// extract all bounds from the source code using their spans
- let all_matching_bounds_strs = expected_generic_param
- .bounds
- .iter()
- .chain(predicates_from_where)
+ let all_matching_bounds_strs = predicates_from_where
.filter_map(|bound| match bound {
GenericBound::Trait(_, _) => {
self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
let node = self.tcx.hir().get(id);
matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
}
+
+ /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
+ /// which is a side-effect of autoref.
+ pub(crate) fn note_type_is_not_clone(
+ &self,
+ diag: &mut Diagnostic,
+ expected_ty: Ty<'tcx>,
+ found_ty: Ty<'tcx>,
+ expr: &hir::Expr<'_>,
+ ) {
+ let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
+ let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
+ let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
+ let results = self.typeck_results.borrow();
+ // First, look for a `Clone::clone` call
+ if segment.ident.name == sym::clone
+ && results.type_dependent_def_id(expr.hir_id).map_or(
+ false,
+ |did| {
+ self.tcx.associated_item(did).container
+ == ty::AssocItemContainer::TraitContainer(clone_trait_did)
+ },
+ )
+ // If that clone call hasn't already dereferenced the self type (i.e. don't give this
+ // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
+ && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
+ // Check that we're in fact trying to clone into the expected type
+ && self.can_coerce(*pointee_ty, expected_ty)
+ // And the expected type doesn't implement `Clone`
+ && !self.predicate_must_hold_considering_regions(&traits::Obligation {
+ cause: traits::ObligationCause::dummy(),
+ param_env: self.param_env,
+ recursion_depth: 0,
+ predicate: ty::Binder::dummy(ty::TraitRef {
+ def_id: clone_trait_did,
+ substs: self.tcx.mk_substs([expected_ty.into()].iter()),
+ })
+ .without_const()
+ .to_predicate(self.tcx),
+ })
+ {
+ diag.span_note(
+ callee_expr.span,
+ &format!(
+ "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
+ ),
+ );
+ }
+ }
}