From 0487f0c0c38c1bae95bd786bc044307a3ff082e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 8 Nov 2019 18:04:05 -0800 Subject: [PATCH 1/3] Suggest calling async closure when needed When using an async closure as a value in a place that expects a future, suggest calling the closure. Fix #65923. --- src/librustc/traits/error_reporting.rs | 153 ++++++++++++------ ...as-arg-where-it-should-have-been-called.rs | 3 + ...rg-where-it-should-have-been-called.stderr | 21 ++- ...as-arg-where-it-should-have-been-called.rs | 2 + ...rg-where-it-should-have-been-called.stderr | 19 ++- 5 files changed, 143 insertions(+), 55 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index c7e51ff321771..4a51ce2b780df 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1238,60 +1238,109 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { points_at_arg: bool, ) { let self_ty = trait_ref.self_ty(); - match self_ty.kind { + let (def_id, output_ty, callable) = match self_ty.kind { + ty::Closure(def_id, substs) => { + (def_id, self.closure_sig(def_id, substs).output(), "closure") + } ty::FnDef(def_id, _) => { - // We tried to apply the bound to an `fn`. Check whether calling it would evaluate - // to a type that *would* satisfy the trait binding. If it would, suggest calling - // it: `bar(foo)` -> `bar(foo)`. This case is *very* likely to be hit if `foo` is - // `async`. - let output_ty = self_ty.fn_sig(self.tcx).output(); - let new_trait_ref = ty::TraitRef { - def_id: trait_ref.def_id(), - substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]), - }; - let obligation = Obligation::new( - obligation.cause.clone(), - obligation.param_env, - new_trait_ref.to_predicate(), - ); - match self.evaluate_obligation(&obligation) { - Ok(EvaluationResult::EvaluatedToOk) | - Ok(EvaluationResult::EvaluatedToOkModuloRegions) | - Ok(EvaluationResult::EvaluatedToAmbig) => { - if let Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) = self.tcx.hir().get_if_local(def_id) { - let body = self.tcx.hir().body(*body_id); - let msg = "use parentheses to call the function"; - let snippet = format!( - "{}({})", - ident, - body.params.iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }).collect::>().join(", "), - ); - // When the obligation error has been ensured to have been caused by - // an argument, the `obligation.cause.span` points at the expression - // of the argument, so we can provide a suggestion. This is signaled - // by `points_at_arg`. Otherwise, we give a more general note. - if points_at_arg { - err.span_suggestion( - obligation.cause.span, - msg, - snippet, - Applicability::HasPlaceholders, - ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); - } - } + (def_id, self_ty.fn_sig(self.tcx).output(), "function") + } + _ => return, + }; + let msg = format!("use parentheses to call the {}", callable); + // We tried to apply the bound to an `fn` or closure. Check whether calling it would + // evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + // it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. + + let new_trait_ref = ty::TraitRef { + def_id: trait_ref.def_id(), + substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]), + }; + let obligation = Obligation::new( + obligation.cause.clone(), + obligation.param_env, + new_trait_ref.to_predicate(), + ); + let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { + Some(format!("{}", name)) + } + _ => { + err.note(&msg); + None + } + } + }; + match self.evaluate_obligation(&obligation) { + Ok(EvaluationResult::EvaluatedToOk) | + Ok(EvaluationResult::EvaluatedToOkModuloRegions) | + Ok(EvaluationResult::EvaluatedToAmbig) => { + let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. + let snippet = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, decl, _, span, ..), + .. + })) => { + err.span_label(*span, "consider calling this closure"); + let hir_id = match hir.as_local_hir_id(def_id) { + Some(hir_id) => hir_id, + None => return, + }; + let parent_node = hir.get_parent_node(hir_id); + let name = match hir.find(parent_node) { + Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Local(local), .. + })) => match get_name(err, &local.pat.kind) { + Some(name) => name, + None => return, + }, + // Different to previous arm because one is `&hir::Local` and the other + // is `P`. + Some(hir::Node::Local(local)) => match get_name(err, &local.pat.kind) { + Some(name) => name, + None => return, + }, + _ => return, + }; + let args = decl.inputs.iter() + .map(|_| "_") + .collect::>().join(", "); + format!("{}({})", name, args) + } + Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Fn(.., body_id), + .. + })) => { + err.span_label(ident.span, "consider calling this function"); + let body = hir.body(*body_id); + let args = body.params.iter() + .map(|arg| match &arg.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => ident.to_string(), + _ => "_".to_string(), + }).collect::>().join(", "); + format!("{}({})", ident, args) } - _ => {} + _ => return, + }; + if points_at_arg { + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. This is signaled + // by `points_at_arg`. Otherwise, we give a more general note. + err.span_suggestion( + obligation.cause.span, + &msg, + snippet, + Applicability::HasPlaceholders, + ); + } else { + err.help(&format!("{}: `{}`", msg, snippet)); } } _ => {} diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs index a2d2ba145bc5e..156162c9027c3 100644 --- a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs +++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs @@ -1,4 +1,5 @@ // edition:2018 +#![feature(async_closure)] use std::future::Future; async fn foo() {} @@ -7,4 +8,6 @@ fn bar(f: impl Future) {} fn main() { bar(foo); //~ERROR E0277 + let async_closure = async || (); + bar(async_closure); //~ERROR E0277 } diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 1cc704b443366..05583876a066c 100644 --- a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,6 +1,9 @@ error[E0277]: the trait bound `fn() -> impl std::future::Future {foo}: std::future::Future` is not satisfied - --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:9:9 + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:9 | +LL | async fn foo() {} + | --- consider calling this function +LL | LL | fn bar(f: impl Future) {} | --- ----------------- required by this bound in `bar` ... @@ -10,6 +13,20 @@ LL | bar(foo); | the trait `std::future::Future` is not implemented for `fn() -> impl std::future::Future {foo}` | help: use parentheses to call the function: `foo()` -error: aborting due to previous error +error[E0277]: the trait bound `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]: std::future::Future` is not satisfied + --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:9 + | +LL | fn bar(f: impl Future) {} + | --- ----------------- required by this bound in `bar` +... +LL | let async_closure = async || (); + | -------- consider calling this closure +LL | bar(async_closure); + | ^^^^^^^^^^^^^ + | | + | the trait `std::future::Future` is not implemented for `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:36]` + | help: use parentheses to call the closure: `async_closure()` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs index acd149c5854e8..4303e5c540569 100644 --- a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs +++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs @@ -15,4 +15,6 @@ fn bar(f: impl T) {} fn main() { bar(foo); //~ERROR E0277 + let closure = || S; + bar(closure); //~ERROR E0277 } diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index 7a49031bde7c2..91f60e8f426c4 100644 --- a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,6 +1,9 @@ error[E0277]: the trait bound `fn() -> impl T {foo}: T` is not satisfied --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:17:9 | +LL | fn foo() -> impl T { S } + | --- consider calling this function +LL | LL | fn bar(f: impl T) {} | --- ------- required by this bound in `bar` ... @@ -10,6 +13,20 @@ LL | bar(foo); | the trait `T` is not implemented for `fn() -> impl T {foo}` | help: use parentheses to call the function: `foo()` -error: aborting due to previous error +error[E0277]: the trait bound `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:23]: T` is not satisfied + --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:19:9 + | +LL | fn bar(f: impl T) {} + | --- ------- required by this bound in `bar` +... +LL | let closure = || S; + | -- consider calling this closure +LL | bar(closure); + | ^^^^^^^ + | | + | the trait `T` is not implemented for `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:23]` + | help: use parentheses to call the closure: `closure()` + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. From d7efa5bd6a22a07cf50a7abe0542a830d91aa182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 16 Nov 2019 17:10:13 -0800 Subject: [PATCH 2/3] review comments --- src/librustc/traits/error_reporting.rs | 172 +++++++++++++------------ 1 file changed, 91 insertions(+), 81 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 4a51ce2b780df..b1652f58772d0 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1230,6 +1230,24 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn mk_obligation_for_def_id( + &self, + def_id: DefId, + output_ty: Ty<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> PredicateObligation<'tcx> { + let new_trait_ref = ty::TraitRef { + def_id, + substs: self.tcx.mk_substs_trait(output_ty, &[]), + }; + Obligation::new(cause, param_env, new_trait_ref.to_predicate()) + } + + + /// We tried to apply the bound to an `fn` or closure. Check whether calling it would + /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, @@ -1248,19 +1266,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => return, }; let msg = format!("use parentheses to call the {}", callable); - // We tried to apply the bound to an `fn` or closure. Check whether calling it would - // evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling - // it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. - let new_trait_ref = ty::TraitRef { - def_id: trait_ref.def_id(), - substs: self.tcx.mk_substs_trait(output_ty.skip_binder(), &[]), - }; - let obligation = Obligation::new( + let obligation = self.mk_obligation_for_def_id( + trait_ref.def_id(), + output_ty.skip_binder(), obligation.cause.clone(), obligation.param_env, - new_trait_ref.to_predicate(), ); + let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. @@ -1277,73 +1290,72 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { match self.evaluate_obligation(&obligation) { Ok(EvaluationResult::EvaluatedToOk) | Ok(EvaluationResult::EvaluatedToOkModuloRegions) | - Ok(EvaluationResult::EvaluatedToAmbig) => { - let hir = self.tcx.hir(); - // Get the name of the callable and the arguments to be used in the suggestion. - let snippet = match hir.get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_, decl, _, span, ..), - .. - })) => { - err.span_label(*span, "consider calling this closure"); - let hir_id = match hir.as_local_hir_id(def_id) { - Some(hir_id) => hir_id, - None => return, - }; - let parent_node = hir.get_parent_node(hir_id); - let name = match hir.find(parent_node) { - Some(hir::Node::Stmt(hir::Stmt { - kind: hir::StmtKind::Local(local), .. - })) => match get_name(err, &local.pat.kind) { - Some(name) => name, - None => return, - }, - // Different to previous arm because one is `&hir::Local` and the other - // is `P`. - Some(hir::Node::Local(local)) => match get_name(err, &local.pat.kind) { - Some(name) => name, - None => return, - }, - _ => return, - }; - let args = decl.inputs.iter() - .map(|_| "_") - .collect::>().join(", "); - format!("{}({})", name, args) - } - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) => { - err.span_label(ident.span, "consider calling this function"); - let body = hir.body(*body_id); - let args = body.params.iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }).collect::>().join(", "); - format!("{}({})", ident, args) - } + Ok(EvaluationResult::EvaluatedToAmbig) => {} + _ => return, + } + let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. + let snippet = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(_, decl, _, span, ..), + .. + })) => { + err.span_label(*span, "consider calling this closure"); + let hir_id = match hir.as_local_hir_id(def_id) { + Some(hir_id) => hir_id, + None => return, + }; + let parent_node = hir.get_parent_node(hir_id); + let name = match hir.find(parent_node) { + Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Local(local), .. + })) => match get_name(err, &local.pat.kind) { + Some(name) => name, + None => return, + }, + // Different to previous arm because one is `&hir::Local` and the other + // is `P`. + Some(hir::Node::Local(local)) => match get_name(err, &local.pat.kind) { + Some(name) => name, + None => return, + }, _ => return, }; - if points_at_arg { - // When the obligation error has been ensured to have been caused by - // an argument, the `obligation.cause.span` points at the expression - // of the argument, so we can provide a suggestion. This is signaled - // by `points_at_arg`. Otherwise, we give a more general note. - err.span_suggestion( - obligation.cause.span, - &msg, - snippet, - Applicability::HasPlaceholders, - ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); - } + let args = decl.inputs.iter() + .map(|_| "_") + .collect::>().join(", "); + format!("{}({})", name, args) + } + Some(hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Fn(.., body_id), + .. + })) => { + err.span_label(ident.span, "consider calling this function"); + let body = hir.body(*body_id); + let args = body.params.iter() + .map(|arg| match &arg.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => ident.to_string(), + _ => "_".to_string(), + }).collect::>().join(", "); + format!("{}({})", ident, args) } - _ => {} + _ => return, + }; + if points_at_arg { + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. This is signaled + // by `points_at_arg`. Otherwise, we give a more general note. + err.span_suggestion( + obligation.cause.span, + &msg, + snippet, + Applicability::HasPlaceholders, + ); + } else { + err.help(&format!("{}: `{}`", msg, snippet)); } } @@ -1377,12 +1389,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let ty::Ref(_, t_type, _) = trait_type.kind { trait_type = t_type; - let substs = self.tcx.mk_substs_trait(trait_type, &[]); - let new_trait_ref = ty::TraitRef::new(trait_ref.def_id, substs); - let new_obligation = Obligation::new( + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.def_id, + trait_type, ObligationCause::dummy(), obligation.param_env, - new_trait_ref.to_predicate(), ); if self.predicate_may_hold(&new_obligation) { @@ -1440,12 +1451,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { hir::Mutability::Immutable => self.tcx.mk_mut_ref(region, t_type), }; - let substs = self.tcx.mk_substs_trait(&trait_type, &[]); - let new_trait_ref = ty::TraitRef::new(trait_ref.skip_binder().def_id, substs); - let new_obligation = Obligation::new( + let new_obligation = self.mk_obligation_for_def_id( + trait_ref.skip_binder().def_id, + trait_type, ObligationCause::dummy(), obligation.param_env, - new_trait_ref.to_predicate(), ); if self.evaluate_obligation_no_overflow( From 614da98454984921142eb2059db7be953d2c855c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 17 Nov 2019 11:27:48 -0800 Subject: [PATCH 3/3] review comments --- src/librustc/traits/error_reporting.rs | 78 +++++++++++++++----------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index b1652f58772d0..18f083e154a86 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -1244,6 +1244,42 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Obligation::new(cause, param_env, new_trait_ref.to_predicate()) } + /// Given a closure's `DefId`, return the given name of the closure. + /// + /// This doesn't account for reassignments, but it's only used for suggestions. + fn get_closure_name( + &self, + def_id: DefId, + err: &mut DiagnosticBuilder<'_>, + msg: &str, + ) -> Option { + let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option { + // Get the local name of this closure. This can be inaccurate because + // of the possibility of reassignment, but this should be good enough. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { + Some(format!("{}", name)) + } + _ => { + err.note(&msg); + None + } + } + }; + + let hir = self.tcx.hir(); + let hir_id = hir.as_local_hir_id(def_id)?; + let parent_node = hir.get_parent_node(hir_id); + match hir.find(parent_node) { + Some(hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Local(local), .. + })) => get_name(err, &local.pat.kind), + // Different to previous arm because one is `&hir::Local` and the other + // is `P`. + Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), + _ => return None, + } + } /// We tried to apply the bound to an `fn` or closure. Check whether calling it would /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling @@ -1274,19 +1310,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { obligation.param_env, ); - let get_name = |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind| -> Option { - // Get the local name of this closure. This can be inaccurate because - // of the possibility of reassignment, but this should be good enough. - match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => { - Some(format!("{}", name)) - } - _ => { - err.note(&msg); - None - } - } - }; match self.evaluate_obligation(&obligation) { Ok(EvaluationResult::EvaluatedToOk) | Ok(EvaluationResult::EvaluatedToOkModuloRegions) | @@ -1301,29 +1324,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .. })) => { err.span_label(*span, "consider calling this closure"); - let hir_id = match hir.as_local_hir_id(def_id) { - Some(hir_id) => hir_id, + let name = match self.get_closure_name(def_id, err, &msg) { + Some(name) => name, None => return, }; - let parent_node = hir.get_parent_node(hir_id); - let name = match hir.find(parent_node) { - Some(hir::Node::Stmt(hir::Stmt { - kind: hir::StmtKind::Local(local), .. - })) => match get_name(err, &local.pat.kind) { - Some(name) => name, - None => return, - }, - // Different to previous arm because one is `&hir::Local` and the other - // is `P`. - Some(hir::Node::Local(local)) => match get_name(err, &local.pat.kind) { - Some(name) => name, - None => return, - }, - _ => return, - }; let args = decl.inputs.iter() .map(|_| "_") - .collect::>().join(", "); + .collect::>() + .join(", "); format!("{}({})", name, args) } Some(hir::Node::Item(hir::Item { @@ -1336,9 +1344,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let args = body.params.iter() .map(|arg| match &arg.pat.kind { hir::PatKind::Binding(_, _, ident, None) + // FIXME: provide a better suggestion when encountering `SelfLower`, it + // should suggest a method call. if ident.name != kw::SelfLower => ident.to_string(), _ => "_".to_string(), - }).collect::>().join(", "); + }) + .collect::>() + .join(", "); format!("{}({})", ident, args) } _ => return,