TypoCorrection.h revision 2d67097ad41f4c2fe82ebce3f587e06498f1bd71
1//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines the TypoCorrection class, which stores the results of
11// Sema's typo correction (Sema::CorrectTypo).
12//
13//===----------------------------------------------------------------------===//
14
15#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
16#define LLVM_CLANG_SEMA_TYPOCORRECTION_H
17
18#include "clang/AST/DeclCXX.h"
19#include "clang/Sema/DeclSpec.h"
20#include "llvm/ADT/SmallVector.h"
21
22namespace clang {
23
24/// @brief Simple class containing the result of Sema::CorrectTypo
25class TypoCorrection {
26public:
27  // "Distance" for unusable corrections
28  static const unsigned InvalidDistance = ~0U;
29  // The largest distance still considered valid (larger edit distances are
30  // mapped to InvalidDistance by getEditDistance).
31  static const unsigned MaximumDistance = 10000U;
32
33  // Relative weightings of the "edit distance" components. The higher the
34  // weight, the more of a penalty to fitness the component will give (higher
35  // weights mean greater contribution to the total edit distance, with the
36  // best correction candidates having the lowest edit distance).
37  static const unsigned CharDistanceWeight = 100U;
38  static const unsigned QualifierDistanceWeight = 110U;
39  static const unsigned CallbackDistanceWeight = 150U;
40
41  TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
42                 NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0,
43                 unsigned QualifierDistance = 0)
44      : CorrectionName(Name), CorrectionNameSpec(NNS),
45        CharDistance(CharDistance), QualifierDistance(QualifierDistance),
46        CallbackDistance(0), ForceSpecifierReplacement(false) {
47    if (NameDecl)
48      CorrectionDecls.push_back(NameDecl);
49  }
50
51  TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
52                 unsigned CharDistance = 0)
53      : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
54        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
55        ForceSpecifierReplacement(false) {
56    if (Name)
57      CorrectionDecls.push_back(Name);
58  }
59
60  TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
61                 unsigned CharDistance = 0)
62      : CorrectionName(Name), CorrectionNameSpec(NNS),
63        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
64        ForceSpecifierReplacement(false) {}
65
66  TypoCorrection()
67      : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
68        CallbackDistance(0), ForceSpecifierReplacement(false) {}
69
70  /// \brief Gets the DeclarationName of the typo correction
71  DeclarationName getCorrection() const { return CorrectionName; }
72  IdentifierInfo* getCorrectionAsIdentifierInfo() const {
73    return CorrectionName.getAsIdentifierInfo();
74  }
75
76  /// \brief Gets the NestedNameSpecifier needed to use the typo correction
77  NestedNameSpecifier* getCorrectionSpecifier() const {
78    return CorrectionNameSpec;
79  }
80  void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
81    CorrectionNameSpec = NNS;
82    ForceSpecifierReplacement = (NNS != 0);
83  }
84
85  void WillReplaceSpecifier(bool ForceReplacement) {
86    ForceSpecifierReplacement = ForceReplacement;
87  }
88
89  bool WillReplaceSpecifier() const {
90    return ForceSpecifierReplacement;
91  }
92
93  void setQualifierDistance(unsigned ED) {
94    QualifierDistance = ED;
95  }
96
97  void setCallbackDistance(unsigned ED) {
98    CallbackDistance = ED;
99  }
100
101  // Convert the given weighted edit distance to a roughly equivalent number of
102  // single-character edits (typically for comparison to the length of the
103  // string being edited).
104  static unsigned NormalizeEditDistance(unsigned ED) {
105    if (ED > MaximumDistance)
106      return InvalidDistance;
107    return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
108  }
109
110  /// \brief Gets the "edit distance" of the typo correction from the typo.
111  /// If Normalized is true, scale the distance down by the CharDistanceWeight
112  /// to return the edit distance in terms of single-character edits.
113  unsigned getEditDistance(bool Normalized = true) const {
114    if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
115        CallbackDistance > MaximumDistance)
116      return InvalidDistance;
117    unsigned ED =
118        CharDistance * CharDistanceWeight +
119        QualifierDistance * QualifierDistanceWeight +
120        CallbackDistance * CallbackDistanceWeight;
121    if (ED > MaximumDistance)
122      return InvalidDistance;
123    // Half the CharDistanceWeight is added to ED to simulate rounding since
124    // integer division truncates the value (i.e. round-to-nearest-int instead
125    // of round-to-zero).
126    return Normalized ? NormalizeEditDistance(ED) : ED;
127  }
128
129  /// \brief Gets the pointer to the declaration of the typo correction
130  NamedDecl *getCorrectionDecl() const {
131    return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
132  }
133  template <class DeclClass>
134  DeclClass *getCorrectionDeclAs() const {
135    return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
136  }
137
138  /// \brief Clears the list of NamedDecls before adding the new one.
139  void setCorrectionDecl(NamedDecl *CDecl) {
140    CorrectionDecls.clear();
141    addCorrectionDecl(CDecl);
142  }
143
144  /// \brief Add the given NamedDecl to the list of NamedDecls that are the
145  /// declarations associated with the DeclarationName of this TypoCorrection
146  void addCorrectionDecl(NamedDecl *CDecl);
147
148  std::string getAsString(const LangOptions &LO) const;
149  std::string getQuoted(const LangOptions &LO) const {
150    return "'" + getAsString(LO) + "'";
151  }
152
153  /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
154  LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
155
156  /// \brief Mark this TypoCorrection as being a keyword.
157  /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
158  /// added to the list of the correction's NamedDecl pointers, NULL is added
159  /// as the only element in the list to mark this TypoCorrection as a keyword.
160  void makeKeyword() {
161    CorrectionDecls.clear();
162    CorrectionDecls.push_back(0);
163    ForceSpecifierReplacement = true;
164  }
165
166  // Check if this TypoCorrection is a keyword by checking if the first
167  // item in CorrectionDecls is NULL.
168  bool isKeyword() const {
169    return !CorrectionDecls.empty() &&
170        CorrectionDecls.front() == 0;
171  }
172
173  // Check if this TypoCorrection is the given keyword.
174  template<std::size_t StrLen>
175  bool isKeyword(const char (&Str)[StrLen]) const {
176    return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
177  }
178
179  // Returns true if the correction either is a keyword or has a known decl.
180  bool isResolved() const { return !CorrectionDecls.empty(); }
181
182  bool isOverloaded() const {
183    return CorrectionDecls.size() > 1;
184  }
185
186  void setCorrectionRange(CXXScopeSpec *SS,
187                          const DeclarationNameInfo &TypoName) {
188    CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
189                                 ? SS->getBeginLoc()
190                                 : TypoName.getLoc());
191    CorrectionRange.setEnd(TypoName.getLoc());
192  }
193
194  SourceRange getCorrectionRange() const {
195    return CorrectionRange;
196  }
197
198  typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
199  decl_iterator begin() {
200    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
201  }
202  decl_iterator end() { return CorrectionDecls.end(); }
203  typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
204  const_decl_iterator begin() const {
205    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
206  }
207  const_decl_iterator end() const { return CorrectionDecls.end(); }
208
209private:
210  bool hasCorrectionDecl() const {
211    return (!isKeyword() && !CorrectionDecls.empty());
212  }
213
214  // Results.
215  DeclarationName CorrectionName;
216  NestedNameSpecifier *CorrectionNameSpec;
217  SmallVector<NamedDecl *, 1> CorrectionDecls;
218  unsigned CharDistance;
219  unsigned QualifierDistance;
220  unsigned CallbackDistance;
221  SourceRange CorrectionRange;
222  bool ForceSpecifierReplacement;
223};
224
225/// @brief Base class for callback objects used by Sema::CorrectTypo to check
226/// the validity of a potential typo correction.
227class CorrectionCandidateCallback {
228public:
229  static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
230
231  CorrectionCandidateCallback()
232      : WantTypeSpecifiers(true), WantExpressionKeywords(true),
233        WantCXXNamedCasts(true), WantRemainingKeywords(true),
234        WantObjCSuper(false),
235        IsObjCIvarLookup(false) {}
236
237  virtual ~CorrectionCandidateCallback() {}
238
239  /// \brief Simple predicate used by the default RankCandidate to
240  /// determine whether to return an edit distance of 0 or InvalidDistance.
241  /// This can be overrided by validators that only need to determine if a
242  /// candidate is viable, without ranking potentially viable candidates.
243  /// Only ValidateCandidate or RankCandidate need to be overriden by a
244  /// callback wishing to check the viability of correction candidates.
245  /// The default predicate always returns true if the candidate is not a type
246  /// name or keyword, true for types if WantTypeSpecifiers is true, and true
247  /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
248  /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
249  virtual bool ValidateCandidate(const TypoCorrection &candidate);
250
251  /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
252  /// to a candidate (where a lower value represents a better candidate), or
253  /// returning InvalidDistance if the candidate is not at all viable. For
254  /// validation callbacks that only need to determine if a candidate is viable,
255  /// the default RankCandidate returns either 0 or InvalidDistance depending
256  /// whether ValidateCandidate returns true or false.
257  virtual unsigned RankCandidate(const TypoCorrection &candidate) {
258    return ValidateCandidate(candidate) ? 0 : InvalidDistance;
259  }
260
261  // Flags for context-dependent keywords.
262  // TODO: Expand these to apply to non-keywords or possibly remove them.
263  bool WantTypeSpecifiers;
264  bool WantExpressionKeywords;
265  bool WantCXXNamedCasts;
266  bool WantRemainingKeywords;
267  bool WantObjCSuper;
268  // Temporary hack for the one case where a CorrectTypoContext enum is used
269  // when looking up results.
270  bool IsObjCIvarLookup;
271};
272
273/// @brief Simple template class for restricting typo correction candidates
274/// to ones having a single Decl* of the given type.
275template <class C>
276class DeclFilterCCC : public CorrectionCandidateCallback {
277public:
278  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
279    return candidate.getCorrectionDeclAs<C>();
280  }
281};
282
283// @brief Callback class to limit the allowed keywords and to only accept typo
284// corrections that are keywords or whose decls refer to functions (or template
285// functions) that accept the given number of arguments.
286class FunctionCallFilterCCC : public CorrectionCandidateCallback {
287public:
288  FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
289                        bool HasExplicitTemplateArgs);
290
291  virtual bool ValidateCandidate(const TypoCorrection &candidate);
292
293 private:
294  unsigned NumArgs;
295  bool HasExplicitTemplateArgs;
296};
297
298// @brief Callback class that effectively disabled typo correction
299class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
300public:
301  NoTypoCorrectionCCC() {
302    WantTypeSpecifiers = false;
303    WantExpressionKeywords = false;
304    WantCXXNamedCasts = false;
305    WantRemainingKeywords = false;
306  }
307
308  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
309    return false;
310  }
311};
312
313}
314
315#endif
316