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, 42263508Sdim NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0, 43263508Sdim unsigned QualifierDistance = 0) 44234353Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 45263508Sdim CharDistance(CharDistance), QualifierDistance(QualifierDistance), 46263508Sdim CallbackDistance(0), ForceSpecifierReplacement(false), 47263508Sdim RequiresImport(false) { 48226633Sdim if (NameDecl) 49226633Sdim CorrectionDecls.push_back(NameDecl); 50226633Sdim } 51224135Sdim 52263508Sdim TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0, 53263508Sdim unsigned CharDistance = 0) 54234353Sdim : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), 55263508Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 56263508Sdim ForceSpecifierReplacement(false), RequiresImport(false) { 57226633Sdim if (Name) 58226633Sdim CorrectionDecls.push_back(Name); 59226633Sdim } 60224135Sdim 61263508Sdim TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0, 62263508Sdim unsigned CharDistance = 0) 63234353Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 64263508Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 65263508Sdim ForceSpecifierReplacement(false), RequiresImport(false) {} 66224135Sdim 67224135Sdim TypoCorrection() 68234353Sdim : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0), 69263508Sdim CallbackDistance(0), ForceSpecifierReplacement(false), 70263508Sdim RequiresImport(false) {} 71224135Sdim 72224135Sdim /// \brief Gets the DeclarationName of the typo correction 73224135Sdim DeclarationName getCorrection() const { return CorrectionName; } 74224135Sdim IdentifierInfo* getCorrectionAsIdentifierInfo() const { 75224135Sdim return CorrectionName.getAsIdentifierInfo(); 76224135Sdim } 77224135Sdim 78224135Sdim /// \brief Gets the NestedNameSpecifier needed to use the typo correction 79224135Sdim NestedNameSpecifier* getCorrectionSpecifier() const { 80224135Sdim return CorrectionNameSpec; 81224135Sdim } 82224135Sdim void setCorrectionSpecifier(NestedNameSpecifier* NNS) { 83224135Sdim CorrectionNameSpec = NNS; 84263508Sdim ForceSpecifierReplacement = (NNS != 0); 85224135Sdim } 86224135Sdim 87263508Sdim void WillReplaceSpecifier(bool ForceReplacement) { 88263508Sdim ForceSpecifierReplacement = ForceReplacement; 89263508Sdim } 90263508Sdim 91263508Sdim bool WillReplaceSpecifier() const { 92263508Sdim return ForceSpecifierReplacement; 93263508Sdim } 94263508Sdim 95234353Sdim void setQualifierDistance(unsigned ED) { 96234353Sdim QualifierDistance = ED; 97234353Sdim } 98224135Sdim 99234353Sdim void setCallbackDistance(unsigned ED) { 100234353Sdim CallbackDistance = ED; 101234353Sdim } 102234353Sdim 103234353Sdim // Convert the given weighted edit distance to a roughly equivalent number of 104234353Sdim // single-character edits (typically for comparison to the length of the 105234353Sdim // string being edited). 106234353Sdim static unsigned NormalizeEditDistance(unsigned ED) { 107234353Sdim if (ED > MaximumDistance) 108234353Sdim return InvalidDistance; 109234353Sdim return (ED + CharDistanceWeight / 2) / CharDistanceWeight; 110234353Sdim } 111234353Sdim 112234353Sdim /// \brief Gets the "edit distance" of the typo correction from the typo. 113234353Sdim /// If Normalized is true, scale the distance down by the CharDistanceWeight 114234353Sdim /// to return the edit distance in terms of single-character edits. 115234353Sdim unsigned getEditDistance(bool Normalized = true) const { 116234353Sdim if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance || 117234353Sdim CallbackDistance > MaximumDistance) 118234353Sdim return InvalidDistance; 119234353Sdim unsigned ED = 120234353Sdim CharDistance * CharDistanceWeight + 121234353Sdim QualifierDistance * QualifierDistanceWeight + 122234353Sdim CallbackDistance * CallbackDistanceWeight; 123234353Sdim if (ED > MaximumDistance) 124234353Sdim return InvalidDistance; 125234353Sdim // Half the CharDistanceWeight is added to ED to simulate rounding since 126234353Sdim // integer division truncates the value (i.e. round-to-nearest-int instead 127234353Sdim // of round-to-zero). 128234353Sdim return Normalized ? NormalizeEditDistance(ED) : ED; 129234353Sdim } 130234353Sdim 131224135Sdim /// \brief Gets the pointer to the declaration of the typo correction 132263508Sdim NamedDecl *getCorrectionDecl() const { 133226633Sdim return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; 134224135Sdim } 135224135Sdim template <class DeclClass> 136224135Sdim DeclClass *getCorrectionDeclAs() const { 137224135Sdim return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); 138224135Sdim } 139263508Sdim 140263508Sdim /// \brief Clears the list of NamedDecls. 141263508Sdim void ClearCorrectionDecls() { 142263508Sdim CorrectionDecls.clear(); 143263508Sdim } 144263508Sdim 145226633Sdim /// \brief Clears the list of NamedDecls before adding the new one. 146224135Sdim void setCorrectionDecl(NamedDecl *CDecl) { 147226633Sdim CorrectionDecls.clear(); 148226633Sdim addCorrectionDecl(CDecl); 149224135Sdim } 150224135Sdim 151263508Sdim /// \brief Clears the list of NamedDecls and adds the given set. 152263508Sdim void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) { 153263508Sdim CorrectionDecls.clear(); 154263508Sdim CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end()); 155263508Sdim } 156263508Sdim 157226633Sdim /// \brief Add the given NamedDecl to the list of NamedDecls that are the 158226633Sdim /// declarations associated with the DeclarationName of this TypoCorrection 159226633Sdim void addCorrectionDecl(NamedDecl *CDecl); 160226633Sdim 161224135Sdim std::string getAsString(const LangOptions &LO) const; 162224135Sdim std::string getQuoted(const LangOptions &LO) const { 163224135Sdim return "'" + getAsString(LO) + "'"; 164224135Sdim } 165224135Sdim 166226633Sdim /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName 167263508Sdim LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); } 168224135Sdim 169226633Sdim /// \brief Mark this TypoCorrection as being a keyword. 170226633Sdim /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be 171226633Sdim /// added to the list of the correction's NamedDecl pointers, NULL is added 172226633Sdim /// as the only element in the list to mark this TypoCorrection as a keyword. 173226633Sdim void makeKeyword() { 174226633Sdim CorrectionDecls.clear(); 175226633Sdim CorrectionDecls.push_back(0); 176263508Sdim ForceSpecifierReplacement = true; 177226633Sdim } 178224135Sdim 179226633Sdim // Check if this TypoCorrection is a keyword by checking if the first 180226633Sdim // item in CorrectionDecls is NULL. 181226633Sdim bool isKeyword() const { 182226633Sdim return !CorrectionDecls.empty() && 183226633Sdim CorrectionDecls.front() == 0; 184226633Sdim } 185226633Sdim 186234353Sdim // Check if this TypoCorrection is the given keyword. 187234353Sdim template<std::size_t StrLen> 188234353Sdim bool isKeyword(const char (&Str)[StrLen]) const { 189234353Sdim return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str); 190234353Sdim } 191234353Sdim 192224135Sdim // Returns true if the correction either is a keyword or has a known decl. 193226633Sdim bool isResolved() const { return !CorrectionDecls.empty(); } 194224135Sdim 195226633Sdim bool isOverloaded() const { 196226633Sdim return CorrectionDecls.size() > 1; 197226633Sdim } 198226633Sdim 199263508Sdim void setCorrectionRange(CXXScopeSpec *SS, 200243830Sdim const DeclarationNameInfo &TypoName) { 201263508Sdim CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty() 202263508Sdim ? SS->getBeginLoc() 203263508Sdim : TypoName.getLoc()); 204243830Sdim CorrectionRange.setEnd(TypoName.getLoc()); 205243830Sdim } 206243830Sdim 207243830Sdim SourceRange getCorrectionRange() const { 208243830Sdim return CorrectionRange; 209243830Sdim } 210243830Sdim 211263508Sdim typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator; 212226633Sdim decl_iterator begin() { 213226633Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 214226633Sdim } 215226633Sdim decl_iterator end() { return CorrectionDecls.end(); } 216263508Sdim typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator; 217234353Sdim const_decl_iterator begin() const { 218234353Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 219234353Sdim } 220234353Sdim const_decl_iterator end() const { return CorrectionDecls.end(); } 221226633Sdim 222263508Sdim /// \brief Returns whether this typo correction is correcting to a 223263508Sdim /// declaration that was declared in a module that has not been imported. 224263508Sdim bool requiresImport() const { return RequiresImport; } 225263508Sdim void setRequiresImport(bool Req) { RequiresImport = Req; } 226263508Sdim 227224135Sdimprivate: 228226633Sdim bool hasCorrectionDecl() const { 229226633Sdim return (!isKeyword() && !CorrectionDecls.empty()); 230226633Sdim } 231226633Sdim 232224135Sdim // Results. 233224135Sdim DeclarationName CorrectionName; 234224135Sdim NestedNameSpecifier *CorrectionNameSpec; 235249423Sdim SmallVector<NamedDecl *, 1> CorrectionDecls; 236234353Sdim unsigned CharDistance; 237234353Sdim unsigned QualifierDistance; 238234353Sdim unsigned CallbackDistance; 239243830Sdim SourceRange CorrectionRange; 240263508Sdim bool ForceSpecifierReplacement; 241263508Sdim bool RequiresImport; 242224135Sdim}; 243224135Sdim 244234353Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check 245234353Sdim/// the validity of a potential typo correction. 246234353Sdimclass CorrectionCandidateCallback { 247263508Sdimpublic: 248234353Sdim static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; 249234353Sdim 250234353Sdim CorrectionCandidateCallback() 251234353Sdim : WantTypeSpecifiers(true), WantExpressionKeywords(true), 252234353Sdim WantCXXNamedCasts(true), WantRemainingKeywords(true), 253234353Sdim WantObjCSuper(false), 254234353Sdim IsObjCIvarLookup(false) {} 255234353Sdim 256234353Sdim virtual ~CorrectionCandidateCallback() {} 257234353Sdim 258234353Sdim /// \brief Simple predicate used by the default RankCandidate to 259234353Sdim /// determine whether to return an edit distance of 0 or InvalidDistance. 260234353Sdim /// This can be overrided by validators that only need to determine if a 261234353Sdim /// candidate is viable, without ranking potentially viable candidates. 262234353Sdim /// Only ValidateCandidate or RankCandidate need to be overriden by a 263234353Sdim /// callback wishing to check the viability of correction candidates. 264249423Sdim /// The default predicate always returns true if the candidate is not a type 265249423Sdim /// name or keyword, true for types if WantTypeSpecifiers is true, and true 266249423Sdim /// for keywords if WantTypeSpecifiers, WantExpressionKeywords, 267249423Sdim /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true. 268249423Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate); 269234353Sdim 270234353Sdim /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank 271234353Sdim /// to a candidate (where a lower value represents a better candidate), or 272234353Sdim /// returning InvalidDistance if the candidate is not at all viable. For 273234353Sdim /// validation callbacks that only need to determine if a candidate is viable, 274234353Sdim /// the default RankCandidate returns either 0 or InvalidDistance depending 275234353Sdim /// whether ValidateCandidate returns true or false. 276234353Sdim virtual unsigned RankCandidate(const TypoCorrection &candidate) { 277234353Sdim return ValidateCandidate(candidate) ? 0 : InvalidDistance; 278234353Sdim } 279234353Sdim 280234353Sdim // Flags for context-dependent keywords. 281234353Sdim // TODO: Expand these to apply to non-keywords or possibly remove them. 282234353Sdim bool WantTypeSpecifiers; 283234353Sdim bool WantExpressionKeywords; 284234353Sdim bool WantCXXNamedCasts; 285234353Sdim bool WantRemainingKeywords; 286234353Sdim bool WantObjCSuper; 287234353Sdim // Temporary hack for the one case where a CorrectTypoContext enum is used 288234353Sdim // when looking up results. 289234353Sdim bool IsObjCIvarLookup; 290234353Sdim}; 291234353Sdim 292234353Sdim/// @brief Simple template class for restricting typo correction candidates 293234353Sdim/// to ones having a single Decl* of the given type. 294234353Sdimtemplate <class C> 295234353Sdimclass DeclFilterCCC : public CorrectionCandidateCallback { 296263508Sdimpublic: 297234353Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate) { 298234353Sdim return candidate.getCorrectionDeclAs<C>(); 299234353Sdim } 300234353Sdim}; 301234353Sdim 302263508Sdim// @brief Callback class to limit the allowed keywords and to only accept typo 303263508Sdim// corrections that are keywords or whose decls refer to functions (or template 304263508Sdim// functions) that accept the given number of arguments. 305263508Sdimclass FunctionCallFilterCCC : public CorrectionCandidateCallback { 306263508Sdimpublic: 307263508Sdim FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs, 308263508Sdim bool HasExplicitTemplateArgs); 309263508Sdim 310263508Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate); 311263508Sdim 312263508Sdim private: 313263508Sdim unsigned NumArgs; 314263508Sdim bool HasExplicitTemplateArgs; 315263508Sdim}; 316263508Sdim 317263508Sdim// @brief Callback class that effectively disabled typo correction 318263508Sdimclass NoTypoCorrectionCCC : public CorrectionCandidateCallback { 319263508Sdimpublic: 320263508Sdim NoTypoCorrectionCCC() { 321263508Sdim WantTypeSpecifiers = false; 322263508Sdim WantExpressionKeywords = false; 323263508Sdim WantCXXNamedCasts = false; 324263508Sdim WantRemainingKeywords = false; 325263508Sdim } 326263508Sdim 327263508Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate) { 328263508Sdim return false; 329263508Sdim } 330263508Sdim}; 331263508Sdim 332224135Sdim} 333224135Sdim 334224135Sdim#endif 335