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