TypoCorrection.h revision 6952c018318a8ce57e336d7ed2a4819a98182fa2
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 21namespace clang { 22 23/// @brief Simple class containing the result of Sema::CorrectTypo 24class TypoCorrection { 25public: 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) 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) 60 : CorrectionName(Name), CorrectionNameSpec(NNS), 61 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {} 62 63 TypoCorrection() 64 : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0), 65 CallbackDistance(0) {} 66 67 /// \brief Gets the DeclarationName of the typo correction 68 DeclarationName getCorrection() const { return CorrectionName; } 69 IdentifierInfo* getCorrectionAsIdentifierInfo() const { 70 return CorrectionName.getAsIdentifierInfo(); 71 } 72 73 /// \brief Gets the NestedNameSpecifier needed to use the typo correction 74 NestedNameSpecifier* getCorrectionSpecifier() const { 75 return CorrectionNameSpec; 76 } 77 void setCorrectionSpecifier(NestedNameSpecifier* NNS) { 78 CorrectionNameSpec = NNS; 79 } 80 81 void setQualifierDistance(unsigned ED) { 82 QualifierDistance = ED; 83 } 84 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). 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 118 NamedDecl* getCorrectionDecl() const { 119 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; 120 } 121 template <class DeclClass> 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. 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; 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. 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. 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> 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. 167 bool isResolved() const { return !CorrectionDecls.empty(); } 168 169 bool isOverloaded() const { 170 return CorrectionDecls.size() > 1; 171 } 172 173 void setCorrectionRange(CXXScopeSpec* SS, 174 const DeclarationNameInfo &TypoName) { 175 CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc() 176 : TypoName.getLoc()); 177 CorrectionRange.setEnd(TypoName.getLoc()); 178 } 179 180 SourceRange getCorrectionRange() const { 181 return CorrectionRange; 182 } 183 184 typedef llvm::SmallVector<NamedDecl*, 1>::iterator decl_iterator; 185 decl_iterator begin() { 186 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 187 } 188 decl_iterator end() { return CorrectionDecls.end(); } 189 typedef llvm::SmallVector<NamedDecl*, 1>::const_iterator const_decl_iterator; 190 const_decl_iterator begin() const { 191 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 192 } 193 const_decl_iterator end() const { return CorrectionDecls.end(); } 194 195private: 196 bool hasCorrectionDecl() const { 197 return (!isKeyword() && !CorrectionDecls.empty()); 198 } 199 200 // Results. 201 DeclarationName CorrectionName; 202 NestedNameSpecifier *CorrectionNameSpec; 203 llvm::SmallVector<NamedDecl*, 1> CorrectionDecls; 204 unsigned CharDistance; 205 unsigned QualifierDistance; 206 unsigned CallbackDistance; 207 SourceRange CorrectionRange; 208}; 209 210/// @brief Base class for callback objects used by Sema::CorrectTypo to check 211/// the validity of a potential typo correction. 212class CorrectionCandidateCallback { 213 public: 214 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; 215 216 CorrectionCandidateCallback() 217 : WantTypeSpecifiers(true), WantExpressionKeywords(true), 218 WantCXXNamedCasts(true), WantRemainingKeywords(true), 219 WantObjCSuper(false), 220 IsObjCIvarLookup(false) {} 221 222 virtual ~CorrectionCandidateCallback() {} 223 224 /// \brief Simple predicate used by the default RankCandidate to 225 /// determine whether to return an edit distance of 0 or InvalidDistance. 226 /// This can be overrided by validators that only need to determine if a 227 /// candidate is viable, without ranking potentially viable candidates. 228 /// Only ValidateCandidate or RankCandidate need to be overriden by a 229 /// callback wishing to check the viability of correction candidates. 230 virtual bool ValidateCandidate(const TypoCorrection &candidate) { 231 return true; 232 } 233 234 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank 235 /// to a candidate (where a lower value represents a better candidate), or 236 /// returning InvalidDistance if the candidate is not at all viable. For 237 /// validation callbacks that only need to determine if a candidate is viable, 238 /// the default RankCandidate returns either 0 or InvalidDistance depending 239 /// whether ValidateCandidate returns true or false. 240 virtual unsigned RankCandidate(const TypoCorrection &candidate) { 241 return ValidateCandidate(candidate) ? 0 : InvalidDistance; 242 } 243 244 // Flags for context-dependent keywords. 245 // TODO: Expand these to apply to non-keywords or possibly remove them. 246 bool WantTypeSpecifiers; 247 bool WantExpressionKeywords; 248 bool WantCXXNamedCasts; 249 bool WantRemainingKeywords; 250 bool WantObjCSuper; 251 // Temporary hack for the one case where a CorrectTypoContext enum is used 252 // when looking up results. 253 bool IsObjCIvarLookup; 254}; 255 256/// @brief Simple template class for restricting typo correction candidates 257/// to ones having a single Decl* of the given type. 258template <class C> 259class DeclFilterCCC : public CorrectionCandidateCallback { 260 public: 261 virtual bool ValidateCandidate(const TypoCorrection &candidate) { 262 return candidate.getCorrectionDeclAs<C>(); 263 } 264}; 265 266} 267 268#endif 269