Skip to content

Commit a3cf382

Browse files
committed
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.
1 parent 56ee65a commit a3cf382

13 files changed

+338
-0
lines changed

compiler/rustc_hir_typeck/src/demand.rs

+74
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8585
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
8686
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
8787
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
88+
self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
8889
}
8990

9091
/// Requires that the two types unify, and prints an error message if
@@ -1941,4 +1942,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19411942
err.span_label(block.span, "this block is missing a tail expression");
19421943
}
19431944
}
1945+
1946+
fn check_wrong_return_type_due_to_generic_arg(
1947+
&self,
1948+
err: &mut Diagnostic,
1949+
expr: &hir::Expr<'_>,
1950+
checked_ty: Ty<'tcx>,
1951+
) {
1952+
let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
1953+
enum CallableKind {
1954+
Function,
1955+
Method,
1956+
Constructor,
1957+
}
1958+
let mut maybe_emit_help = |def_id: hir::def_id::DefId,
1959+
callable: rustc_span::symbol::Ident,
1960+
args: &[hir::Expr<'_>],
1961+
kind: CallableKind| {
1962+
let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
1963+
let fn_ty = self.tcx.bound_type_of(def_id).0;
1964+
if !fn_ty.is_fn() {
1965+
return;
1966+
}
1967+
let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
1968+
let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
1969+
if matches!(arg.kind(), ty::Param(_))
1970+
&& fn_sig.output().contains(arg)
1971+
&& self.node_ty(args[arg_idx].hir_id) == checked_ty
1972+
{
1973+
let mut multi_span: MultiSpan = parent_expr.span.into();
1974+
multi_span.push_span_label(
1975+
args[arg_idx].span,
1976+
format!(
1977+
"this argument influences the {} of `{}`",
1978+
if matches!(kind, CallableKind::Constructor) {
1979+
"type"
1980+
} else {
1981+
"return type"
1982+
},
1983+
callable
1984+
),
1985+
);
1986+
err.span_help(
1987+
multi_span,
1988+
format!(
1989+
"the {} `{}` due to the type of the argument passed",
1990+
match kind {
1991+
CallableKind::Function => "return type of this call is",
1992+
CallableKind::Method => "return type of this call is",
1993+
CallableKind::Constructor => "type constructed contains",
1994+
},
1995+
checked_ty
1996+
),
1997+
);
1998+
}
1999+
};
2000+
match parent_expr.kind {
2001+
hir::ExprKind::Call(fun, args) => {
2002+
let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
2003+
let hir::def::Res::Def(kind, def_id) = path.res else { return; };
2004+
let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
2005+
CallableKind::Constructor
2006+
} else {
2007+
CallableKind::Function
2008+
};
2009+
maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
2010+
}
2011+
hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
2012+
let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
2013+
maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
2014+
}
2015+
_ => return,
2016+
}
2017+
}
19442018
}

tests/ui/closures/issue-84128.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ LL | Foo(())
66
| |
77
| arguments to this struct are incorrect
88
|
9+
help: the type constructed contains `()` due to the type of the argument passed
10+
--> $DIR/issue-84128.rs:13:9
11+
|
12+
LL | Foo(())
13+
| ^^^^--^
14+
| |
15+
| this argument influences the type of `Foo`
916
note: tuple struct defined here
1017
--> $DIR/issue-84128.rs:5:8
1118
|

tests/ui/closures/issue-87461.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ LL | Ok(())
66
| |
77
| arguments to this enum variant are incorrect
88
|
9+
help: the type constructed contains `()` due to the type of the argument passed
10+
--> $DIR/issue-87461.rs:10:5
11+
|
12+
LL | Ok(())
13+
| ^^^--^
14+
| |
15+
| this argument influences the type of `Ok`
916
note: tuple variant defined here
1017
--> $SRC_DIR/core/src/result.rs:LL:COL
1118

@@ -17,6 +24,13 @@ LL | Ok(())
1724
| |
1825
| arguments to this enum variant are incorrect
1926
|
27+
help: the type constructed contains `()` due to the type of the argument passed
28+
--> $DIR/issue-87461.rs:17:5
29+
|
30+
LL | Ok(())
31+
| ^^^--^
32+
| |
33+
| this argument influences the type of `Ok`
2034
note: tuple variant defined here
2135
--> $SRC_DIR/core/src/result.rs:LL:COL
2236

@@ -28,6 +42,13 @@ LL | Ok(())
2842
| |
2943
| arguments to this enum variant are incorrect
3044
|
45+
help: the type constructed contains `()` due to the type of the argument passed
46+
--> $DIR/issue-87461.rs:26:9
47+
|
48+
LL | Ok(())
49+
| ^^^--^
50+
| |
51+
| this argument influences the type of `Ok`
3152
note: tuple variant defined here
3253
--> $SRC_DIR/core/src/result.rs:LL:COL
3354

