|| self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+ || self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected);
if !suggested {
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
+ self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
}
/// Requires that the two types unify, and prints an error message if
// Get the evaluated type *after* calling the method call, so that the influence
// of the arguments can be reflected in the receiver type. The receiver
// expression has the type *before* theis analysis is done.
- let ty = match self.lookup_probe(
+ let ty = match self.lookup_probe_for_diagnostic(
segment.ident,
rcvr_ty,
expr,
probe::ProbeScope::TraitsInScope,
+ None,
) {
Ok(pick) => pick.self_ty,
Err(_) => rcvr_ty,
let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; };
let Ok(pick) = self
- .probe_for_name(
- probe::Mode::MethodCall,
+ .lookup_probe_for_diagnostic(
path.ident,
- probe::IsSuggestion(true),
self_ty,
- deref.hir_id,
+ deref,
probe::ProbeScope::TraitsInScope,
+ None,
) else {
return;
};
let in_scope_methods = self.probe_for_name_many(
probe::Mode::MethodCall,
path.ident,
+ Some(expected),
probe::IsSuggestion(true),
self_ty,
deref.hir_id,
let all_methods = self.probe_for_name_many(
probe::Mode::MethodCall,
path.ident,
+ Some(expected),
probe::IsSuggestion(true),
self_ty,
deref.hir_id,
}
}
// If we've reached our target type with just removing `&`, then just print now.
- if steps == 0 {
+ if steps == 0 && !remove.trim().is_empty() {
return Some((
prefix_span,
format!("consider removing the `{}`", remove.trim()),
} else {
(prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
};
+ if suggestion.trim().is_empty() {
+ return None;
+ }
return Some((
span,
pub fn check_for_range_as_method_call(
&self,
err: &mut Diagnostic,
- expr: &hir::Expr<'_>,
+ expr: &hir::Expr<'tcx>,
checked_ty: Ty<'tcx>,
expected_ty: Ty<'tcx>,
) {
return;
}
let mut expr = end.expr;
+ let mut expectation = Some(expected_ty);
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
- // `src/test/ui/methods/issues/issue-90315.stderr`.
+ // `tests/ui/methods/issues/issue-90315.stderr`.
expr = rcvr;
+ // If we have more than one layer of calls, then the expected ty
+ // cannot guide the method probe.
+ expectation = None;
}
let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
let [hir::PathSegment { ident, .. }] = p.segments else { return; };
let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
- let Ok(_pick) = self.probe_for_name(
- probe::Mode::MethodCall,
+ let Ok(_pick) = self.lookup_probe_for_diagnostic(
*ident,
- probe::IsSuggestion(true),
self_ty,
- expr.hir_id,
+ expr,
probe::ProbeScope::AllTraits,
+ expectation,
) else { return; };
let mut sugg = ".";
let mut span = start.expr.span.between(end.expr.span);
err.span_label(block.span, "this block is missing a tail expression");
}
}
+
+ fn check_wrong_return_type_due_to_generic_arg(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ checked_ty: Ty<'tcx>,
+ ) {
+ let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
+ enum CallableKind {
+ Function,
+ Method,
+ Constructor,
+ }
+ let mut maybe_emit_help = |def_id: hir::def_id::DefId,
+ callable: rustc_span::symbol::Ident,
+ args: &[hir::Expr<'_>],
+ kind: CallableKind| {
+ let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
+ let fn_ty = self.tcx.bound_type_of(def_id).0;
+ if !fn_ty.is_fn() {
+ return;
+ }
+ let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
+ let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
+ if matches!(arg.kind(), ty::Param(_))
+ && fn_sig.output().contains(arg)
+ && self.node_ty(args[arg_idx].hir_id) == checked_ty
+ {
+ let mut multi_span: MultiSpan = parent_expr.span.into();
+ multi_span.push_span_label(
+ args[arg_idx].span,
+ format!(
+ "this argument influences the {} of `{}`",
+ if matches!(kind, CallableKind::Constructor) {
+ "type"
+ } else {
+ "return type"
+ },
+ callable
+ ),
+ );
+ err.span_help(
+ multi_span,
+ format!(
+ "the {} `{}` due to the type of the argument passed",
+ match kind {
+ CallableKind::Function => "return type of this call is",
+ CallableKind::Method => "return type of this call is",
+ CallableKind::Constructor => "type constructed contains",
+ },
+ checked_ty
+ ),
+ );
+ }
+ };
+ match parent_expr.kind {
+ hir::ExprKind::Call(fun, args) => {
+ let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
+ let hir::def::Res::Def(kind, def_id) = path.res else { return; };
+ let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
+ CallableKind::Constructor
+ } else {
+ CallableKind::Function
+ };
+ maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
+ }
+ hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
+ let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
+ maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
+ }
+ _ => return,
+ }
+ }
}