aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2018-10-11 19:03:33 +0000
committerDavid Malcolm <dmalcolm@gcc.gnu.org>2018-10-11 19:03:33 +0000
commit03f6d32edb50546f1a123e848ae98a70a747b0c7 (patch)
treeeebbad5740eb6f7d20c0276e17e09a489b15efde /gcc/cp
parentc7f45560c7856139118f71dd31d1bc2f3eb7b98c (diff)
downloadgcc-03f6d32edb50546f1a123e848ae98a70a747b0c7.zip
gcc-03f6d32edb50546f1a123e848ae98a70a747b0c7.tar.gz
gcc-03f6d32edb50546f1a123e848ae98a70a747b0c7.tar.bz2
C++: suggestions for misspelled private members (PR c++/84993)
PR c++/84993 identifies a problem with our suggestions for misspelled member names in the C++ FE for the case where the member is private. For example, given: class foo { public: double get_ratio() const { return m_ratio; } private: double m_ratio; }; void test(foo *ptr) { if (ptr->ratio >= 0.5) ;// etc } ...we currently emit this suggestion: <source>: In function 'void test(foo*)': <source>:12:12: error: 'class foo' has no member named 'ratio'; did you mean 'm_ratio'? if (ptr->ratio >= 0.5) ^~~~~ m_ratio ...but if the user follows this suggestion, they get: <source>: In function 'void test(foo*)': <source>:12:12: error: 'double foo::m_ratio' is private within this context if (ptr->m_ratio >= 0.5) ^~~~~~~ <source>:7:10: note: declared private here double m_ratio; ^~~~~~~ <source>:12:12: note: field 'double foo::m_ratio' can be accessed via 'double foo::get_ratio() const' if (ptr->m_ratio >= 0.5) ^~~~~~~ get_ratio() It feels wrong to be emitting a fix-it hint that doesn't compile, so this patch adds the accessor fix-it hint logic to this case, so that we directly offer a valid suggestion: <source>: In function 'void test(foo*)': <source>:12:12: error: 'class foo' has no member named 'ratio'; did you mean 'double foo::m_ratio'? (accessible via 'double foo::get_ratio() const') if (ptr->ratio >= 0.5) ^~~~~ get_ratio() gcc/cp/ChangeLog: PR c++/84993 * call.c (enforce_access): Move diagnostics to... (complain_about_access): ...this new function. * cp-tree.h (class access_failure_info): Rename split out field "m_field_decl" into "m_decl" and "m_diag_decl". (access_failure_info::record_access_failure): Add tree param. (access_failure_info::was_inaccessible_p): New accessor. (access_failure_info::get_decl): New accessor. (access_failure_info::get_diag_decl): New accessor. (access_failure_info::get_any_accessor): New member function. (access_failure_info::add_fixit_hint): New static member function. (complain_about_access): New decl. * typeck.c (access_failure_info::record_access_failure): Update for change to fields. (access_failure_info::maybe_suggest_accessor): Split out into... (access_failure_info::get_any_accessor): ...this new function... (access_failure_info::add_fixit_hint): ...and this new function. (finish_class_member_access_expr): Split out "has no member named" error-handling into... (complain_about_unrecognized_member): ...this new function, and check that the guessed name is accessible along the access path. Only provide a spell-correction fix-it hint if it is; otherwise, attempt to issue an accessor fix-it hint. gcc/testsuite/ChangeLog: PR c++/84993 * g++.dg/torture/accessor-fixits-9.C: New test. From-SVN: r265056
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/ChangeLog26
-rw-r--r--gcc/cp/call.c64
-rw-r--r--gcc/cp/cp-tree.h17
-rw-r--r--gcc/cp/typeck.c150
4 files changed, 189 insertions, 68 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index da443d9..a3d29d7 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,29 @@
+2018-10-11 David Malcolm <dmalcolm@redhat.com>
+
+ PR c++/84993
+ * call.c (enforce_access): Move diagnostics to...
+ (complain_about_access): ...this new function.
+ * cp-tree.h (class access_failure_info): Rename split out field
+ "m_field_decl" into "m_decl" and "m_diag_decl".
+ (access_failure_info::record_access_failure): Add tree param.
+ (access_failure_info::was_inaccessible_p): New accessor.
+ (access_failure_info::get_decl): New accessor.
+ (access_failure_info::get_diag_decl): New accessor.
+ (access_failure_info::get_any_accessor): New member function.
+ (access_failure_info::add_fixit_hint): New static member function.
+ (complain_about_access): New decl.
+ * typeck.c (access_failure_info::record_access_failure): Update
+ for change to fields.
+ (access_failure_info::maybe_suggest_accessor): Split out into...
+ (access_failure_info::get_any_accessor): ...this new function...
+ (access_failure_info::add_fixit_hint): ...and this new function.
+ (finish_class_member_access_expr): Split out "has no member named"
+ error-handling into...
+ (complain_about_unrecognized_member): ...this new function, and
+ check that the guessed name is accessible along the access path.
+ Only provide a spell-correction fix-it hint if it is; otherwise,
+ attempt to issue an accessor fix-it hint.
+
2018-10-11 Nathan Sidwell <nathan@acm.org>
* parser.c (cp_parser_translation_unit): Return void. Don't fail
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 747f837..0baf26e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6514,6 +6514,38 @@ build_op_delete_call (enum tree_code code, tree addr, tree size,
return error_mark_node;
}
+/* Issue diagnostics about a disallowed access of DECL, using DIAG_DECL
+ in the diagnostics.
+
+ If ISSUE_ERROR is true, then issue an error about the
+ access, followed by a note showing the declaration.
+ Otherwise, just show the note. */
+
+void
+complain_about_access (tree decl, tree diag_decl, bool issue_error)
+{
+ if (TREE_PRIVATE (decl))
+ {
+ if (issue_error)
+ error ("%q#D is private within this context", diag_decl);
+ inform (DECL_SOURCE_LOCATION (diag_decl),
+ "declared private here");
+ }
+ else if (TREE_PROTECTED (decl))
+ {
+ if (issue_error)
+ error ("%q#D is protected within this context", diag_decl);
+ inform (DECL_SOURCE_LOCATION (diag_decl),
+ "declared protected here");
+ }
+ else
+ {
+ if (issue_error)
+ error ("%q#D is inaccessible within this context", diag_decl);
+ inform (DECL_SOURCE_LOCATION (diag_decl), "declared here");
+ }
+}
+
/* If the current scope isn't allowed to access DECL along
BASETYPE_PATH, give an error. The most derived class in
BASETYPE_PATH is the one used to qualify DECL. DIAG_DECL is
@@ -6538,34 +6570,12 @@ enforce_access (tree basetype_path, tree decl, tree diag_decl,
if (!accessible_p (basetype_path, decl, true))
{
+ if (flag_new_inheriting_ctors)
+ diag_decl = strip_inheriting_ctors (diag_decl);
if (complain & tf_error)
- {
- if (flag_new_inheriting_ctors)
- diag_decl = strip_inheriting_ctors (diag_decl);
- if (TREE_PRIVATE (decl))
- {
- error ("%q#D is private within this context", diag_decl);
- inform (DECL_SOURCE_LOCATION (diag_decl),
- "declared private here");
- if (afi)
- afi->record_access_failure (basetype_path, diag_decl);
- }
- else if (TREE_PROTECTED (decl))
- {
- error ("%q#D is protected within this context", diag_decl);
- inform (DECL_SOURCE_LOCATION (diag_decl),
- "declared protected here");
- if (afi)
- afi->record_access_failure (basetype_path, diag_decl);
- }
- else
- {
- error ("%q#D is inaccessible within this context", diag_decl);
- inform (DECL_SOURCE_LOCATION (diag_decl), "declared here");
- if (afi)
- afi->record_access_failure (basetype_path, diag_decl);
- }
- }
+ complain_about_access (decl, diag_decl, true);
+ if (afi)
+ afi->record_access_failure (basetype_path, decl, diag_decl);
return false;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..26ded3a 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6103,18 +6103,27 @@ extern void complain_about_bad_argument (location_t arg_loc,
class access_failure_info
{
public:
- access_failure_info () : m_was_inaccessible (false), m_basetype_path (NULL_TREE),
- m_field_decl (NULL_TREE) {}
+ access_failure_info () : m_was_inaccessible (false),
+ m_basetype_path (NULL_TREE),
+ m_decl (NULL_TREE), m_diag_decl (NULL_TREE) {}
- void record_access_failure (tree basetype_path, tree field_decl);
+ void record_access_failure (tree basetype_path, tree decl, tree diag_decl);
+
+ bool was_inaccessible_p () const { return m_was_inaccessible; }
+ tree get_decl () const { return m_decl; }
+ tree get_diag_decl () const { return m_diag_decl; }
+ tree get_any_accessor (bool const_p) const;
void maybe_suggest_accessor (bool const_p) const;
+ static void add_fixit_hint (rich_location *richloc, tree accessor);
private:
bool m_was_inaccessible;
tree m_basetype_path;
- tree m_field_decl;
+ tree m_decl;
+ tree m_diag_decl;
};
+extern void complain_about_access (tree, tree, bool);
extern bool enforce_access (tree, tree, tree,
tsubst_flags_t,
access_failure_info *afi = NULL);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 8f81959..c921096 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -2708,43 +2708,138 @@ check_template_keyword (tree decl)
}
/* Record that an access failure occurred on BASETYPE_PATH attempting
- to access FIELD_DECL. */
+ to access DECL, where DIAG_DECL should be used for diagnostics. */
void
access_failure_info::record_access_failure (tree basetype_path,
- tree field_decl)
+ tree decl, tree diag_decl)
{
m_was_inaccessible = true;
m_basetype_path = basetype_path;
- m_field_decl = field_decl;
+ m_decl = decl;
+ m_diag_decl = diag_decl;
}
/* If an access failure was recorded, then attempt to locate an
- accessor function for the pertinent field, and if one is
- available, add a note and fix-it hint suggesting using it. */
+ accessor function for the pertinent field.
+ Otherwise, return NULL_TREE. */
-void
-access_failure_info::maybe_suggest_accessor (bool const_p) const
+tree
+access_failure_info::get_any_accessor (bool const_p) const
{
- if (!m_was_inaccessible)
- return;
+ if (!was_inaccessible_p ())
+ return NULL_TREE;
tree accessor
- = locate_field_accessor (m_basetype_path, m_field_decl, const_p);
+ = locate_field_accessor (m_basetype_path, m_diag_decl, const_p);
if (!accessor)
- return;
+ return NULL_TREE;
/* The accessor must itself be accessible for it to be a reasonable
suggestion. */
if (!accessible_p (m_basetype_path, accessor, true))
- return;
+ return NULL_TREE;
- rich_location richloc (line_table, input_location);
+ return accessor;
+}
+
+/* Add a fix-it hint to RICHLOC suggesting the use of ACCESSOR_DECL, by
+ replacing the primary location in RICHLOC with "accessor()". */
+
+void
+access_failure_info::add_fixit_hint (rich_location *richloc,
+ tree accessor_decl)
+{
pretty_printer pp;
- pp_printf (&pp, "%s()", IDENTIFIER_POINTER (DECL_NAME (accessor)));
- richloc.add_fixit_replace (pp_formatted_text (&pp));
+ pp_printf (&pp, "%s()", IDENTIFIER_POINTER (DECL_NAME (accessor_decl)));
+ richloc->add_fixit_replace (pp_formatted_text (&pp));
+}
+
+/* If an access failure was recorded, then attempt to locate an
+ accessor function for the pertinent field, and if one is
+ available, add a note and fix-it hint suggesting using it. */
+
+void
+access_failure_info::maybe_suggest_accessor (bool const_p) const
+{
+ tree accessor = get_any_accessor (const_p);
+ if (accessor == NULL_TREE)
+ return;
+ rich_location richloc (line_table, input_location);
+ add_fixit_hint (&richloc, accessor);
inform (&richloc, "field %q#D can be accessed via %q#D",
- m_field_decl, accessor);
+ m_diag_decl, accessor);
+}
+
+/* Subroutine of finish_class_member_access_expr.
+ Issue an error about NAME not being a member of ACCESS_PATH (or
+ OBJECT_TYPE), potentially providing a fix-it hint for misspelled
+ names. */
+
+static void
+complain_about_unrecognized_member (tree access_path, tree name,
+ tree object_type)
+{
+ /* Attempt to provide a hint about misspelled names. */
+ tree guessed_id = lookup_member_fuzzy (access_path, name,
+ /*want_type=*/false);
+ if (guessed_id == NULL_TREE)
+ {
+ /* No hint. */
+ error ("%q#T has no member named %qE",
+ TREE_CODE (access_path) == TREE_BINFO
+ ? TREE_TYPE (access_path) : object_type, name);
+ return;
+ }
+
+ location_t bogus_component_loc = input_location;
+ gcc_rich_location rich_loc (bogus_component_loc);
+
+ /* Check that the guessed name is accessible along access_path. */
+ access_failure_info afi;
+ lookup_member (access_path, guessed_id, /*protect=*/1,
+ /*want_type=*/false, /*complain=*/false,
+ &afi);
+ if (afi.was_inaccessible_p ())
+ {
+ tree accessor = afi.get_any_accessor (TYPE_READONLY (object_type));
+ if (accessor)
+ {
+ /* The guessed name isn't directly accessible, but can be accessed
+ via an accessor member function. */
+ afi.add_fixit_hint (&rich_loc, accessor);
+ error_at (&rich_loc,
+ "%q#T has no member named %qE;"
+ " did you mean %q#D? (accessible via %q#D)",
+ TREE_CODE (access_path) == TREE_BINFO
+ ? TREE_TYPE (access_path) : object_type,
+ name, afi.get_diag_decl (), accessor);
+ }
+ else
+ {
+ /* The guessed name isn't directly accessible, and no accessor
+ member function could be found. */
+ error_at (&rich_loc,
+ "%q#T has no member named %qE;"
+ " did you mean %q#D? (not accessible from this context)",
+ TREE_CODE (access_path) == TREE_BINFO
+ ? TREE_TYPE (access_path) : object_type,
+ name, afi.get_diag_decl ());
+ complain_about_access (afi.get_decl (), afi.get_diag_decl (), false);
+ }
+ }
+ else
+ {
+ /* The guessed name is directly accessible; suggest it. */
+ rich_loc.add_fixit_misspelled_id (bogus_component_loc,
+ guessed_id);
+ error_at (&rich_loc,
+ "%q#T has no member named %qE;"
+ " did you mean %qE?",
+ TREE_CODE (access_path) == TREE_BINFO
+ ? TREE_TYPE (access_path) : object_type,
+ name, guessed_id);
+ }
}
/* This function is called by the parser to process a class member
@@ -2940,27 +3035,8 @@ finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
/* Try again at instantiation time. */
goto dependent;
if (complain & tf_error)
- {
- tree guessed_id = lookup_member_fuzzy (access_path, name,
- /*want_type=*/false);
- if (guessed_id)
- {
- location_t bogus_component_loc = input_location;
- gcc_rich_location rich_loc (bogus_component_loc);
- rich_loc.add_fixit_misspelled_id (bogus_component_loc,
- guessed_id);
- error_at (&rich_loc,
- "%q#T has no member named %qE;"
- " did you mean %qE?",
- TREE_CODE (access_path) == TREE_BINFO
- ? TREE_TYPE (access_path) : object_type,
- name, guessed_id);
- }
- else
- error ("%q#T has no member named %qE",
- TREE_CODE (access_path) == TREE_BINFO
- ? TREE_TYPE (access_path) : object_type, name);
- }
+ complain_about_unrecognized_member (access_path, name,
+ object_type);
return error_mark_node;
}
if (member == error_mark_node)