• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
22 namespace clang {
23 
24 /// @brief Simple class containing the result of Sema::CorrectTypo
25 class TypoCorrection {
26 public:
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)
CorrectionName(Name)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)
CorrectionName(Name)62       : CorrectionName(Name), CorrectionNameSpec(NNS),
63         CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
64         ForceSpecifierReplacement(false) {}
65 
TypoCorrection()66   TypoCorrection()
67       : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
68         CallbackDistance(0), ForceSpecifierReplacement(false) {}
69 
70   /// \brief Gets the DeclarationName of the typo correction
getCorrection()71   DeclarationName getCorrection() const { return CorrectionName; }
getCorrectionAsIdentifierInfo()72   IdentifierInfo* getCorrectionAsIdentifierInfo() const {
73     return CorrectionName.getAsIdentifierInfo();
74   }
75 
76   /// \brief Gets the NestedNameSpecifier needed to use the typo correction
getCorrectionSpecifier()77   NestedNameSpecifier* getCorrectionSpecifier() const {
78     return CorrectionNameSpec;
79   }
setCorrectionSpecifier(NestedNameSpecifier * NNS)80   void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
81     CorrectionNameSpec = NNS;
82     ForceSpecifierReplacement = (NNS != 0);
83   }
84 
WillReplaceSpecifier(bool ForceReplacement)85   void WillReplaceSpecifier(bool ForceReplacement) {
86     ForceSpecifierReplacement = ForceReplacement;
87   }
88 
WillReplaceSpecifier()89   bool WillReplaceSpecifier() const {
90     return ForceSpecifierReplacement;
91   }
92 
setQualifierDistance(unsigned ED)93   void setQualifierDistance(unsigned ED) {
94     QualifierDistance = ED;
95   }
96 
setCallbackDistance(unsigned ED)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).
NormalizeEditDistance(unsigned ED)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
getCorrectionDecl()130   NamedDecl *getCorrectionDecl() const {
131     return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
132   }
133   template <class DeclClass>
getCorrectionDeclAs()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.
setCorrectionDecl(NamedDecl * CDecl)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;
getQuoted(const LangOptions & LO)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.
makeKeyword()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.
isKeyword()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>
isKeyword(const char (& Str)[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.
isResolved()180   bool isResolved() const { return !CorrectionDecls.empty(); }
181 
isOverloaded()182   bool isOverloaded() const {
183     return CorrectionDecls.size() > 1;
184   }
185 
setCorrectionRange(CXXScopeSpec * SS,const DeclarationNameInfo & TypoName)186   void setCorrectionRange(CXXScopeSpec* SS,
187                           const DeclarationNameInfo &TypoName) {
188     CorrectionRange.setBegin(ForceSpecifierReplacement && SS
189                                  ? SS->getBeginLoc()
190                                  : TypoName.getLoc());
191     CorrectionRange.setEnd(TypoName.getLoc());
192   }
193 
getCorrectionRange()194   SourceRange getCorrectionRange() const {
195     return CorrectionRange;
196   }
197 
198   typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
begin()199   decl_iterator begin() {
200     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
201   }
end()202   decl_iterator end() { return CorrectionDecls.end(); }
203   typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
begin()204   const_decl_iterator begin() const {
205     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
206   }
end()207   const_decl_iterator end() const { return CorrectionDecls.end(); }
208 
209 private:
hasCorrectionDecl()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.
227 class CorrectionCandidateCallback {
228 public:
229   static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
230 
CorrectionCandidateCallback()231   CorrectionCandidateCallback()
232       : WantTypeSpecifiers(true), WantExpressionKeywords(true),
233         WantCXXNamedCasts(true), WantRemainingKeywords(true),
234         WantObjCSuper(false),
235         IsObjCIvarLookup(false) {}
236 
~CorrectionCandidateCallback()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.
RankCandidate(const TypoCorrection & candidate)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.
275 template <class C>
276 class DeclFilterCCC : public CorrectionCandidateCallback {
277 public:
ValidateCandidate(const TypoCorrection & candidate)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.
286 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
287 public:
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
299 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
300 public:
NoTypoCorrectionCCC()301   NoTypoCorrectionCCC() {
302     WantTypeSpecifiers = false;
303     WantExpressionKeywords = false;
304     WantCXXNamedCasts = false;
305     WantRemainingKeywords = false;
306   }
307 
ValidateCandidate(const TypoCorrection & candidate)308   virtual bool ValidateCandidate(const TypoCorrection &candidate) {
309     return false;
310   }
311 };
312 
313 }
314 
315 #endif
316