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