spellcheck.h revision 1.6 1 1.1 mrg /* Find near-matches for strings and identifiers.
2 1.6 mrg Copyright (C) 2015-2020 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.5 mrg get_edit_distance (const char *s, int len_s,
29 1.5 mrg const char *t, int len_t);
30 1.1 mrg
31 1.1 mrg extern edit_distance_t
32 1.5 mrg get_edit_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.3 mrg /* A traits class for describing a string-like type usable by
39 1.3 mrg class best_match.
40 1.3 mrg Specializations should provide the implementations of the following:
41 1.3 mrg
42 1.3 mrg static size_t get_length (TYPE);
43 1.3 mrg static const char *get_string (TYPE);
44 1.3 mrg
45 1.3 mrg get_string should return a non-NULL ptr, which does not need to be
46 1.3 mrg 0-terminated. */
47 1.3 mrg
48 1.3 mrg template <typename TYPE>
49 1.3 mrg struct edit_distance_traits {};
50 1.3 mrg
51 1.3 mrg /* Specialization of edit_distance_traits for C-style strings. */
52 1.3 mrg
53 1.3 mrg template <>
54 1.3 mrg struct edit_distance_traits<const char *>
55 1.3 mrg {
56 1.3 mrg static size_t get_length (const char *str)
57 1.3 mrg {
58 1.3 mrg gcc_assert (str);
59 1.3 mrg return strlen (str);
60 1.3 mrg }
61 1.3 mrg
62 1.3 mrg static const char *get_string (const char *str)
63 1.3 mrg {
64 1.3 mrg gcc_assert (str);
65 1.3 mrg return str;
66 1.3 mrg }
67 1.3 mrg };
68 1.3 mrg
69 1.5 mrg extern edit_distance_t get_edit_distance_cutoff (size_t goal_len,
70 1.5 mrg size_t candidate_len);
71 1.5 mrg
72 1.3 mrg /* A type for use when determining the best match against a string,
73 1.3 mrg expressed as a template so that we can match against various
74 1.3 mrg string-like types (const char *, frontend identifiers, and preprocessor
75 1.3 mrg macros).
76 1.3 mrg
77 1.3 mrg This type accumulates the best possible match against GOAL_TYPE for
78 1.3 mrg a sequence of elements of CANDIDATE_TYPE, whilst minimizing the
79 1.5 mrg number of calls to get_edit_distance and to
80 1.3 mrg edit_distance_traits<T>::get_length. */
81 1.3 mrg
82 1.3 mrg template <typename GOAL_TYPE, typename CANDIDATE_TYPE>
83 1.3 mrg class best_match
84 1.3 mrg {
85 1.3 mrg public:
86 1.3 mrg typedef GOAL_TYPE goal_t;
87 1.3 mrg typedef CANDIDATE_TYPE candidate_t;
88 1.3 mrg typedef edit_distance_traits<goal_t> goal_traits;
89 1.3 mrg typedef edit_distance_traits<candidate_t> candidate_traits;
90 1.3 mrg
91 1.3 mrg /* Constructor. */
92 1.3 mrg
93 1.3 mrg best_match (GOAL_TYPE goal,
94 1.3 mrg edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
95 1.3 mrg : m_goal (goal_traits::get_string (goal)),
96 1.3 mrg m_goal_len (goal_traits::get_length (goal)),
97 1.3 mrg m_best_candidate (NULL),
98 1.3 mrg m_best_distance (best_distance_so_far)
99 1.3 mrg {}
100 1.3 mrg
101 1.3 mrg /* Compare the edit distance between CANDIDATE and m_goal,
102 1.3 mrg and if it's the best so far, record it. */
103 1.3 mrg
104 1.3 mrg void consider (candidate_t candidate)
105 1.3 mrg {
106 1.3 mrg size_t candidate_len = candidate_traits::get_length (candidate);
107 1.3 mrg
108 1.3 mrg /* Calculate a lower bound on the candidate's distance to the goal,
109 1.3 mrg based on the difference in lengths; it will require at least
110 1.3 mrg this many insertions/deletions. */
111 1.3 mrg edit_distance_t min_candidate_distance
112 1.3 mrg = abs ((ssize_t)candidate_len - (ssize_t)m_goal_len);
113 1.3 mrg
114 1.3 mrg /* If the candidate's length is sufficiently different to that
115 1.3 mrg of the goal string, then the number of insertions/deletions
116 1.3 mrg may be >= the best distance so far. If so, we can reject
117 1.3 mrg the candidate immediately without needing to compute
118 1.3 mrg the exact distance, since it won't be an improvement. */
119 1.3 mrg if (min_candidate_distance >= m_best_distance)
120 1.3 mrg return;
121 1.3 mrg
122 1.3 mrg /* If the candidate will be unable to beat the criterion in
123 1.3 mrg get_best_meaningful_candidate, reject it without computing
124 1.3 mrg the exact distance. */
125 1.5 mrg edit_distance_t cutoff = get_cutoff (candidate_len);
126 1.3 mrg if (min_candidate_distance > cutoff)
127 1.3 mrg return;
128 1.3 mrg
129 1.3 mrg /* Otherwise, compute the distance and see if the candidate
130 1.3 mrg has beaten the previous best value. */
131 1.3 mrg edit_distance_t dist
132 1.5 mrg = get_edit_distance (m_goal, m_goal_len,
133 1.5 mrg candidate_traits::get_string (candidate),
134 1.5 mrg candidate_len);
135 1.3 mrg if (dist < m_best_distance)
136 1.3 mrg {
137 1.3 mrg m_best_distance = dist;
138 1.3 mrg m_best_candidate = candidate;
139 1.3 mrg m_best_candidate_len = candidate_len;
140 1.3 mrg }
141 1.3 mrg }
142 1.3 mrg
143 1.3 mrg /* Assuming that BEST_CANDIDATE is known to be better than
144 1.3 mrg m_best_candidate, update (without recomputing the edit distance to
145 1.3 mrg the goal). */
146 1.3 mrg
147 1.3 mrg void set_best_so_far (CANDIDATE_TYPE best_candidate,
148 1.3 mrg edit_distance_t best_distance,
149 1.3 mrg size_t best_candidate_len)
150 1.3 mrg {
151 1.3 mrg gcc_assert (best_distance < m_best_distance);
152 1.3 mrg m_best_candidate = best_candidate;
153 1.3 mrg m_best_distance = best_distance;
154 1.3 mrg m_best_candidate_len = best_candidate_len;
155 1.3 mrg }
156 1.3 mrg
157 1.5 mrg /* Generate the maximum edit distance for which we consider a suggestion
158 1.5 mrg to be meaningful, given a candidate of length CANDIDATE_LEN. */
159 1.5 mrg
160 1.5 mrg edit_distance_t get_cutoff (size_t candidate_len) const
161 1.5 mrg {
162 1.5 mrg return ::get_edit_distance_cutoff (m_goal_len, candidate_len);
163 1.5 mrg }
164 1.5 mrg
165 1.3 mrg /* Get the best candidate so far, but applying a filter to ensure
166 1.3 mrg that we return NULL if none of the candidates are close to the goal,
167 1.3 mrg to avoid offering nonsensical suggestions to the user. */
168 1.3 mrg
169 1.3 mrg candidate_t get_best_meaningful_candidate () const
170 1.3 mrg {
171 1.5 mrg /* If the edit distance is too high, the suggestion is likely to be
172 1.5 mrg meaningless. */
173 1.3 mrg if (m_best_candidate)
174 1.3 mrg {
175 1.5 mrg edit_distance_t cutoff = get_cutoff (m_best_candidate_len);
176 1.3 mrg if (m_best_distance > cutoff)
177 1.3 mrg return NULL;
178 1.3 mrg }
179 1.3 mrg
180 1.3 mrg /* If the goal string somehow makes it into the candidate list, offering
181 1.3 mrg it as a suggestion will be nonsensical e.g.
182 1.3 mrg 'constexpr' does not name a type; did you mean 'constexpr'?
183 1.3 mrg Ultimately such suggestions are due to bugs in constructing the
184 1.3 mrg candidate list, but as a band-aid, do not offer suggestions for
185 1.3 mrg distance == 0 (where candidate == goal). */
186 1.3 mrg if (m_best_distance == 0)
187 1.3 mrg return NULL;
188 1.3 mrg
189 1.3 mrg return m_best_candidate;
190 1.3 mrg }
191 1.3 mrg
192 1.4 mrg /* Get the closest candidate so far, without applying any filtering. */
193 1.4 mrg
194 1.4 mrg candidate_t blithely_get_best_candidate () const
195 1.4 mrg {
196 1.4 mrg return m_best_candidate;
197 1.4 mrg }
198 1.4 mrg
199 1.3 mrg edit_distance_t get_best_distance () const { return m_best_distance; }
200 1.3 mrg size_t get_best_candidate_length () const { return m_best_candidate_len; }
201 1.3 mrg
202 1.3 mrg private:
203 1.3 mrg const char *m_goal;
204 1.3 mrg size_t m_goal_len;
205 1.3 mrg candidate_t m_best_candidate;
206 1.3 mrg edit_distance_t m_best_distance;
207 1.3 mrg size_t m_best_candidate_len;
208 1.3 mrg };
209 1.1 mrg
210 1.1 mrg #endif /* GCC_SPELLCHECK_H */
211