1224135Sdim//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===// 2224135Sdim// 3224135Sdim// The LLVM Compiler Infrastructure 4224135Sdim// 5224135Sdim// This file is distributed under the University of Illinois Open Source 6224135Sdim// License. See LICENSE.TXT for details. 7224135Sdim// 8224135Sdim//===----------------------------------------------------------------------===// 9224135Sdim// 10224135Sdim// This file defines the TypoCorrection class, which stores the results of 11224135Sdim// Sema's typo correction (Sema::CorrectTypo). 12224135Sdim// 13224135Sdim//===----------------------------------------------------------------------===// 14224135Sdim 15224135Sdim#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H 16224135Sdim#define LLVM_CLANG_SEMA_TYPOCORRECTION_H 17224135Sdim 18224135Sdim#include "clang/AST/DeclCXX.h" 19249423Sdim#include "clang/Sema/DeclSpec.h" 20226633Sdim#include "llvm/ADT/SmallVector.h" 21224135Sdim 22224135Sdimnamespace clang { 23224135Sdim 24224135Sdim/// @brief Simple class containing the result of Sema::CorrectTypo 25224135Sdimclass TypoCorrection { 26224135Sdimpublic: 27234353Sdim // "Distance" for unusable corrections 28234353Sdim static const unsigned InvalidDistance = ~0U; 29234353Sdim // The largest distance still considered valid (larger edit distances are 30234353Sdim // mapped to InvalidDistance by getEditDistance). 31234353Sdim static const unsigned MaximumDistance = 10000U; 32234353Sdim 33234353Sdim // Relative weightings of the "edit distance" components. The higher the 34234353Sdim // weight, the more of a penalty to fitness the component will give (higher 35234353Sdim // weights mean greater contribution to the total edit distance, with the 36234353Sdim // best correction candidates having the lowest edit distance). 37234353Sdim static const unsigned CharDistanceWeight = 100U; 38234353Sdim static const unsigned QualifierDistanceWeight = 110U; 39234353Sdim static const unsigned CallbackDistanceWeight = 150U; 40234353Sdim 41224135Sdim TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, 42234353Sdim NestedNameSpecifier *NNS=0, unsigned CharDistance=0, 43234353Sdim unsigned QualifierDistance=0) 44234353Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 45234353Sdim CharDistance(CharDistance), QualifierDistance(QualifierDistance), 46234353Sdim CallbackDistance(0) { 47226633Sdim if (NameDecl) 48226633Sdim CorrectionDecls.push_back(NameDecl); 49226633Sdim } 50224135Sdim 51226633Sdim TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=0, 52234353Sdim unsigned CharDistance=0) 53234353Sdim : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), 54234353Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) { 55226633Sdim if (Name) 56226633Sdim CorrectionDecls.push_back(Name); 57226633Sdim } 58224135Sdim 59226633Sdim TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=0, 60234353Sdim unsigned CharDistance=0) 61234353Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 62234353Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {} 63224135Sdim 64224135Sdim TypoCorrection() 65234353Sdim : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0), 66234353Sdim CallbackDistance(0) {} 67224135Sdim 68224135Sdim /// \brief Gets the DeclarationName of the typo correction 69224135Sdim DeclarationName getCorrection() const { return CorrectionName; } 70224135Sdim IdentifierInfo* getCorrectionAsIdentifierInfo() const { 71224135Sdim return CorrectionName.getAsIdentifierInfo(); 72224135Sdim } 73224135Sdim 74224135Sdim /// \brief Gets the NestedNameSpecifier needed to use the typo correction 75224135Sdim NestedNameSpecifier* getCorrectionSpecifier() const { 76224135Sdim return CorrectionNameSpec; 77224135Sdim } 78224135Sdim void setCorrectionSpecifier(NestedNameSpecifier* NNS) { 79224135Sdim CorrectionNameSpec = NNS; 80224135Sdim } 81224135Sdim 82234353Sdim void setQualifierDistance(unsigned ED) { 83234353Sdim QualifierDistance = ED; 84234353Sdim } 85224135Sdim 86234353Sdim void setCallbackDistance(unsigned ED) { 87234353Sdim CallbackDistance = ED; 88234353Sdim } 89234353Sdim 90234353Sdim // Convert the given weighted edit distance to a roughly equivalent number of 91234353Sdim // single-character edits (typically for comparison to the length of the 92234353Sdim // string being edited). 93234353Sdim static unsigned NormalizeEditDistance(unsigned ED) { 94234353Sdim if (ED > MaximumDistance) 95234353Sdim return InvalidDistance; 96234353Sdim return (ED + CharDistanceWeight / 2) / CharDistanceWeight; 97234353Sdim } 98234353Sdim 99234353Sdim /// \brief Gets the "edit distance" of the typo correction from the typo. 100234353Sdim /// If Normalized is true, scale the distance down by the CharDistanceWeight 101234353Sdim /// to return the edit distance in terms of single-character edits. 102234353Sdim unsigned getEditDistance(bool Normalized = true) const { 103234353Sdim if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance || 104234353Sdim CallbackDistance > MaximumDistance) 105234353Sdim return InvalidDistance; 106234353Sdim unsigned ED = 107234353Sdim CharDistance * CharDistanceWeight + 108234353Sdim QualifierDistance * QualifierDistanceWeight + 109234353Sdim CallbackDistance * CallbackDistanceWeight; 110234353Sdim if (ED > MaximumDistance) 111234353Sdim return InvalidDistance; 112234353Sdim // Half the CharDistanceWeight is added to ED to simulate rounding since 113234353Sdim // integer division truncates the value (i.e. round-to-nearest-int instead 114234353Sdim // of round-to-zero). 115234353Sdim return Normalized ? NormalizeEditDistance(ED) : ED; 116234353Sdim } 117234353Sdim 118224135Sdim /// \brief Gets the pointer to the declaration of the typo correction 119224135Sdim NamedDecl* getCorrectionDecl() const { 120226633Sdim return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; 121224135Sdim } 122224135Sdim template <class DeclClass> 123224135Sdim DeclClass *getCorrectionDeclAs() const { 124224135Sdim return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); 125224135Sdim } 126224135Sdim 127226633Sdim /// \brief Clears the list of NamedDecls before adding the new one. 128224135Sdim void setCorrectionDecl(NamedDecl *CDecl) { 129226633Sdim CorrectionDecls.clear(); 130226633Sdim addCorrectionDecl(CDecl); 131224135Sdim } 132224135Sdim 133226633Sdim /// \brief Add the given NamedDecl to the list of NamedDecls that are the 134226633Sdim /// declarations associated with the DeclarationName of this TypoCorrection 135226633Sdim void addCorrectionDecl(NamedDecl *CDecl); 136226633Sdim 137224135Sdim std::string getAsString(const LangOptions &LO) const; 138224135Sdim std::string getQuoted(const LangOptions &LO) const { 139224135Sdim return "'" + getAsString(LO) + "'"; 140224135Sdim } 141224135Sdim 142226633Sdim /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName 143224135Sdim operator bool() const { return bool(CorrectionName); } 144224135Sdim 145226633Sdim /// \brief Mark this TypoCorrection as being a keyword. 146226633Sdim /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be 147226633Sdim /// added to the list of the correction's NamedDecl pointers, NULL is added 148226633Sdim /// as the only element in the list to mark this TypoCorrection as a keyword. 149226633Sdim void makeKeyword() { 150226633Sdim CorrectionDecls.clear(); 151226633Sdim CorrectionDecls.push_back(0); 152226633Sdim } 153224135Sdim 154226633Sdim // Check if this TypoCorrection is a keyword by checking if the first 155226633Sdim // item in CorrectionDecls is NULL. 156226633Sdim bool isKeyword() const { 157226633Sdim return !CorrectionDecls.empty() && 158226633Sdim CorrectionDecls.front() == 0; 159226633Sdim } 160226633Sdim 161234353Sdim // Check if this TypoCorrection is the given keyword. 162234353Sdim template<std::size_t StrLen> 163234353Sdim bool isKeyword(const char (&Str)[StrLen]) const { 164234353Sdim return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str); 165234353Sdim } 166234353Sdim 167224135Sdim // Returns true if the correction either is a keyword or has a known decl. 168226633Sdim bool isResolved() const { return !CorrectionDecls.empty(); } 169224135Sdim 170226633Sdim bool isOverloaded() const { 171226633Sdim return CorrectionDecls.size() > 1; 172226633Sdim } 173226633Sdim 174243830Sdim void setCorrectionRange(CXXScopeSpec* SS, 175243830Sdim const DeclarationNameInfo &TypoName) { 176243830Sdim CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc() 177243830Sdim : TypoName.getLoc()); 178243830Sdim CorrectionRange.setEnd(TypoName.getLoc()); 179243830Sdim } 180243830Sdim 181243830Sdim SourceRange getCorrectionRange() const { 182243830Sdim return CorrectionRange; 183243830Sdim } 184243830Sdim 185249423Sdim typedef SmallVector<NamedDecl *, 1>::iterator decl_iterator; 186226633Sdim decl_iterator begin() { 187226633Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 188226633Sdim } 189226633Sdim decl_iterator end() { return CorrectionDecls.end(); } 190249423Sdim typedef SmallVector<NamedDecl *, 1>::const_iterator const_decl_iterator; 191234353Sdim const_decl_iterator begin() const { 192234353Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 193234353Sdim } 194234353Sdim const_decl_iterator end() const { return CorrectionDecls.end(); } 195226633Sdim 196224135Sdimprivate: 197226633Sdim bool hasCorrectionDecl() const { 198226633Sdim return (!isKeyword() && !CorrectionDecls.empty()); 199226633Sdim } 200226633Sdim 201224135Sdim // Results. 202224135Sdim DeclarationName CorrectionName; 203224135Sdim NestedNameSpecifier *CorrectionNameSpec; 204249423Sdim SmallVector<NamedDecl *, 1> CorrectionDecls; 205234353Sdim unsigned CharDistance; 206234353Sdim unsigned QualifierDistance; 207234353Sdim unsigned CallbackDistance; 208243830Sdim SourceRange CorrectionRange; 209224135Sdim}; 210224135Sdim 211234353Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check 212234353Sdim/// the validity of a potential typo correction. 213234353Sdimclass CorrectionCandidateCallback { 214234353Sdim public: 215234353Sdim static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; 216234353Sdim 217234353Sdim CorrectionCandidateCallback() 218234353Sdim : WantTypeSpecifiers(true), WantExpressionKeywords(true), 219234353Sdim WantCXXNamedCasts(true), WantRemainingKeywords(true), 220234353Sdim WantObjCSuper(false), 221234353Sdim IsObjCIvarLookup(false) {} 222234353Sdim 223234353Sdim virtual ~CorrectionCandidateCallback() {} 224234353Sdim 225234353Sdim /// \brief Simple predicate used by the default RankCandidate to 226234353Sdim /// determine whether to return an edit distance of 0 or InvalidDistance. 227234353Sdim /// This can be overrided by validators that only need to determine if a 228234353Sdim /// candidate is viable, without ranking potentially viable candidates. 229234353Sdim /// Only ValidateCandidate or RankCandidate need to be overriden by a 230234353Sdim /// callback wishing to check the viability of correction candidates. 231249423Sdim /// The default predicate always returns true if the candidate is not a type 232249423Sdim /// name or keyword, true for types if WantTypeSpecifiers is true, and true 233249423Sdim /// for keywords if WantTypeSpecifiers, WantExpressionKeywords, 234249423Sdim /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true. 235249423Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate); 236234353Sdim 237234353Sdim /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank 238234353Sdim /// to a candidate (where a lower value represents a better candidate), or 239234353Sdim /// returning InvalidDistance if the candidate is not at all viable. For 240234353Sdim /// validation callbacks that only need to determine if a candidate is viable, 241234353Sdim /// the default RankCandidate returns either 0 or InvalidDistance depending 242234353Sdim /// whether ValidateCandidate returns true or false. 243234353Sdim virtual unsigned RankCandidate(const TypoCorrection &candidate) { 244234353Sdim return ValidateCandidate(candidate) ? 0 : InvalidDistance; 245234353Sdim } 246234353Sdim 247234353Sdim // Flags for context-dependent keywords. 248234353Sdim // TODO: Expand these to apply to non-keywords or possibly remove them. 249234353Sdim bool WantTypeSpecifiers; 250234353Sdim bool WantExpressionKeywords; 251234353Sdim bool WantCXXNamedCasts; 252234353Sdim bool WantRemainingKeywords; 253234353Sdim bool WantObjCSuper; 254234353Sdim // Temporary hack for the one case where a CorrectTypoContext enum is used 255234353Sdim // when looking up results. 256234353Sdim bool IsObjCIvarLookup; 257234353Sdim}; 258234353Sdim 259234353Sdim/// @brief Simple template class for restricting typo correction candidates 260234353Sdim/// to ones having a single Decl* of the given type. 261234353Sdimtemplate <class C> 262234353Sdimclass DeclFilterCCC : public CorrectionCandidateCallback { 263234353Sdim public: 264234353Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate) { 265234353Sdim return candidate.getCorrectionDeclAs<C>(); 266234353Sdim } 267234353Sdim}; 268234353Sdim 269224135Sdim} 270224135Sdim 271224135Sdim#endif 272