From a291d4e94cbcd9082f942147cde92b8ae5a39cdc Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Tue, 19 Mar 2019 15:54:21 -0400 Subject: [PATCH] convert field/method confusion help to suggestions --- src/librustc_typeck/check/method/suggest.rs | 83 +++++++++++-------- .../issue-18343.stderr | 4 +- .../issue-2392.stderr | 48 +++++++---- .../issue-32128.stderr | 4 +- .../issue-33784.stderr | 12 ++- 5 files changed, 98 insertions(+), 53 deletions(-) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 4bf6471a6293c..83a98c38c90ce 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -332,44 +332,61 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - for (ty, _) in self.autoderef(span, rcvr_ty) { - if let ty::Adt(def, substs) = ty.sty { - if !def.is_enum() { + let field_receiver = self + .autoderef(span, rcvr_ty) + .find_map(|(ty, _)| match ty.sty { + ty::Adt(def, substs) if !def.is_enum() => { let variant = &def.non_enum_variant(); - if let Some(index) = self.tcx.find_field_index(item_name, variant) { + self.tcx.find_field_index(item_name, variant).map(|index| { let field = &variant.fields[index]; - let snippet = tcx.sess.source_map().span_to_snippet(expr.span); - let expr_string = match snippet { - Ok(expr_string) => expr_string, - _ => "s".into(), // Default to a generic placeholder for the - // expression when we can't generate a - // string snippet. - }; - let field_ty = field.ty(tcx, substs); - let scope = self.tcx.hir().get_module_parent_by_hir_id( - self.body_id); - if field.vis.is_accessible_from(scope, self.tcx) { - if self.is_fn_ty(&field_ty, span) { - err.help(&format!("use `({0}.{1})(...)` if you \ - meant to call the function \ - stored in the `{1}` field", - expr_string, - item_name)); - } else { - err.help(&format!("did you mean to write `{0}.{1}` \ - instead of `{0}.{1}(...)`?", - expr_string, - item_name)); - } - err.span_label(span, "field, not a method"); - } else { - err.span_label(span, "private field, not a method"); - } - break; - } + (field, field_ty) + }) + } + _ => None, + }); + + if let Some((field, field_ty)) = field_receiver { + let scope = self.tcx.hir().get_module_parent_by_hir_id(self.body_id); + let is_accessible = field.vis.is_accessible_from(scope, self.tcx); + + if is_accessible { + if self.is_fn_ty(&field_ty, span) { + let expr_span = expr.span.to(item_name.span); + err.multipart_suggestion( + &format!( + "to call the function stored in `{}`, \ + surround the field access with parentheses", + item_name, + ), + vec![ + (expr_span.shrink_to_lo(), '('.to_string()), + (expr_span.shrink_to_hi(), ')'.to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + let call_expr = self.tcx.hir().expect_expr_by_hir_id( + self.tcx.hir().get_parent_node_by_hir_id(expr.hir_id), + ); + + let span = call_expr.span.trim_start(item_name.span).unwrap(); + + err.span_suggestion( + span, + "remove the arguments", + String::new(), + Applicability::MaybeIncorrect, + ); } } + + let field_kind = if is_accessible { + "field" + } else { + "private field" + }; + err.span_label(item_name.span, format!("{}, not a method", field_kind)); } } else { err.span_label(span, format!("{} not found in `{}`", item_kind, ty_str)); diff --git a/src/test/ui/confuse-field-and-method/issue-18343.stderr b/src/test/ui/confuse-field-and-method/issue-18343.stderr index 36112cd0e10c9..03f9d990dbb23 100644 --- a/src/test/ui/confuse-field-and-method/issue-18343.stderr +++ b/src/test/ui/confuse-field-and-method/issue-18343.stderr @@ -6,8 +6,10 @@ LL | struct Obj where F: FnMut() -> u32 { ... LL | o.closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(o.closure)(...)` if you meant to call the function stored in the `closure` field +LL | (o.closure)(); + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/confuse-field-and-method/issue-2392.stderr b/src/test/ui/confuse-field-and-method/issue-2392.stderr index 7cd1941d80e83..a3ad54da025a4 100644 --- a/src/test/ui/confuse-field-and-method/issue-2392.stderr +++ b/src/test/ui/confuse-field-and-method/issue-2392.stderr @@ -6,8 +6,10 @@ LL | struct Obj where F: FnOnce() -> u32 { ... LL | o_closure.closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(o_closure.closure)(...)` if you meant to call the function stored in the `closure` field +LL | (o_closure.closure)(); + | ^ ^ error[E0599]: no method named `not_closure` found for type `Obj<[closure@$DIR/issue-2392.rs:39:36: 39:41]>` in the current scope --> $DIR/issue-2392.rs:42:15 @@ -16,9 +18,9 @@ LL | struct Obj where F: FnOnce() -> u32 { | -------------------------------------- method `not_closure` not found for this ... LL | o_closure.not_closure(); - | ^^^^^^^^^^^ field, not a method - | - = help: did you mean to write `o_closure.not_closure` instead of `o_closure.not_closure(...)`? + | ^^^^^^^^^^^-- help: remove the arguments + | | + | field, not a method error[E0599]: no method named `closure` found for type `Obj u32 {func}>` in the current scope --> $DIR/issue-2392.rs:46:12 @@ -28,8 +30,10 @@ LL | struct Obj where F: FnOnce() -> u32 { ... LL | o_func.closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(o_func.closure)(...)` if you meant to call the function stored in the `closure` field +LL | (o_func.closure)(); + | ^ ^ error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope --> $DIR/issue-2392.rs:49:14 @@ -39,8 +43,10 @@ LL | struct BoxedObj { ... LL | boxed_fn.boxed_closure(); | ^^^^^^^^^^^^^ field, not a method +help: to call the function stored in `boxed_closure`, surround the field access with parentheses | - = help: use `(boxed_fn.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field +LL | (boxed_fn.boxed_closure)(); + | ^ ^ error[E0599]: no method named `boxed_closure` found for type `BoxedObj` in the current scope --> $DIR/issue-2392.rs:52:19 @@ -50,8 +56,10 @@ LL | struct BoxedObj { ... LL | boxed_closure.boxed_closure(); | ^^^^^^^^^^^^^ field, not a method +help: to call the function stored in `boxed_closure`, surround the field access with parentheses | - = help: use `(boxed_closure.boxed_closure)(...)` if you meant to call the function stored in the `boxed_closure` field +LL | (boxed_closure.boxed_closure)(); + | ^ ^ error[E0599]: no method named `closure` found for type `Obj u32 {func}>` in the current scope --> $DIR/issue-2392.rs:57:12 @@ -61,8 +69,10 @@ LL | struct Obj where F: FnOnce() -> u32 { ... LL | w.wrap.closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(w.wrap.closure)(...)` if you meant to call the function stored in the `closure` field +LL | (w.wrap.closure)(); + | ^ ^ error[E0599]: no method named `not_closure` found for type `Obj u32 {func}>` in the current scope --> $DIR/issue-2392.rs:59:12 @@ -71,9 +81,9 @@ LL | struct Obj where F: FnOnce() -> u32 { | -------------------------------------- method `not_closure` not found for this ... LL | w.wrap.not_closure(); - | ^^^^^^^^^^^ field, not a method - | - = help: did you mean to write `w.wrap.not_closure` instead of `w.wrap.not_closure(...)`? + | ^^^^^^^^^^^-- help: remove the arguments + | | + | field, not a method error[E0599]: no method named `closure` found for type `Obj + 'static)>>` in the current scope --> $DIR/issue-2392.rs:62:24 @@ -83,8 +93,10 @@ LL | struct Obj where F: FnOnce() -> u32 { ... LL | check_expression().closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(check_expression().closure)(...)` if you meant to call the function stored in the `closure` field +LL | (check_expression().closure)(); + | ^ ^ error[E0599]: no method named `f1` found for type `FuncContainer` in the current scope --> $DIR/issue-2392.rs:68:31 @@ -94,8 +106,10 @@ LL | struct FuncContainer { ... LL | (*self.container).f1(1); | ^^ field, not a method +help: to call the function stored in `f1`, surround the field access with parentheses | - = help: use `((*self.container).f1)(...)` if you meant to call the function stored in the `f1` field +LL | ((*self.container).f1)(1); + | ^ ^ error[E0599]: no method named `f2` found for type `FuncContainer` in the current scope --> $DIR/issue-2392.rs:69:31 @@ -105,8 +119,10 @@ LL | struct FuncContainer { ... LL | (*self.container).f2(1); | ^^ field, not a method +help: to call the function stored in `f2`, surround the field access with parentheses | - = help: use `((*self.container).f2)(...)` if you meant to call the function stored in the `f2` field +LL | ((*self.container).f2)(1); + | ^ ^ error[E0599]: no method named `f3` found for type `FuncContainer` in the current scope --> $DIR/issue-2392.rs:70:31 @@ -116,8 +132,10 @@ LL | struct FuncContainer { ... LL | (*self.container).f3(1); | ^^ field, not a method +help: to call the function stored in `f3`, surround the field access with parentheses | - = help: use `((*self.container).f3)(...)` if you meant to call the function stored in the `f3` field +LL | ((*self.container).f3)(1); + | ^ ^ error: aborting due to 11 previous errors diff --git a/src/test/ui/confuse-field-and-method/issue-32128.stderr b/src/test/ui/confuse-field-and-method/issue-32128.stderr index 902f60668f2ed..fbabb3a88cc6c 100644 --- a/src/test/ui/confuse-field-and-method/issue-32128.stderr +++ b/src/test/ui/confuse-field-and-method/issue-32128.stderr @@ -6,8 +6,10 @@ LL | struct Example { ... LL | demo.example(1); | ^^^^^^^ field, not a method +help: to call the function stored in `example`, surround the field access with parentheses | - = help: use `(demo.example)(...)` if you meant to call the function stored in the `example` field +LL | (demo.example)(1); + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/confuse-field-and-method/issue-33784.stderr b/src/test/ui/confuse-field-and-method/issue-33784.stderr index cce961f1e4edc..60f1a932f4442 100644 --- a/src/test/ui/confuse-field-and-method/issue-33784.stderr +++ b/src/test/ui/confuse-field-and-method/issue-33784.stderr @@ -3,24 +3,30 @@ error[E0599]: no method named `closure` found for type `&Obj<[closure@$DIR/issue | LL | p.closure(); | ^^^^^^^ field, not a method +help: to call the function stored in `closure`, surround the field access with parentheses | - = help: use `(p.closure)(...)` if you meant to call the function stored in the `closure` field +LL | (p.closure)(); + | ^ ^ error[E0599]: no method named `fn_ptr` found for type `&&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:48]>` in the current scope --> $DIR/issue-33784.rs:29:7 | LL | q.fn_ptr(); | ^^^^^^ field, not a method +help: to call the function stored in `fn_ptr`, surround the field access with parentheses | - = help: use `(q.fn_ptr)(...)` if you meant to call the function stored in the `fn_ptr` field +LL | (q.fn_ptr)(); + | ^ ^ error[E0599]: no method named `c_fn_ptr` found for type `&D` in the current scope --> $DIR/issue-33784.rs:32:7 | LL | s.c_fn_ptr(); | ^^^^^^^^ field, not a method +help: to call the function stored in `c_fn_ptr`, surround the field access with parentheses | - = help: use `(s.c_fn_ptr)(...)` if you meant to call the function stored in the `c_fn_ptr` field +LL | (s.c_fn_ptr)(); + | ^ ^ error: aborting due to 3 previous errors