Commit 8ece8dfb by David Malcolm Committed by David Malcolm

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
parent 32c912aa
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
......
......@@ -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
......
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.
......
......@@ -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);
......
......@@ -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. */
......
......@@ -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)
......
......@@ -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;
}
......@@ -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 */
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>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment