@@ -61,55 +61,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
61
61
pointing_at_return_type
62
62
}
63
63
64
- /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
65
- /// the ctor would successfully solve the type mismatch and if so, suggest it:
64
+ /// When encountering an fn-like type, try accessing the output of the type
65
+ /// // and suggesting calling it if it satisfies a predicate (i.e. if the
66
+ /// output has a method or a field):
66
67
/// ```compile_fail,E0308
67
68
/// fn foo(x: usize) -> usize { x }
68
69
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
69
70
/// ```
70
- fn suggest_fn_call (
71
+ pub ( crate ) fn suggest_fn_call (
71
72
& self ,
72
73
err : & mut Diagnostic ,
73
74
expr : & hir:: Expr < ' _ > ,
74
- expected : Ty < ' tcx > ,
75
75
found : Ty < ' tcx > ,
76
+ can_satisfy : impl FnOnce ( Ty < ' tcx > ) -> bool ,
76
77
) -> bool {
77
- let ( def_id, output, inputs) = match * found. kind ( ) {
78
- ty:: FnDef ( def_id, _) => {
79
- let fn_sig = found. fn_sig ( self . tcx ) ;
80
- ( def_id, fn_sig. output ( ) , fn_sig. inputs ( ) . skip_binder ( ) . len ( ) )
81
- }
82
- ty:: Closure ( def_id, substs) => {
83
- let fn_sig = substs. as_closure ( ) . sig ( ) ;
84
- ( def_id, fn_sig. output ( ) , fn_sig. inputs ( ) . skip_binder ( ) . len ( ) - 1 )
85
- }
86
- ty:: Opaque ( def_id, substs) => {
87
- let sig = self . tcx . bound_item_bounds ( def_id) . subst ( self . tcx , substs) . iter ( ) . find_map ( |pred| {
88
- if let ty:: PredicateKind :: Projection ( proj) = pred. kind ( ) . skip_binder ( )
89
- && Some ( proj. projection_ty . item_def_id ) == self . tcx . lang_items ( ) . fn_once_output ( )
90
- // args tuple will always be substs[1]
91
- && let ty:: Tuple ( args) = proj. projection_ty . substs . type_at ( 1 ) . kind ( )
92
- {
93
- Some ( (
94
- pred. kind ( ) . rebind ( proj. term . ty ( ) . unwrap ( ) ) ,
95
- args. len ( ) ,
96
- ) )
78
+ // Autoderef is useful here because sometimes we box callables, etc.
79
+ let Some ( ( def_id, output, inputs) ) = self . autoderef ( expr. span , found) . silence_errors ( ) . find_map ( |( found, _) | {
80
+ match * found. kind ( ) {
81
+ ty:: FnPtr ( fn_sig) => Some ( ( None , fn_sig. output ( ) , fn_sig. inputs ( ) . skip_binder ( ) . len ( ) ) ) ,
82
+ ty:: FnDef ( def_id, _) => {
83
+ let fn_sig = found. fn_sig ( self . tcx ) ;
84
+ Some ( ( Some ( def_id) , fn_sig. output ( ) , fn_sig. inputs ( ) . skip_binder ( ) . len ( ) ) )
85
+ }
86
+ ty:: Closure ( def_id, substs) => {
87
+ let fn_sig = substs. as_closure ( ) . sig ( ) ;
88
+ Some ( ( Some ( def_id) , fn_sig. output ( ) , fn_sig. inputs ( ) . skip_binder ( ) . len ( ) - 1 ) )
89
+ }
90
+ ty:: Opaque ( def_id, substs) => {
91
+ let sig = self . tcx . bound_item_bounds ( def_id) . subst ( self . tcx , substs) . iter ( ) . find_map ( |pred| {
92
+ if let ty:: PredicateKind :: Projection ( proj) = pred. kind ( ) . skip_binder ( )
93
+ && Some ( proj. projection_ty . item_def_id ) == self . tcx . lang_items ( ) . fn_once_output ( )
94
+ // args tuple will always be substs[1]
95
+ && let ty:: Tuple ( args) = proj. projection_ty . substs . type_at ( 1 ) . kind ( )
96
+ {
97
+ Some ( (
98
+ pred. kind ( ) . rebind ( proj. term . ty ( ) . unwrap ( ) ) ,
99
+ args. len ( ) ,
100
+ ) )
101
+ } else {
102
+ None
103
+ }
104
+ } ) ;
105
+ if let Some ( ( output, inputs) ) = sig {
106
+ Some ( ( Some ( def_id) , output, inputs) )
97
107
} else {
98
108
None
99
109
}
100
- } ) ;
101
- if let Some ( ( output, inputs) ) = sig {
102
- ( def_id, output, inputs)
103
- } else {
104
- return false ;
105
110
}
111
+ _ => None ,
106
112
}
107
- _ => return false ,
108
- } ;
113
+ } ) else { return false ; } ;
109
114
110
115
let output = self . replace_bound_vars_with_fresh_vars ( expr. span , infer:: FnCall , output) ;
111
- let output = self . normalize_associated_types_in ( expr. span , output) ;
112
- if !output. is_ty_var ( ) && self . can_coerce ( output, expected) {
116
+ // We don't want to register any extra obligations, which should be
117
+ // implied by wf, but also because that would possibly result in
118
+ // erroneous errors later on.
119
+ let infer:: InferOk { value : output, obligations : _ } =
120
+ self . normalize_associated_types_in_as_infer_ok ( expr. span , output) ;
121
+ if !output. is_ty_var ( ) && can_satisfy ( output) {
113
122
let ( sugg_call, mut applicability) = match inputs {
114
123
0 => ( "" . to_string ( ) , Applicability :: MachineApplicable ) ,
115
124
1 ..=4 => (
@@ -119,11 +128,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
119
128
_ => ( "..." . to_string ( ) , Applicability :: HasPlaceholders ) ,
120
129
} ;
121
130
122
- let msg = match self . tcx . def_kind ( def_id) {
123
- DefKind :: Fn => "call this function" ,
124
- DefKind :: Closure | DefKind :: OpaqueTy => "call this closure" ,
125
- DefKind :: Ctor ( CtorOf :: Struct , _) => "instantiate this tuple struct" ,
126
- DefKind :: Ctor ( CtorOf :: Variant , _) => "instantiate this tuple variant" ,
131
+ let msg = match def_id . map ( |def_id| self . tcx . def_kind ( def_id) ) {
132
+ Some ( DefKind :: Fn ) => "call this function" ,
133
+ Some ( DefKind :: Closure | DefKind :: OpaqueTy ) => "call this closure" ,
134
+ Some ( DefKind :: Ctor ( CtorOf :: Struct , _) ) => "instantiate this tuple struct" ,
135
+ Some ( DefKind :: Ctor ( CtorOf :: Variant , _) ) => "instantiate this tuple variant" ,
127
136
_ => "call this function" ,
128
137
} ;
129
138
@@ -178,12 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
178
187
} else {
179
188
err. span_suggestion ( sp, & msg, suggestion, applicability) ;
180
189
}
181
- } else if let ( ty:: FnDef ( def_id, ..) , true ) =
182
- ( & found. kind ( ) , self . suggest_fn_call ( err, expr, expected, found) )
190
+ } else if self . suggest_fn_call ( err, expr, found, |output| self . can_coerce ( output, expected) )
191
+ && let ty:: FnDef ( def_id, ..) = & found. kind ( )
192
+ && let Some ( sp) = self . tcx . hir ( ) . span_if_local ( * def_id)
183
193
{
184
- if let Some ( sp) = self . tcx . hir ( ) . span_if_local ( * def_id) {
185
- err. span_label ( sp, format ! ( "{found} defined here" ) ) ;
186
- }
194
+ err. span_label ( sp, format ! ( "{found} defined here" ) ) ;
187
195
} else if !self . check_for_cast ( err, expr, found, expected, expected_ty_expr) {
188
196
let methods = self . get_conversion_methods ( expr. span , expected, found, expr. hir_id ) ;
189
197
if !methods. is_empty ( ) {
0 commit comments