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" 19252723Sdim#include "clang/Sema/DeclSpec.h" 20226890Sdim#include "llvm/ADT/SmallVector.h" 21224135Sdim 22224135Sdimnamespace clang { 23224135Sdim 24224135Sdim/// @brief Simple class containing the result of Sema::CorrectTypo 25224135Sdimclass TypoCorrection { 26224135Sdimpublic: 27235633Sdim // "Distance" for unusable corrections 28235633Sdim static const unsigned InvalidDistance = ~0U; 29235633Sdim // The largest distance still considered valid (larger edit distances are 30235633Sdim // mapped to InvalidDistance by getEditDistance). 31235633Sdim static const unsigned MaximumDistance = 10000U; 32235633Sdim 33235633Sdim // Relative weightings of the "edit distance" components. The higher the 34235633Sdim // weight, the more of a penalty to fitness the component will give (higher 35235633Sdim // weights mean greater contribution to the total edit distance, with the 36235633Sdim // best correction candidates having the lowest edit distance). 37235633Sdim static const unsigned CharDistanceWeight = 100U; 38235633Sdim static const unsigned QualifierDistanceWeight = 110U; 39235633Sdim static const unsigned CallbackDistanceWeight = 150U; 40235633Sdim 41224135Sdim TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, 42263509Sdim NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0, 43263509Sdim unsigned QualifierDistance = 0) 44235633Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 45263509Sdim CharDistance(CharDistance), QualifierDistance(QualifierDistance), 46263509Sdim CallbackDistance(0), ForceSpecifierReplacement(false), 47263509Sdim RequiresImport(false) { 48226890Sdim if (NameDecl) 49226890Sdim CorrectionDecls.push_back(NameDecl); 50226890Sdim } 51224135Sdim 52263509Sdim TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0, 53263509Sdim unsigned CharDistance = 0) 54235633Sdim : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), 55263509Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 56263509Sdim ForceSpecifierReplacement(false), RequiresImport(false) { 57226890Sdim if (Name) 58226890Sdim CorrectionDecls.push_back(Name); 59226890Sdim } 60224135Sdim 61263509Sdim TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0, 62263509Sdim unsigned CharDistance = 0) 63235633Sdim : CorrectionName(Name), CorrectionNameSpec(NNS), 64263509Sdim CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 65263509Sdim ForceSpecifierReplacement(false), RequiresImport(false) {} 66224135Sdim 67224135Sdim TypoCorrection() 68235633Sdim : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0), 69263509Sdim CallbackDistance(0), ForceSpecifierReplacement(false), 70263509Sdim 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; 84263509Sdim ForceSpecifierReplacement = (NNS != 0); 85224135Sdim } 86224135Sdim 87263509Sdim void WillReplaceSpecifier(bool ForceReplacement) { 88263509Sdim ForceSpecifierReplacement = ForceReplacement; 89263509Sdim } 90263509Sdim 91263509Sdim bool WillReplaceSpecifier() const { 92263509Sdim return ForceSpecifierReplacement; 93263509Sdim } 94263509Sdim 95235633Sdim void setQualifierDistance(unsigned ED) { 96235633Sdim QualifierDistance = ED; 97235633Sdim } 98224135Sdim 99235633Sdim void setCallbackDistance(unsigned ED) { 100235633Sdim CallbackDistance = ED; 101235633Sdim } 102235633Sdim 103235633Sdim // Convert the given weighted edit distance to a roughly equivalent number of 104235633Sdim // single-character edits (typically for comparison to the length of the 105235633Sdim // string being edited). 106235633Sdim static unsigned NormalizeEditDistance(unsigned ED) { 107235633Sdim if (ED > MaximumDistance) 108235633Sdim return InvalidDistance; 109235633Sdim return (ED + CharDistanceWeight / 2) / CharDistanceWeight; 110235633Sdim } 111235633Sdim 112235633Sdim /// \brief Gets the "edit distance" of the typo correction from the typo. 113235633Sdim /// If Normalized is true, scale the distance down by the CharDistanceWeight 114235633Sdim /// to return the edit distance in terms of single-character edits. 115235633Sdim unsigned getEditDistance(bool Normalized = true) const { 116235633Sdim if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance || 117235633Sdim CallbackDistance > MaximumDistance) 118235633Sdim return InvalidDistance; 119235633Sdim unsigned ED = 120235633Sdim CharDistance * CharDistanceWeight + 121235633Sdim QualifierDistance * QualifierDistanceWeight + 122235633Sdim CallbackDistance * CallbackDistanceWeight; 123235633Sdim if (ED > MaximumDistance) 124235633Sdim return InvalidDistance; 125235633Sdim // Half the CharDistanceWeight is added to ED to simulate rounding since 126235633Sdim // integer division truncates the value (i.e. round-to-nearest-int instead 127235633Sdim // of round-to-zero). 128235633Sdim return Normalized ? NormalizeEditDistance(ED) : ED; 129235633Sdim } 130235633Sdim 131224135Sdim /// \brief Gets the pointer to the declaration of the typo correction 132263509Sdim NamedDecl *getCorrectionDecl() const { 133226890Sdim return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; 134224135Sdim } 135224135Sdim template <class DeclClass> 136224135Sdim DeclClass *getCorrectionDeclAs() const { 137224135Sdim return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); 138224135Sdim } 139263509Sdim 140263509Sdim /// \brief Clears the list of NamedDecls. 141263509Sdim void ClearCorrectionDecls() { 142263509Sdim CorrectionDecls.clear(); 143263509Sdim } 144263509Sdim 145226890Sdim /// \brief Clears the list of NamedDecls before adding the new one. 146224135Sdim void setCorrectionDecl(NamedDecl *CDecl) { 147226890Sdim CorrectionDecls.clear(); 148226890Sdim addCorrectionDecl(CDecl); 149224135Sdim } 150224135Sdim 151263509Sdim /// \brief Clears the list of NamedDecls and adds the given set. 152263509Sdim void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) { 153263509Sdim CorrectionDecls.clear(); 154263509Sdim CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end()); 155263509Sdim } 156263509Sdim 157226890Sdim /// \brief Add the given NamedDecl to the list of NamedDecls that are the 158226890Sdim /// declarations associated with the DeclarationName of this TypoCorrection 159226890Sdim void addCorrectionDecl(NamedDecl *CDecl); 160226890Sdim 161224135Sdim std::string getAsString(const LangOptions &LO) const; 162224135Sdim std::string getQuoted(const LangOptions &LO) const { 163224135Sdim return "'" + getAsString(LO) + "'"; 164224135Sdim } 165224135Sdim 166226890Sdim /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName 167263509Sdim LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); } 168224135Sdim 169226890Sdim /// \brief Mark this TypoCorrection as being a keyword. 170226890Sdim /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be 171226890Sdim /// added to the list of the correction's NamedDecl pointers, NULL is added 172226890Sdim /// as the only element in the list to mark this TypoCorrection as a keyword. 173226890Sdim void makeKeyword() { 174226890Sdim CorrectionDecls.clear(); 175226890Sdim CorrectionDecls.push_back(0); 176263509Sdim ForceSpecifierReplacement = true; 177226890Sdim } 178224135Sdim 179226890Sdim // Check if this TypoCorrection is a keyword by checking if the first 180226890Sdim // item in CorrectionDecls is NULL. 181226890Sdim bool isKeyword() const { 182226890Sdim return !CorrectionDecls.empty() && 183226890Sdim CorrectionDecls.front() == 0; 184226890Sdim } 185226890Sdim 186235633Sdim // Check if this TypoCorrection is the given keyword. 187235633Sdim template<std::size_t StrLen> 188235633Sdim bool isKeyword(const char (&Str)[StrLen]) const { 189235633Sdim return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str); 190235633Sdim } 191235633Sdim 192224135Sdim // Returns true if the correction either is a keyword or has a known decl. 193226890Sdim bool isResolved() const { return !CorrectionDecls.empty(); } 194224135Sdim 195226890Sdim bool isOverloaded() const { 196226890Sdim return CorrectionDecls.size() > 1; 197226890Sdim } 198226890Sdim 199263509Sdim void setCorrectionRange(CXXScopeSpec *SS, 200245431Sdim const DeclarationNameInfo &TypoName) { 201263509Sdim CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty() 202263509Sdim ? SS->getBeginLoc() 203263509Sdim : TypoName.getLoc()); 204245431Sdim CorrectionRange.setEnd(TypoName.getLoc()); 205245431Sdim } 206245431Sdim 207245431Sdim SourceRange getCorrectionRange() const { 208245431Sdim return CorrectionRange; 209245431Sdim } 210245431Sdim 211263509Sdim typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator; 212226890Sdim decl_iterator begin() { 213226890Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 214226890Sdim } 215226890Sdim decl_iterator end() { return CorrectionDecls.end(); } 216263509Sdim typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator; 217235633Sdim const_decl_iterator begin() const { 218235633Sdim return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 219235633Sdim } 220235633Sdim const_decl_iterator end() const { return CorrectionDecls.end(); } 221226890Sdim 222263509Sdim /// \brief Returns whether this typo correction is correcting to a 223263509Sdim /// declaration that was declared in a module that has not been imported. 224263509Sdim bool requiresImport() const { return RequiresImport; } 225263509Sdim void setRequiresImport(bool Req) { RequiresImport = Req; } 226263509Sdim 227224135Sdimprivate: 228226890Sdim bool hasCorrectionDecl() const { 229226890Sdim return (!isKeyword() && !CorrectionDecls.empty()); 230226890Sdim } 231226890Sdim 232224135Sdim // Results. 233224135Sdim DeclarationName CorrectionName; 234224135Sdim NestedNameSpecifier *CorrectionNameSpec; 235252723Sdim SmallVector<NamedDecl *, 1> CorrectionDecls; 236235633Sdim unsigned CharDistance; 237235633Sdim unsigned QualifierDistance; 238235633Sdim unsigned CallbackDistance; 239245431Sdim SourceRange CorrectionRange; 240263509Sdim bool ForceSpecifierReplacement; 241263509Sdim bool RequiresImport; 242224135Sdim}; 243224135Sdim 244235633Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check 245235633Sdim/// the validity of a potential typo correction. 246235633Sdimclass CorrectionCandidateCallback { 247263509Sdimpublic: 248235633Sdim static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; 249235633Sdim 250235633Sdim CorrectionCandidateCallback() 251235633Sdim : WantTypeSpecifiers(true), WantExpressionKeywords(true), 252235633Sdim WantCXXNamedCasts(true), WantRemainingKeywords(true), 253235633Sdim WantObjCSuper(false), 254235633Sdim IsObjCIvarLookup(false) {} 255235633Sdim 256235633Sdim virtual ~CorrectionCandidateCallback() {} 257235633Sdim 258235633Sdim /// \brief Simple predicate used by the default RankCandidate to 259235633Sdim /// determine whether to return an edit distance of 0 or InvalidDistance. 260235633Sdim /// This can be overrided by validators that only need to determine if a 261235633Sdim /// candidate is viable, without ranking potentially viable candidates. 262235633Sdim /// Only ValidateCandidate or RankCandidate need to be overriden by a 263235633Sdim /// callback wishing to check the viability of correction candidates. 264252723Sdim /// The default predicate always returns true if the candidate is not a type 265252723Sdim /// name or keyword, true for types if WantTypeSpecifiers is true, and true 266252723Sdim /// for keywords if WantTypeSpecifiers, WantExpressionKeywords, 267252723Sdim /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true. 268252723Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate); 269235633Sdim 270235633Sdim /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank 271235633Sdim /// to a candidate (where a lower value represents a better candidate), or 272235633Sdim /// returning InvalidDistance if the candidate is not at all viable. For 273235633Sdim /// validation callbacks that only need to determine if a candidate is viable, 274235633Sdim /// the default RankCandidate returns either 0 or InvalidDistance depending 275235633Sdim /// whether ValidateCandidate returns true or false. 276235633Sdim virtual unsigned RankCandidate(const TypoCorrection &candidate) { 277235633Sdim return ValidateCandidate(candidate) ? 0 : InvalidDistance; 278235633Sdim } 279235633Sdim 280235633Sdim // Flags for context-dependent keywords. 281235633Sdim // TODO: Expand these to apply to non-keywords or possibly remove them. 282235633Sdim bool WantTypeSpecifiers; 283235633Sdim bool WantExpressionKeywords; 284235633Sdim bool WantCXXNamedCasts; 285235633Sdim bool WantRemainingKeywords; 286235633Sdim bool WantObjCSuper; 287235633Sdim // Temporary hack for the one case where a CorrectTypoContext enum is used 288235633Sdim // when looking up results. 289235633Sdim bool IsObjCIvarLookup; 290235633Sdim}; 291235633Sdim 292235633Sdim/// @brief Simple template class for restricting typo correction candidates 293235633Sdim/// to ones having a single Decl* of the given type. 294235633Sdimtemplate <class C> 295235633Sdimclass DeclFilterCCC : public CorrectionCandidateCallback { 296263509Sdimpublic: 297235633Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate) { 298235633Sdim return candidate.getCorrectionDeclAs<C>(); 299235633Sdim } 300235633Sdim}; 301235633Sdim 302263509Sdim// @brief Callback class to limit the allowed keywords and to only accept typo 303263509Sdim// corrections that are keywords or whose decls refer to functions (or template 304263509Sdim// functions) that accept the given number of arguments. 305263509Sdimclass FunctionCallFilterCCC : public CorrectionCandidateCallback { 306263509Sdimpublic: 307263509Sdim FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs, 308263509Sdim bool HasExplicitTemplateArgs); 309263509Sdim 310263509Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate); 311263509Sdim 312263509Sdim private: 313263509Sdim unsigned NumArgs; 314263509Sdim bool HasExplicitTemplateArgs; 315263509Sdim}; 316263509Sdim 317263509Sdim// @brief Callback class that effectively disabled typo correction 318263509Sdimclass NoTypoCorrectionCCC : public CorrectionCandidateCallback { 319263509Sdimpublic: 320263509Sdim NoTypoCorrectionCCC() { 321263509Sdim WantTypeSpecifiers = false; 322263509Sdim WantExpressionKeywords = false; 323263509Sdim WantCXXNamedCasts = false; 324263509Sdim WantRemainingKeywords = false; 325263509Sdim } 326263509Sdim 327263509Sdim virtual bool ValidateCandidate(const TypoCorrection &candidate) { 328263509Sdim return false; 329263509Sdim } 330263509Sdim}; 331263509Sdim 332224135Sdim} 333224135Sdim 334224135Sdim#endif 335