Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit a hint for bad call return types due to generic arguments #106752

Merged
merged 1 commit into from
Jan 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`
Comment on lines +9 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm noticing that there are some cases where the existing machinery to detect the source of expectations isn't as robust as we'd like, but that's independent of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #106821

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`
Comment on lines +14 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case should probably have been silenced.

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