Rollup merge of #106752 - sulami:master, r=estebank
Emit a hint for bad call return types due to generic arguments When the return type of a function call depends on the type of an argument, e.g. ``` fn foo<T>(x: T) -> T { x } ``` and the expected type is set due to either an explicitly typed binding, or because the call to the function is in a tail position without semicolon, the current error implies that the argument in the call has the wrong type. This new hint highlights that the expected type doesn't match the returned type, which matches the argument type, and that that's why we're flagging the argument type. Fixes #43608.
This commit is contained in:
commit
108b5f462b
13 changed files with 338 additions and 0 deletions
|
@ -85,6 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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
|
||||
|
@ -1941,4 +1942,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,13 @@ LL | Foo(())
|
|||
| |
|
||||
| arguments to this struct are incorrect
|
||||
|
|
||||
help: the type constructed contains `()` due to the type of the argument passed
|
||||
--> $DIR/issue-84128.rs:13:9
|
||||
|
|
||||
LL | Foo(())
|
||||
| ^^^^--^
|
||||
| |
|
||||
| this argument influences the type of `Foo`
|
||||
note: tuple struct defined here
|
||||
--> $DIR/issue-84128.rs:5:8
|
||||
|
|
||||
|
|
|
@ -6,6 +6,13 @@ LL | Ok(())
|
|||
| |
|
||||
| arguments to this enum variant are incorrect
|
||||
|
|
||||
help: the type constructed contains `()` due to the type of the argument passed
|
||||
--> $DIR/issue-87461.rs:10:5
|
||||
|
|
||||
LL | Ok(())
|
||||
| ^^^--^
|
||||
| |
|
||||
| this argument influences the type of `Ok`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/result.rs:LL:COL
|
||||
|
||||
|
@ -17,6 +24,13 @@ LL | Ok(())
|
|||
| |
|
||||
| arguments to this enum variant are incorrect
|
||||
|
|
||||
help: the type constructed contains `()` due to the type of the argument passed
|
||||
--> $DIR/issue-87461.rs:17:5
|
||||
|
|
||||
LL | Ok(())
|
||||
| ^^^--^
|
||||
| |
|
||||
| this argument influences the type of `Ok`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/result.rs:LL:COL
|
||||
|
||||
|
@ -28,6 +42,13 @@ LL | Ok(())
|
|||
| |
|
||||
| arguments to this enum variant are incorrect
|
||||
|
|
||||
help: the type constructed contains `()` due to the type of the argument passed
|
||||
--> $DIR/issue-87461.rs:26:9
|
||||
|
|
||||
LL | Ok(())
|
||||
| ^^^--^
|
||||
| |
|
||||
| this argument influences the type of `Ok`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/result.rs:LL:COL
|
||||
|
||||
|
|
|
@ -23,6 +23,13 @@ LL | A(self.0 + rhs.0)
|
|||
|
|
||||
= note: expected type parameter `B`
|
||||
found associated type `<B as Add>::Output`
|
||||
help: the type constructed contains `<B as Add>::Output` due to the type of the argument passed
|
||||
--> $DIR/missing-bounds.rs:11:9
|
||||
|
|
||||
LL | A(self.0 + rhs.0)
|
||||
| ^^--------------^
|
||||
| |
|
||||
| this argument influences the type of `A`
|
||||
note: tuple struct defined here
|
||||
--> $DIR/missing-bounds.rs:5:8
|
||||
|
|
||||
|
|
|
@ -11,6 +11,13 @@ LL | Some(true)
|
|||
|
|
||||
= note: expected type parameter `bool` (type parameter `bool`)
|
||||
found type `bool` (`bool`)
|
||||
help: the type constructed contains `bool` due to the type of the argument passed
|
||||
--> $DIR/issue-35030.rs:9:9
|
||||
|
|
||||
LL | Some(true)
|
||||
| ^^^^^----^
|
||||
| |
|
||||
| this argument influences the type of `Some`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ LL | let _: Option<(i32, bool)> = Some(1, 2);
|
|||
| ^
|
||||
= note: expected tuple `(i32, bool)`
|
||||
found type `{integer}`
|
||||
help: the type constructed contains `{integer}` due to the type of the argument passed
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:6:34
|
||||
|
|
||||
LL | let _: Option<(i32, bool)> = Some(1, 2);
|
||||
| ^^^^^-^^^^
|
||||
| |
|
||||
| this argument influences the type of `Some`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
help: remove the extra argument
|
||||
|
@ -64,6 +71,13 @@ LL | let _: Option<(i32,)> = Some(5_usize);
|
|||
|
|
||||
= note: expected tuple `(i32,)`
|
||||
found type `usize`
|
||||
help: the type constructed contains `usize` due to the type of the argument passed
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:14:29
|
||||
|
|
||||
LL | let _: Option<(i32,)> = Some(5_usize);
|
||||
| ^^^^^-------^
|
||||
| |
|
||||
| this argument influences the type of `Some`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
|
||||
|
@ -77,6 +91,13 @@ LL | let _: Option<(i32,)> = Some((5_usize));
|
|||
|
|
||||
= note: expected tuple `(i32,)`
|
||||
found type `usize`
|
||||
help: the type constructed contains `usize` due to the type of the argument passed
|
||||
--> $DIR/args-instead-of-tuple-errors.rs:17:29
|
||||
|
|
||||
LL | let _: Option<(i32,)> = Some((5_usize));
|
||||
| ^^^^^---------^
|
||||
| |
|
||||
| this argument influences the type of `Some`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
|
||||
|
|
|
@ -8,6 +8,13 @@ LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
|
|||
|
|
||||
= note: expected reference `&str`
|
||||
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
|
||||
help: the return type of this call is `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]` due to the type of the argument passed
|
||||
--> $DIR/sugg-else-for-closure.rs:6:14
|
||||
|
|
||||
LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
|
||||
| ^^^^^^^^^^^^-------------------------------^
|
||||
| |
|
||||
| this argument influences the return type of `unwrap_or`
|
||||
note: associated function defined here
|
||||
--> $SRC_DIR/core/src/option.rs:LL:COL
|
||||
help: try calling `unwrap_or_else` instead
|
||||
|
|
|
@ -11,6 +11,13 @@ LL | builder.push(output);
|
|||
|
|
||||
= note: expected type parameter `F`
|
||||
found struct `Class<P>`
|
||||
help: the return type of this call is `Class<P>` due to the type of the argument passed
|
||||
--> $DIR/issue-52893.rs:53:9
|
||||
|
|
||||
LL | builder.push(output);
|
||||
| ^^^^^^^^^^^^^------^
|
||||
| |
|
||||
| this argument influences the return type of `push`
|
||||
note: associated function defined here
|
||||
--> $DIR/issue-52893.rs:11:8
|
||||
|
|
||||
|
|
28
tests/ui/type/wrong-call-return-type-due-to-generic-arg.rs
Normal file
28
tests/ui/type/wrong-call-return-type-due-to-generic-arg.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
fn function<T>(x: T, y: bool) -> T {
|
||||
x
|
||||
}
|
||||
|
||||
struct S {}
|
||||
impl S {
|
||||
fn method<T>(&self, x: T) -> T {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
fn wrong_arg_type(x: u32) -> u32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Should not trigger.
|
||||
let x = wrong_arg_type(0u16); //~ ERROR mismatched types
|
||||
let x: u16 = function(0, 0u8); //~ ERROR mismatched types
|
||||
|
||||
// Should trigger exactly once for the first argument.
|
||||
let x: u16 = function(0u32, 0u8); //~ ERROR arguments to this function are incorrect
|
||||
|
||||
// Should trigger.
|
||||
let x: u16 = function(0u32, true); //~ ERROR mismatched types
|
||||
let x: u16 = (S {}).method(0u32); //~ ERROR mismatched types
|
||||
function(0u32, 8u8) //~ ERROR arguments to this function are incorrect
|
||||
}
|
131
tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr
Normal file
131
tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr
Normal file
|
@ -0,0 +1,131 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:18:28
|
||||
|
|
||||
LL | let x = wrong_arg_type(0u16);
|
||||
| -------------- ^^^^ expected `u32`, found `u16`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:12:4
|
||||
|
|
||||
LL | fn wrong_arg_type(x: u32) -> u32 {
|
||||
| ^^^^^^^^^^^^^^ ------
|
||||
help: change the type of the numeric literal from `u16` to `u32`
|
||||
|
|
||||
LL | let x = wrong_arg_type(0u32);
|
||||
| ~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:19:30
|
||||
|
|
||||
LL | let x: u16 = function(0, 0u8);
|
||||
| -------- ^^^ expected `bool`, found `u8`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
note: function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
|
||||
|
|
||||
LL | fn function<T>(x: T, y: bool) -> T {
|
||||
| ^^^^^^^^ -------
|
||||
|
||||
error[E0308]: arguments to this function are incorrect
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
|
||||
|
|
||||
LL | let x: u16 = function(0u32, 0u8);
|
||||
| ^^^^^^^^ ---- --- expected `bool`, found `u8`
|
||||
| |
|
||||
| expected `u16`, found `u32`
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
|
||||
|
|
||||
LL | let x: u16 = function(0u32, 0u8);
|
||||
| ^^^^^^^^^----^^^^^^
|
||||
| |
|
||||
| this argument influences the return type of `function`
|
||||
note: function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
|
||||
|
|
||||
LL | fn function<T>(x: T, y: bool) -> T {
|
||||
| ^^^^^^^^ ---- -------
|
||||
help: change the type of the numeric literal from `u32` to `u16`
|
||||
|
|
||||
LL | let x: u16 = function(0u16, 0u8);
|
||||
| ~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:27
|
||||
|
|
||||
LL | let x: u16 = function(0u32, true);
|
||||
| -------- ^^^^ expected `u16`, found `u32`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:18
|
||||
|
|
||||
LL | let x: u16 = function(0u32, true);
|
||||
| ^^^^^^^^^----^^^^^^^
|
||||
| |
|
||||
| this argument influences the return type of `function`
|
||||
note: function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
|
||||
|
|
||||
LL | fn function<T>(x: T, y: bool) -> T {
|
||||
| ^^^^^^^^ ----
|
||||
help: change the type of the numeric literal from `u32` to `u16`
|
||||
|
|
||||
LL | let x: u16 = function(0u16, true);
|
||||
| ~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:32
|
||||
|
|
||||
LL | let x: u16 = (S {}).method(0u32);
|
||||
| ------ ^^^^ expected `u16`, found `u32`
|
||||
| |
|
||||
| arguments to this method are incorrect
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:18
|
||||
|
|
||||
LL | let x: u16 = (S {}).method(0u32);
|
||||
| ^^^^^^^^^^^^^^----^
|
||||
| |
|
||||
| this argument influences the return type of `method`
|
||||
note: associated function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:7:8
|
||||
|
|
||||
LL | fn method<T>(&self, x: T) -> T {
|
||||
| ^^^^^^ ----
|
||||
help: change the type of the numeric literal from `u32` to `u16`
|
||||
|
|
||||
LL | let x: u16 = (S {}).method(0u16);
|
||||
| ~~~
|
||||
|
||||
error[E0308]: arguments to this function are incorrect
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
|
||||
|
|
||||
LL | function(0u32, 8u8)
|
||||
| ^^^^^^^^ ---- --- expected `bool`, found `u8`
|
||||
| |
|
||||
| expected `()`, found `u32`
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
|
||||
|
|
||||
LL | function(0u32, 8u8)
|
||||
| ^^^^^^^^^----^^^^^^
|
||||
| |
|
||||
| this argument influences the return type of `function`
|
||||
note: function defined here
|
||||
--> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
|
||||
|
|
||||
LL | fn function<T>(x: T, y: bool) -> T {
|
||||
| ^^^^^^^^ ---- -------
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
|
@ -8,6 +8,13 @@ LL | fn main() { test(Ok(())); }
|
|||
|
|
||||
= note: expected enum `Option<()>`
|
||||
found unit type `()`
|
||||
help: the type constructed contains `()` due to the type of the argument passed
|
||||
--> $DIR/issue-46112.rs:9:18
|
||||
|
|
||||
LL | fn main() { test(Ok(())); }
|
||||
| ^^^--^
|
||||
| |
|
||||
| this argument influences the type of `Ok`
|
||||
note: tuple variant defined here
|
||||
--> $SRC_DIR/core/src/result.rs:LL:COL
|
||||
help: try wrapping the expression in `Some`
|
||||
|
|
|
@ -14,6 +14,13 @@ LL | <F as FnOnce(&mut u8)>::call_once(f, 1)
|
|||
|
|
||||
= note: expected tuple `(&mut u8,)`
|
||||
found type `{integer}`
|
||||
help: the return type of this call is `{integer}` due to the type of the argument passed
|
||||
--> $DIR/issue-84768.rs:7:5
|
||||
|
|
||||
LL | <F as FnOnce(&mut u8)>::call_once(f, 1)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^
|
||||
| |
|
||||
| this argument influences the return type of `FnOnce`
|
||||
note: associated function defined here
|
||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
||||
|
||||
|
|
|
@ -21,6 +21,13 @@ LL | <i32 as Add<i32>>::add(1u32, 2);
|
|||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/ufcs-qpath-self-mismatch.rs:7:5
|
||||
|
|
||||
LL | <i32 as Add<i32>>::add(1u32, 2);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^----^^^^
|
||||
| |
|
||||
| this argument influences the return type of `Add`
|
||||
note: associated function defined here
|
||||
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
||||
help: change the type of the numeric literal from `u32` to `i32`
|
||||
|
@ -36,6 +43,13 @@ LL | <i32 as Add<i32>>::add(1, 2u32);
|
|||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
help: the return type of this call is `u32` due to the type of the argument passed
|
||||
--> $DIR/ufcs-qpath-self-mismatch.rs:9:5
|
||||
|
|
||||
LL | <i32 as Add<i32>>::add(1, 2u32);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^----^
|
||||
| |
|
||||
| this argument influences the return type of `Add`
|
||||
note: associated function defined here
|
||||
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
||||
help: change the type of the numeric literal from `u32` to `i32`
|
||||
|
|
Loading…
Add table
Reference in a new issue