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) 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 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 209 private: 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 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. 275 template <class C> 276 class DeclFilterCCC : public CorrectionCandidateCallback { 277 public: 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: 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