@@ -1043,17 +1043,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
10431043 && let ty:: Tuple ( inputs) = * sig. tupled_inputs_ty . kind ( )
10441044 && inputs. is_empty ( )
10451045 && self . tcx . is_lang_item ( trait_pred. def_id ( ) , LangItem :: Future )
1046+ && let ObligationCauseCode :: FunctionArg { arg_hir_id, .. } = obligation. cause . code ( )
1047+ && let hir:: Node :: Expr ( hir:: Expr { kind : hir:: ExprKind :: Closure ( ..) , .. } ) =
1048+ self . tcx . hir_node ( * arg_hir_id)
10461049 && let Some ( hir:: Node :: Expr ( hir:: Expr {
1047- kind :
1048- hir:: ExprKind :: Closure ( hir:: Closure {
1049- kind : hir:: ClosureKind :: CoroutineClosure ( CoroutineDesugaring :: Async ) ,
1050- fn_arg_span : Some ( arg_span) ,
1051- ..
1052- } ) ,
1053- ..
1050+ kind : hir:: ExprKind :: Closure ( closure) , ..
10541051 } ) ) = self . tcx . hir_get_if_local ( def_id)
1055- && obligation. cause . span . contains ( * arg_span)
1052+ && let hir:: ClosureKind :: CoroutineClosure ( CoroutineDesugaring :: Async ) = closure. kind
1053+ && let Some ( arg_span) = closure. fn_arg_span
1054+ && obligation. cause . span . contains ( arg_span)
10561055 {
1056+ let mut body = self . tcx . hir_body ( closure. body ) . value ;
1057+ let peeled = body. peel_blocks ( ) . peel_drop_temps ( ) ;
1058+ if let hir:: ExprKind :: Closure ( inner) = peeled. kind {
1059+ body = self . tcx . hir_body ( inner. body ) . value ;
1060+ }
1061+ if !matches ! ( body. peel_blocks( ) . peel_drop_temps( ) . kind, hir:: ExprKind :: Block ( ..) ) {
1062+ return false ;
1063+ }
1064+
10571065 let sm = self . tcx . sess . source_map ( ) ;
10581066 let removal_span = if let Ok ( snippet) =
10591067 sm. span_to_snippet ( arg_span. with_hi ( arg_span. hi ( ) + rustc_span:: BytePos ( 1 ) ) )
@@ -1062,7 +1070,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
10621070 // There's a space after `||`, include it in the removal
10631071 arg_span. with_hi ( arg_span. hi ( ) + rustc_span:: BytePos ( 1 ) )
10641072 } else {
1065- * arg_span
1073+ arg_span
10661074 } ;
10671075 err. span_suggestion_verbose (
10681076 removal_span,
@@ -1102,23 +1110,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
11021110 . collect :: < Vec < _ > > ( )
11031111 . join ( ", " ) ;
11041112
1105- if matches ! ( obligation . cause . code ( ) , ObligationCauseCode :: FunctionArg { .. } )
1113+ if let ObligationCauseCode :: FunctionArg { arg_hir_id , .. } = obligation . cause . code ( )
11061114 && obligation. cause . span . can_be_used_for_suggestions ( )
11071115 {
1108- let ( span, sugg) = if let Some ( snippet) =
1109- self . tcx . sess . source_map ( ) . span_to_snippet ( obligation. cause . span ) . ok ( )
1110- && snippet. starts_with ( "|" )
1111- {
1112- ( obligation. cause . span , format ! ( "({snippet})({args})" ) )
1113- } else {
1114- ( obligation. cause . span . shrink_to_hi ( ) , format ! ( "({args})" ) )
1116+ let span = obligation. cause . span ;
1117+
1118+ let arg_expr = match self . tcx . hir_node ( * arg_hir_id) {
1119+ hir:: Node :: Expr ( expr) => Some ( expr) ,
1120+ _ => None ,
11151121 } ;
11161122
1117- // When the obligation error has been ensured to have been caused by
1118- // an argument, the `obligation.cause.span` points at the expression
1119- // of the argument, so we can provide a suggestion. Otherwise, we give
1120- // a more general note.
1121- err. span_suggestion_verbose ( span, msg, sugg, Applicability :: HasPlaceholders ) ;
1123+ let is_closure_expr =
1124+ arg_expr. is_some_and ( |expr| matches ! ( expr. kind, hir:: ExprKind :: Closure ( ..) ) ) ;
1125+
1126+ // If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,
1127+ // which doesn't help and is often outright wrong.
1128+ if args. is_empty ( )
1129+ && let Some ( expr) = arg_expr
1130+ && let hir:: ExprKind :: Closure ( closure) = expr. kind
1131+ {
1132+ let mut body = self . tcx . hir_body ( closure. body ) . value ;
1133+
1134+ // Async closures desugar to a closure returning a coroutine
1135+ if let hir:: ClosureKind :: CoroutineClosure ( hir:: CoroutineDesugaring :: Async ) =
1136+ closure. kind
1137+ {
1138+ let peeled = body. peel_blocks ( ) . peel_drop_temps ( ) ;
1139+ if let hir:: ExprKind :: Closure ( inner) = peeled. kind {
1140+ body = self . tcx . hir_body ( inner. body ) . value ;
1141+ }
1142+ }
1143+
1144+ let peeled_body = body. peel_blocks ( ) . peel_drop_temps ( ) ;
1145+ if let hir:: ExprKind :: Call ( callee, call_args) = peeled_body. kind
1146+ && call_args. is_empty ( )
1147+ && let hir:: ExprKind :: Block ( ..) = callee. peel_blocks ( ) . peel_drop_temps ( ) . kind
1148+ {
1149+ return false ;
1150+ }
1151+ }
1152+
1153+ if is_closure_expr {
1154+ err. multipart_suggestions (
1155+ msg,
1156+ vec ! [ vec![
1157+ ( span. shrink_to_lo( ) , "(" . to_string( ) ) ,
1158+ ( span. shrink_to_hi( ) , format!( ")({args})" ) ) ,
1159+ ] ] ,
1160+ Applicability :: HasPlaceholders ,
1161+ ) ;
1162+ } else {
1163+ err. span_suggestion_verbose (
1164+ span. shrink_to_hi ( ) ,
1165+ msg,
1166+ format ! ( "({args})" ) ,
1167+ Applicability :: HasPlaceholders ,
1168+ ) ;
1169+ }
11221170 } else if let DefIdOrName :: DefId ( def_id) = def_id_or_name {
11231171 let name = match self . tcx . hir_get_if_local ( def_id) {
11241172 Some ( hir:: Node :: Expr ( hir:: Expr {
0 commit comments