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