1 1.1 mrg /* Find near-matches for strings and identifiers. 2 1.7 mrg Copyright (C) 2015-2022 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.7 mrg /* spellcheck.cc */ 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