diff options
author | David Malcolm <dmalcolm@redhat.com> | 2015-11-20 01:26:00 +0000 |
---|---|---|
committer | David Malcolm <dmalcolm@gcc.gnu.org> | 2015-11-20 01:26:00 +0000 |
commit | 8ece8dfbd9cf38e10e0266ed22ead5bb389d8125 (patch) | |
tree | 3572616c35cc69549b3ff9761e6d1af8fb70780b /gcc/cp | |
parent | 32c912aad1f29233f15da6baf33c765e3b6213fd (diff) | |
download | gcc-8ece8dfbd9cf38e10e0266ed22ead5bb389d8125.zip gcc-8ece8dfbd9cf38e10e0266ed22ead5bb389d8125.tar.gz gcc-8ece8dfbd9cf38e10e0266ed22ead5bb389d8125.tar.bz2 |
C++ FE: offer suggestions for misspelled field names
gcc/c/ChangeLog:
* c-typeck.c (lookup_field_fuzzy): Move determination of closest
candidate into a new function, find_closest_identifier.
gcc/cp/ChangeLog:
* cp-tree.h (lookup_member_fuzzy): New decl.
* search.c: Include spellcheck.h.
(class lookup_field_fuzzy_info): New class.
(lookup_field_fuzzy_info::fuzzy_lookup_fnfields): New.
(lookup_field_fuzzy_info::fuzzy_lookup_field): New.
(lookup_field_fuzzy_r): New.
(lookup_member_fuzzy): New.
* typeck.c (finish_class_member_access_expr): When issuing
a "has no member named" error, call lookup_member_fuzzy, and
offer any result as a suggestion.
gcc/ChangeLog:
* spellcheck-tree.c (find_closest_identifier): New function, taken
from c/c-typeck.c:lookup_field_fuzzy, with NULL corrected to
NULL_TREE in two places.
* spellcheck.h (find_closest_identifier): New decl.
gcc/testsuite/ChangeLog:
* g++.dg/spellcheck-fields.C: New file.
From-SVN: r230638
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 13 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 1 | ||||
-rw-r--r-- | gcc/cp/search.c | 139 | ||||
-rw-r--r-- | gcc/cp/typeck.c | 15 |
4 files changed, 165 insertions, 3 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 92b1d28..cf436d7 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2015-11-19 David Malcolm <dmalcolm@redhat.com> + + * cp-tree.h (lookup_member_fuzzy): New decl. + * search.c: Include spellcheck.h. + (class lookup_field_fuzzy_info): New class. + (lookup_field_fuzzy_info::fuzzy_lookup_fnfields): New. + (lookup_field_fuzzy_info::fuzzy_lookup_field): New. + (lookup_field_fuzzy_r): New. + (lookup_member_fuzzy): New. + * typeck.c (finish_class_member_access_expr): When issuing + a "has no member named" error, call lookup_member_fuzzy, and + offer any result as a suggestion. + 2015-11-19 Torvald Riegel <triegel@redhat.com> * except.c (do_free_exception): Use transactional wrapper. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 14ea119..38bd7dd 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6152,6 +6152,7 @@ extern int class_method_index_for_fn (tree, tree); extern tree lookup_fnfields (tree, tree, int); extern tree lookup_member (tree, tree, int, bool, tsubst_flags_t); +extern tree lookup_member_fuzzy (tree, tree, bool); extern int look_for_overrides (tree, tree); extern void get_pure_virtuals (tree); extern void maybe_suppress_debug_info (tree); diff --git a/gcc/cp/search.c b/gcc/cp/search.c index 94502f6..0c11a83 100644 --- a/gcc/cp/search.c +++ b/gcc/cp/search.c @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see #include "cp-tree.h" #include "intl.h" #include "toplev.h" +#include "spellcheck.h" static int is_subobject_of_p (tree, tree); static tree dfs_lookup_base (tree, void *); @@ -1352,6 +1353,144 @@ lookup_member (tree xbasetype, tree name, int protect, bool want_type, return rval; } +/* Helper class for lookup_member_fuzzy. */ + +class lookup_field_fuzzy_info +{ + public: + lookup_field_fuzzy_info (bool want_type_p) : + m_want_type_p (want_type_p), m_candidates () {} + + void fuzzy_lookup_fnfields (tree type); + void fuzzy_lookup_field (tree type); + + /* If true, we are looking for types, not data members. */ + bool m_want_type_p; + /* The result: a vec of identifiers. */ + auto_vec<tree> m_candidates; +}; + +/* Locate all methods within TYPE, append them to m_candidates. */ + +void +lookup_field_fuzzy_info::fuzzy_lookup_fnfields (tree type) +{ + vec<tree, va_gc> *method_vec; + tree fn; + size_t i; + + if (!CLASS_TYPE_P (type)) + return; + + method_vec = CLASSTYPE_METHOD_VEC (type); + if (!method_vec) + return; + + for (i = 0; vec_safe_iterate (method_vec, i, &fn); ++i) + if (fn) + m_candidates.safe_push (DECL_NAME (OVL_CURRENT (fn))); +} + +/* Locate all fields within TYPE, append them to m_candidates. */ + +void +lookup_field_fuzzy_info::fuzzy_lookup_field (tree type) +{ + if (TREE_CODE (type) == TEMPLATE_TYPE_PARM + || TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM + || TREE_CODE (type) == TYPENAME_TYPE) + /* The TYPE_FIELDS of a TEMPLATE_TYPE_PARM and + BOUND_TEMPLATE_TEMPLATE_PARM are not fields at all; + instead TYPE_FIELDS is the TEMPLATE_PARM_INDEX. + The TYPE_FIELDS of TYPENAME_TYPE is its TYPENAME_TYPE_FULLNAME. */ + return; + + for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) + { + if (!m_want_type_p || DECL_DECLARES_TYPE_P (field)) + if (DECL_NAME (field)) + m_candidates.safe_push (DECL_NAME (field)); + } +} + + +/* Helper function for lookup_member_fuzzy, called via dfs_walk_all + DATA is really a lookup_field_fuzzy_info. Look for a field with + the name indicated there in BINFO. Gathers pertinent identifiers into + m_candidates. */ + +static tree +lookup_field_fuzzy_r (tree binfo, void *data) +{ + lookup_field_fuzzy_info *lffi = (lookup_field_fuzzy_info *) data; + tree type = BINFO_TYPE (binfo); + + /* First, look for functions. */ + if (!lffi->m_want_type_p) + lffi->fuzzy_lookup_fnfields (type); + + /* Look for data member and types. */ + lffi->fuzzy_lookup_field (type); + + return NULL_TREE; +} + +/* Like lookup_member, but try to find the closest match for NAME, + rather than an exact match, and return an identifier (or NULL_TREE). + Do not complain. */ + +tree +lookup_member_fuzzy (tree xbasetype, tree name, bool want_type_p) +{ + tree type = NULL_TREE, basetype_path = NULL_TREE; + struct lookup_field_fuzzy_info lffi (want_type_p); + + /* rval_binfo is the binfo associated with the found member, note, + this can be set with useful information, even when rval is not + set, because it must deal with ALL members, not just non-function + members. It is used for ambiguity checking and the hidden + checks. Whereas rval is only set if a proper (not hidden) + non-function member is found. */ + + if (name == error_mark_node + || xbasetype == NULL_TREE + || xbasetype == error_mark_node) + return NULL_TREE; + + gcc_assert (identifier_p (name)); + + if (TREE_CODE (xbasetype) == TREE_BINFO) + { + type = BINFO_TYPE (xbasetype); + basetype_path = xbasetype; + } + else + { + if (!RECORD_OR_UNION_CODE_P (TREE_CODE (xbasetype))) + return NULL_TREE; + type = xbasetype; + xbasetype = NULL_TREE; + } + + type = complete_type (type); + + /* Make sure we're looking for a member of the current instantiation in the + right partial specialization. */ + if (flag_concepts && dependent_type_p (type)) + type = currently_open_class (type); + + if (!basetype_path) + basetype_path = TYPE_BINFO (type); + + if (!basetype_path) + return NULL_TREE; + + /* Populate lffi.m_candidates. */ + dfs_walk_all (basetype_path, &lookup_field_fuzzy_r, NULL, &lffi); + + return find_closest_identifier (name, &lffi.m_candidates); +} + /* Like lookup_member, except that if we find a function member we return NULL_TREE. */ diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 5f7d4bb..9517890 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -2792,9 +2792,18 @@ finish_class_member_access_expr (tree object, tree name, bool template_p, if (member == NULL_TREE) { if (complain & tf_error) - error ("%q#T has no member named %qE", - TREE_CODE (access_path) == TREE_BINFO - ? TREE_TYPE (access_path) : object_type, name); + { + tree guessed_id = lookup_member_fuzzy (access_path, name, + /*want_type=*/false); + if (guessed_id) + error ("%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); + } return error_mark_node; } if (member == error_mark_node) |