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
This commit is contained in:
David Malcolm 2015-11-20 01:26:00 +00:00 committed by David Malcolm
parent 32c912aad1
commit 8ece8dfbd9
9 changed files with 222 additions and 30 deletions

View File

@ -1,3 +1,8 @@
2015-11-19 David Malcolm <dmalcolm@redhat.com>
* c-typeck.c (lookup_field_fuzzy): Move determination of closest
candidate into a new function, find_closest_identifier.
2015-11-19 Marek Polacek <polacek@redhat.com>
PR c/68412

View File

@ -2274,33 +2274,7 @@ lookup_field_fuzzy (tree type, tree component)
lookup_field_fuzzy_find_candidates (type, component,
&candidates);
/* Now determine which is closest. */
int i;
tree identifier;
tree best_identifier = NULL;
edit_distance_t best_distance = MAX_EDIT_DISTANCE;
FOR_EACH_VEC_ELT (candidates, i, identifier)
{
gcc_assert (TREE_CODE (identifier) == IDENTIFIER_NODE);
edit_distance_t dist = levenshtein_distance (component, identifier);
if (dist < best_distance)
{
best_distance = dist;
best_identifier = identifier;
}
}
/* If more than half of the letters were misspelled, the suggestion is
likely to be meaningless. */
if (best_identifier)
{
unsigned int cutoff = MAX (IDENTIFIER_LENGTH (component),
IDENTIFIER_LENGTH (best_identifier)) / 2;
if (best_distance > cutoff)
return NULL;
}
return best_identifier;
return find_closest_identifier (component, &candidates);
}
/* Make an expression to refer to the COMPONENT field of structure or

View File

@ -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.

View File

@ -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);

View File

@ -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. */

View File

@ -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)

View File

@ -37,3 +37,44 @@ levenshtein_distance (tree ident_s, tree ident_t)
IDENTIFIER_POINTER (ident_t),
IDENTIFIER_LENGTH (ident_t));
}
/* Given TARGET, an identifier, and CANDIDATES, a vec of identifiers,
determine which element within CANDIDATES has the lowest edit
distance to TARGET. If there are multiple elements with the
same minimal distance, the first in the vector wins.
If more than half of the letters were misspelled, the suggestion is
likely to be meaningless, so return NULL_TREE for this case. */
tree
find_closest_identifier (tree target, const auto_vec<tree> *candidates)
{
gcc_assert (TREE_CODE (target) == IDENTIFIER_NODE);
int i;
tree identifier;
tree best_identifier = NULL_TREE;
edit_distance_t best_distance = MAX_EDIT_DISTANCE;
FOR_EACH_VEC_ELT (*candidates, i, identifier)
{
gcc_assert (TREE_CODE (identifier) == IDENTIFIER_NODE);
edit_distance_t dist = levenshtein_distance (target, identifier);
if (dist < best_distance)
{
best_distance = dist;
best_identifier = identifier;
}
}
/* If more than half of the letters were misspelled, the suggestion is
likely to be meaningless. */
if (best_identifier)
{
unsigned int cutoff = MAX (IDENTIFIER_LENGTH (target),
IDENTIFIER_LENGTH (best_identifier)) / 2;
if (best_distance > cutoff)
return NULL_TREE;
}
return best_identifier;
}

View File

@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
typedef unsigned int edit_distance_t;
const edit_distance_t MAX_EDIT_DISTANCE = UINT_MAX;
/* spellcheck.c */
extern edit_distance_t
levenshtein_distance (const char *s, int len_s,
const char *t, int len_t);
@ -30,7 +31,12 @@ levenshtein_distance (const char *s, int len_s,
extern edit_distance_t
levenshtein_distance (const char *s, const char *t);
/* spellcheck-tree.c */
extern edit_distance_t
levenshtein_distance (tree ident_s, tree ident_t);
extern tree
find_closest_identifier (tree target, const auto_vec<tree> *candidates);
#endif /* GCC_SPELLCHECK_H */

View File

@ -1,3 +1,7 @@
2015-11-19 David Malcolm <dmalcolm@redhat.com>
* g++.dg/spellcheck-fields.C: New file.
2015-11-19 Aditya Kumar <aditya.k7@samsung.com>
Sebastian Pop <s.pop@samsung.com>