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