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