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