tests/ui/generic-associated-types/missing-bounds.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ LL | A(self.0 + rhs.0)
2323
|
2424
= note: expected type parameter `B`
2525
found associated type `<B as Add>::Output`
26+
help: the type constructed contains `<B as Add>::Output` due to the type of the argument passed
27+
--> $DIR/missing-bounds.rs:11:9
28+
|
29+
LL | A(self.0 + rhs.0)
30+
| ^^--------------^
31+
| |
32+
| this argument influences the type of `A`
2633
note: tuple struct defined here
2734
--> $DIR/missing-bounds.rs:5:8
2835
|

tests/ui/mismatched_types/issue-35030.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ LL | Some(true)
1111
|
1212
= note: expected type parameter `bool` (type parameter `bool`)
1313
found type `bool` (`bool`)
14+
help: the type constructed contains `bool` due to the type of the argument passed
15+
--> $DIR/issue-35030.rs:9:9
16+
|
17+
LL | Some(true)
18+
| ^^^^^----^
19+
| |
20+
| this argument influences the type of `Some`
1421
note: tuple variant defined here
1522
--> $SRC_DIR/core/src/option.rs:LL:COL
1623

tests/ui/suggestions/args-instead-of-tuple-errors.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ LL | let _: Option<(i32, bool)> = Some(1, 2);
1111
| ^
1212
= note: expected tuple `(i32, bool)`
1313
found type `{integer}`
14+
help: the type constructed contains `{integer}` due to the type of the argument passed
15+
--> $DIR/args-instead-of-tuple-errors.rs:6:34
16+
|
17+
LL | let _: Option<(i32, bool)> = Some(1, 2);
18+
| ^^^^^-^^^^
19+
| |
20+
| this argument influences the type of `Some`
1421
note: tuple variant defined here
1522
--> $SRC_DIR/core/src/option.rs:LL:COL
1623
help: remove the extra argument
@@ -64,6 +71,13 @@ LL | let _: Option<(i32,)> = Some(5_usize);
6471
|
6572
= note: expected tuple `(i32,)`
6673
found type `usize`
74+
help: the type constructed contains `usize` due to the type of the argument passed
75+
--> $DIR/args-instead-of-tuple-errors.rs:14:29
76+
|
77+
LL | let _: Option<(i32,)> = Some(5_usize);
78+
| ^^^^^-------^
79+
| |
80+
| this argument influences the type of `Some`
6781
note: tuple variant defined here
6882
--> $SRC_DIR/core/src/option.rs:LL:COL
6983

@@ -77,6 +91,13 @@ LL | let _: Option<(i32,)> = Some((5_usize));
7791
|
7892
= note: expected tuple `(i32,)`
7993
found type `usize`
94+
help: the type constructed contains `usize` due to the type of the argument passed
95+
--> $DIR/args-instead-of-tuple-errors.rs:17:29
96+
|
97+
LL | let _: Option<(i32,)> = Some((5_usize));
98+
| ^^^^^---------^
99+
| |
100+
| this argument influences the type of `Some`
80101
note: tuple variant defined here
81102
--> $SRC_DIR/core/src/option.rs:LL:COL
82103

tests/ui/suggestions/sugg-else-for-closure.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
88
|
99
= note: expected reference `&str`
1010
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
11+
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
12+
--> $DIR/sugg-else-for-closure.rs:6:14
13+
|
14+
LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
15+
| ^^^^^^^^^^^^-------------------------------^
16+
| |
17+
| this argument influences the return type of `unwrap_or`
1118
note: associated function defined here
1219
--> $SRC_DIR/core/src/option.rs:LL:COL
1320
help: try calling `unwrap_or_else` instead

tests/ui/traits/issue-52893.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ LL | builder.push(output);
1111
|
1212
= note: expected type parameter `F`
1313
found struct `Class<P>`
14+
help: the return type of this call is `Class<P>` due to the type of the argument passed
15+
--> $DIR/issue-52893.rs:53:9
16+
|
17+
LL | builder.push(output);
18+
| ^^^^^^^^^^^^^------^
19+
| |
20+
| this argument influences the return type of `push`
1421
note: associated function defined here
1522
--> $DIR/issue-52893.rs:11:8
1623
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
fn function<T>(x: T, y: bool) -> T {
2+
x
3+
}
4+
5+
struct S {}
6+
impl S {
7+
fn method<T>(&self, x: T) -> T {
8+
x
9+
}
10+
}
11+
12+
fn wrong_arg_type(x: u32) -> u32 {
13+
x
14+
}
15+
16+
fn main() {
17+
// Should not trigger.
18+
let x = wrong_arg_type(0u16); //~ ERROR mismatched types
19+
let x: u16 = function(0, 0u8); //~ ERROR mismatched types
20+
21+
// Should trigger exactly once for the first argument.
22+
let x: u16 = function(0u32, 0u8); //~ ERROR arguments to this function are incorrect
23+
24+
// Should trigger.
25+
let x: u16 = function(0u32, true); //~ ERROR mismatched types
26+
let x: u16 = (S {}).method(0u32); //~ ERROR mismatched types
27+
function(0u32, 8u8) //~ ERROR arguments to this function are incorrect
28+
}

0 commit comments

Comments
 (0)