From 59af2ac098ae6bb927799dfec03e9bb9934ccb8f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 22 May 2016 08:11:59 +0200 Subject: [PATCH] typeck: suggest (x.field)(...) to call struct fields even when x is a reference Fixes: #33784 --- src/librustc_typeck/check/method/suggest.rs | 48 ++++++++++++--------- src/test/compile-fail/issue-33784.rs | 46 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/test/compile-fail/issue-33784.rs diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 346449d0a5133..54521782474fe 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -162,26 +162,34 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }, rcvr_ty); - // If the item has the name of a field, give a help note - if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) { - if let Some(field) = def.struct_variant().find_field_named(item_name) { - let expr_string = match tcx.sess.codemap().span_to_snippet(expr.span) { - 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); - - if self.is_fn_ty(&field_ty, span) { - err.span_note(span, - &format!("use `({0}.{1})(...)` if you meant to call \ - the function stored in the `{1}` field", - expr_string, item_name)); - } else { - err.span_note(span, &format!("did you mean to write `{0}.{1}`?", - expr_string, item_name)); + // 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 Some(expr) = rcvr_expr { + for (ty, _) in self.autoderef(span, rcvr_ty) { + if let ty::TyStruct(def, substs) = ty.sty { + if let Some(field) = def.struct_variant().find_field_named(item_name) { + let snippet = tcx.sess.codemap().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); + + if self.is_fn_ty(&field_ty, span) { + err.span_note(span, &format!( + "use `({0}.{1})(...)` if you meant to call the function \ + stored in the `{1}` field", + expr_string, item_name)); + } else { + err.span_note(span, &format!( + "did you mean to write `{0}.{1}`?", + expr_string, item_name)); + } + break; + } } } } diff --git a/src/test/compile-fail/issue-33784.rs b/src/test/compile-fail/issue-33784.rs new file mode 100644 index 0000000000000..4229be29473db --- /dev/null +++ b/src/test/compile-fail/issue-33784.rs @@ -0,0 +1,46 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ops::Deref; + +struct Obj where F: FnMut() -> u32 { + fn_ptr: fn() -> (), + closure: F, +} + +struct C { + c_fn_ptr: fn() -> (), +} + +struct D(C); + +impl Deref for D { + type Target = C; + fn deref(&self) -> &C { + &self.0 + } +} + + +fn empty() {} + +fn main() { + let o = Obj { fn_ptr: empty, closure: || 42 }; + let p = &o; + p.closure(); //~ ERROR no method named `closure` found + //~^ NOTE use `(p.closure)(...)` if you meant to call the function stored in the `closure` field + let q = &p; + q.fn_ptr(); //~ ERROR no method named `fn_ptr` found + //~^ NOTE use `(q.fn_ptr)(...)` if you meant to call the function stored in the `fn_ptr` field + let r = D(C { c_fn_ptr: empty }); + let s = &r; + s.c_fn_ptr(); //~ ERROR no method named `c_fn_ptr` found + //~^ NOTE use `(s.c_fn_ptr)(...)` if you meant to call the function stored in the `c_fn_ptr` +}