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:
parent
32c912aad1
commit
8ece8dfbd9
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
139
gcc/cp/search.c
139
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. */
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user