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:
Matthias Krüger 2023-01-14 13:04:26 +01:00 committed by GitHub
commit 108b5f462b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 338 additions and 0 deletions

View File

@ -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,
}
}
}

View File

@ -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
|

View File

@ -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

View File

@ -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
|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
|

View 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
}

View 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`.

View File

@ -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`

View File

@ -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

View File

@ -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`