Skip to content

Commit

Permalink
Emit a hint for bad call return types due to generic arguments
Browse files Browse the repository at this point in the history
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 rust-lang#43608.
  • Loading branch information
sulami committed Jan 13, 2023
1 parent 56ee65a commit a3cf382
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 0 deletions.
74 changes: 74 additions & 0 deletions compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}
}
7 changes: 7 additions & 0 deletions tests/ui/closures/issue-84128.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down
21 changes: 21 additions & 0 deletions tests/ui/closures/issue-87461.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

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

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

Expand Down
7 changes: 7 additions & 0 deletions tests/ui/generic-associated-types/missing-bounds.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/mismatched_types/issue-35030.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
21 changes: 21 additions & 0 deletions tests/ui/suggestions/args-instead-of-tuple-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

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

Expand Down
7 changes: 7 additions & 0 deletions tests/ui/suggestions/sugg-else-for-closure.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/traits/issue-52893.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand Down
28 changes: 28 additions & 0 deletions tests/ui/type/wrong-call-return-type-due-to-generic-arg.rs
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit a3cf382

Please sign in to comment.