spellcheck.h revision 1.1.1.3 1 1.1 mrg /* Find near-matches for strings and identifiers.
2 1.1.1.3 mrg Copyright (C) 2015-2018 Free Software Foundation, Inc.
3 1.1 mrg
4 1.1 mrg This file is part of GCC.
5 1.1 mrg
6 1.1 mrg GCC is free software; you can redistribute it and/or modify it under
7 1.1 mrg the terms of the GNU General Public License as published by the Free
8 1.1 mrg Software Foundation; either version 3, or (at your option) any later
9 1.1 mrg version.
10 1.1 mrg
11 1.1 mrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 1.1 mrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 1.1 mrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 1.1 mrg for more details.
15 1.1 mrg
16 1.1 mrg You should have received a copy of the GNU General Public License
17 1.1 mrg along with GCC; see the file COPYING3. If not see
18 1.1 mrg <http://www.gnu.org/licenses/>. */
19 1.1 mrg
20 1.1 mrg #ifndef GCC_SPELLCHECK_H
21 1.1 mrg #define GCC_SPELLCHECK_H
22 1.1 mrg
23 1.1 mrg typedef unsigned int edit_distance_t;
24 1.1 mrg const edit_distance_t MAX_EDIT_DISTANCE = UINT_MAX;
25 1.1 mrg
26 1.1 mrg /* spellcheck.c */
27 1.1 mrg extern edit_distance_t
28 1.1 mrg levenshtein_distance (const char *s, int len_s,
29 1.1 mrg const char *t, int len_t);
30 1.1 mrg
31 1.1 mrg extern edit_distance_t
32 1.1 mrg levenshtein_distance (const char *s, const char *t);
33 1.1 mrg
34 1.1 mrg extern const char *
35 1.1 mrg find_closest_string (const char *target,
36 1.1 mrg const auto_vec<const char *> *candidates);
37 1.1 mrg
38 1.1.1.2 mrg /* A traits class for describing a string-like type usable by
39 1.1.1.2 mrg class best_match.
40 1.1.1.2 mrg Specializations should provide the implementations of the following:
41 1.1.1.2 mrg
42 1.1.1.2 mrg static size_t get_length (TYPE);
43 1.1.1.2 mrg static const char *get_string (TYPE);
44 1.1.1.2 mrg
45 1.1.1.2 mrg get_string should return a non-NULL ptr, which does not need to be
46 1.1.1.2 mrg 0-terminated. */
47 1.1.1.2 mrg
48 1.1.1.2 mrg template <typename TYPE>
49 1.1.1.2 mrg struct edit_distance_traits {};
50 1.1.1.2 mrg
51 1.1.1.2 mrg /* Specialization of edit_distance_traits for C-style strings. */
52 1.1.1.2 mrg
53 1.1.1.2 mrg template <>
54 1.1.1.2 mrg struct edit_distance_traits<const char *>
55 1.1.1.2 mrg {
56 1.1.1.2 mrg static size_t get_length (const char *str)
57 1.1.1.2 mrg {
58 1.1.1.2 mrg gcc_assert (str);
59 1.1.1.2 mrg return strlen (str);
60 1.1.1.2 mrg }
61 1.1.1.2 mrg
62 1.1.1.2 mrg static const char *get_string (const char *str)
63 1.1.1.2 mrg {
64 1.1.1.2 mrg gcc_assert (str);
65 1.1.1.2 mrg return str;
66 1.1.1.2 mrg }
67 1.1.1.2 mrg };
68 1.1.1.2 mrg
69 1.1.1.2 mrg /* A type for use when determining the best match against a string,
70 1.1.1.2 mrg expressed as a template so that we can match against various
71 1.1.1.2 mrg string-like types (const char *, frontend identifiers, and preprocessor
72 1.1.1.2 mrg macros).
73 1.1.1.2 mrg
74 1.1.1.2 mrg This type accumulates the best possible match against GOAL_TYPE for
75 1.1.1.2 mrg a sequence of elements of CANDIDATE_TYPE, whilst minimizing the
76 1.1.1.2 mrg number of calls to levenshtein_distance and to
77 1.1.1.2 mrg edit_distance_traits<T>::get_length. */
78 1.1.1.2 mrg
79 1.1.1.2 mrg template <typename GOAL_TYPE, typename CANDIDATE_TYPE>
80 1.1.1.2 mrg class best_match
81 1.1.1.2 mrg {
82 1.1.1.2 mrg public:
83 1.1.1.2 mrg typedef GOAL_TYPE goal_t;
84 1.1.1.2 mrg typedef CANDIDATE_TYPE candidate_t;
85 1.1.1.2 mrg typedef edit_distance_traits<goal_t> goal_traits;
86 1.1.1.2 mrg typedef edit_distance_traits<candidate_t> candidate_traits;
87 1.1.1.2 mrg
88 1.1.1.2 mrg /* Constructor. */
89 1.1.1.2 mrg
90 1.1.1.2 mrg best_match (GOAL_TYPE goal,
91 1.1.1.2 mrg edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
92 1.1.1.2 mrg : m_goal (goal_traits::get_string (goal)),
93 1.1.1.2 mrg m_goal_len (goal_traits::get_length (goal)),
94 1.1.1.2 mrg m_best_candidate (NULL),
95 1.1.1.2 mrg m_best_distance (best_distance_so_far)
96 1.1.1.2 mrg {}
97 1.1.1.2 mrg
98 1.1.1.2 mrg /* Compare the edit distance between CANDIDATE and m_goal,
99 1.1.1.2 mrg and if it's the best so far, record it. */
100 1.1.1.2 mrg
101 1.1.1.2 mrg void consider (candidate_t candidate)
102 1.1.1.2 mrg {
103 1.1.1.2 mrg size_t candidate_len = candidate_traits::get_length (candidate);
104 1.1.1.2 mrg
105 1.1.1.2 mrg /* Calculate a lower bound on the candidate's distance to the goal,
106 1.1.1.2 mrg based on the difference in lengths; it will require at least
107 1.1.1.2 mrg this many insertions/deletions. */
108 1.1.1.2 mrg edit_distance_t min_candidate_distance
109 1.1.1.2 mrg = abs ((ssize_t)candidate_len - (ssize_t)m_goal_len);
110 1.1.1.2 mrg
111 1.1.1.2 mrg /* If the candidate's length is sufficiently different to that
112 1.1.1.2 mrg of the goal string, then the number of insertions/deletions
113 1.1.1.2 mrg may be >= the best distance so far. If so, we can reject
114 1.1.1.2 mrg the candidate immediately without needing to compute
115 1.1.1.2 mrg the exact distance, since it won't be an improvement. */
116 1.1.1.2 mrg if (min_candidate_distance >= m_best_distance)
117 1.1.1.2 mrg return;
118 1.1.1.2 mrg
119 1.1.1.2 mrg /* If the candidate will be unable to beat the criterion in
120 1.1.1.2 mrg get_best_meaningful_candidate, reject it without computing
121 1.1.1.2 mrg the exact distance. */
122 1.1.1.2 mrg unsigned int cutoff = MAX (m_goal_len, candidate_len) / 2;
123 1.1.1.2 mrg if (min_candidate_distance > cutoff)
124 1.1.1.2 mrg return;
125 1.1.1.2 mrg
126 1.1.1.2 mrg /* Otherwise, compute the distance and see if the candidate
127 1.1.1.2 mrg has beaten the previous best value. */
128 1.1.1.2 mrg edit_distance_t dist
129 1.1.1.2 mrg = levenshtein_distance (m_goal, m_goal_len,
130 1.1.1.2 mrg candidate_traits::get_string (candidate),
131 1.1.1.2 mrg candidate_len);
132 1.1.1.2 mrg if (dist < m_best_distance)
133 1.1.1.2 mrg {
134 1.1.1.2 mrg m_best_distance = dist;
135 1.1.1.2 mrg m_best_candidate = candidate;
136 1.1.1.2 mrg m_best_candidate_len = candidate_len;
137 1.1.1.2 mrg }
138 1.1.1.2 mrg }
139 1.1.1.2 mrg
140 1.1.1.2 mrg /* Assuming that BEST_CANDIDATE is known to be better than
141 1.1.1.2 mrg m_best_candidate, update (without recomputing the edit distance to
142 1.1.1.2 mrg the goal). */
143 1.1.1.2 mrg
144 1.1.1.2 mrg void set_best_so_far (CANDIDATE_TYPE best_candidate,
145 1.1.1.2 mrg edit_distance_t best_distance,
146 1.1.1.2 mrg size_t best_candidate_len)
147 1.1.1.2 mrg {
148 1.1.1.2 mrg gcc_assert (best_distance < m_best_distance);
149 1.1.1.2 mrg m_best_candidate = best_candidate;
150 1.1.1.2 mrg m_best_distance = best_distance;
151 1.1.1.2 mrg m_best_candidate_len = best_candidate_len;
152 1.1.1.2 mrg }
153 1.1.1.2 mrg
154 1.1.1.2 mrg /* Get the best candidate so far, but applying a filter to ensure
155 1.1.1.2 mrg that we return NULL if none of the candidates are close to the goal,
156 1.1.1.2 mrg to avoid offering nonsensical suggestions to the user. */
157 1.1.1.2 mrg
158 1.1.1.2 mrg candidate_t get_best_meaningful_candidate () const
159 1.1.1.2 mrg {
160 1.1.1.2 mrg /* If more than half of the letters were misspelled, the suggestion is
161 1.1.1.2 mrg likely to be meaningless. */
162 1.1.1.2 mrg if (m_best_candidate)
163 1.1.1.2 mrg {
164 1.1.1.2 mrg unsigned int cutoff = MAX (m_goal_len, m_best_candidate_len) / 2;
165 1.1.1.2 mrg if (m_best_distance > cutoff)
166 1.1.1.2 mrg return NULL;
167 1.1.1.2 mrg }
168 1.1.1.2 mrg
169 1.1.1.2 mrg /* If the goal string somehow makes it into the candidate list, offering
170 1.1.1.2 mrg it as a suggestion will be nonsensical e.g.
171 1.1.1.2 mrg 'constexpr' does not name a type; did you mean 'constexpr'?
172 1.1.1.2 mrg Ultimately such suggestions are due to bugs in constructing the
173 1.1.1.2 mrg candidate list, but as a band-aid, do not offer suggestions for
174 1.1.1.2 mrg distance == 0 (where candidate == goal). */
175 1.1.1.2 mrg if (m_best_distance == 0)
176 1.1.1.2 mrg return NULL;
177 1.1.1.2 mrg
178 1.1.1.2 mrg return m_best_candidate;
179 1.1.1.2 mrg }
180 1.1.1.2 mrg
181 1.1.1.3 mrg /* Get the closest candidate so far, without applying any filtering. */
182 1.1.1.3 mrg
183 1.1.1.3 mrg candidate_t blithely_get_best_candidate () const
184 1.1.1.3 mrg {
185 1.1.1.3 mrg return m_best_candidate;
186 1.1.1.3 mrg }
187 1.1.1.3 mrg
188 1.1.1.2 mrg edit_distance_t get_best_distance () const { return m_best_distance; }
189 1.1.1.2 mrg size_t get_best_candidate_length () const { return m_best_candidate_len; }
190 1.1.1.2 mrg
191 1.1.1.2 mrg private:
192 1.1.1.2 mrg const char *m_goal;
193 1.1.1.2 mrg size_t m_goal_len;
194 1.1.1.2 mrg candidate_t m_best_candidate;
195 1.1.1.2 mrg edit_distance_t m_best_distance;
196 1.1.1.2 mrg size_t m_best_candidate_len;
197 1.1.1.2 mrg };
198 1.1 mrg
199 1.1 mrg #endif /* GCC_SPELLCHECK_H */
200