TypoCorrection.h revision 263508
1272343Sngie//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===// 2272343Sngie// 3272343Sngie// The LLVM Compiler Infrastructure 4272343Sngie// 5272343Sngie// This file is distributed under the University of Illinois Open Source 6272343Sngie// License. See LICENSE.TXT for details. 7272343Sngie// 8272343Sngie//===----------------------------------------------------------------------===// 9272343Sngie// 10272343Sngie// This file defines the TypoCorrection class, which stores the results of 11272343Sngie// Sema's typo correction (Sema::CorrectTypo). 12272343Sngie// 13272343Sngie//===----------------------------------------------------------------------===// 14272343Sngie 15272343Sngie#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H 16272343Sngie#define LLVM_CLANG_SEMA_TYPOCORRECTION_H 17272343Sngie 18272343Sngie#include "clang/AST/DeclCXX.h" 19272343Sngie#include "clang/Sema/DeclSpec.h" 20272343Sngie#include "llvm/ADT/SmallVector.h" 21272343Sngie 22272343Sngienamespace clang { 23272343Sngie 24272343Sngie/// @brief Simple class containing the result of Sema::CorrectTypo 25272343Sngieclass TypoCorrection { 26272343Sngiepublic: 27272343Sngie // "Distance" for unusable corrections 28272343Sngie static const unsigned InvalidDistance = ~0U; 29272343Sngie // The largest distance still considered valid (larger edit distances are 30272343Sngie // mapped to InvalidDistance by getEditDistance). 31272343Sngie static const unsigned MaximumDistance = 10000U; 32272343Sngie 33272343Sngie // Relative weightings of the "edit distance" components. The higher the 34272343Sngie // weight, the more of a penalty to fitness the component will give (higher 35272343Sngie // weights mean greater contribution to the total edit distance, with the 36272343Sngie // best correction candidates having the lowest edit distance). 37272343Sngie static const unsigned CharDistanceWeight = 100U; 38272343Sngie static const unsigned QualifierDistanceWeight = 110U; 39272343Sngie static const unsigned CallbackDistanceWeight = 150U; 40272343Sngie 41272343Sngie TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, 42272343Sngie NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0, 43272343Sngie unsigned QualifierDistance = 0) 44272343Sngie : CorrectionName(Name), CorrectionNameSpec(NNS), 45272343Sngie CharDistance(CharDistance), QualifierDistance(QualifierDistance), 46272343Sngie CallbackDistance(0), ForceSpecifierReplacement(false), 47272343Sngie RequiresImport(false) { 48272343Sngie if (NameDecl) 49272343Sngie CorrectionDecls.push_back(NameDecl); 50272343Sngie } 51272343Sngie 52272343Sngie TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0, 53272343Sngie unsigned CharDistance = 0) 54272343Sngie : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), 55272343Sngie CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 56272343Sngie ForceSpecifierReplacement(false), RequiresImport(false) { 57272343Sngie if (Name) 58272343Sngie CorrectionDecls.push_back(Name); 59272343Sngie } 60272343Sngie 61272343Sngie TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0, 62272343Sngie unsigned CharDistance = 0) 63272343Sngie : CorrectionName(Name), CorrectionNameSpec(NNS), 64272343Sngie CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0), 65272343Sngie ForceSpecifierReplacement(false), RequiresImport(false) {} 66272343Sngie 67272343Sngie TypoCorrection() 68272343Sngie : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0), 69272343Sngie CallbackDistance(0), ForceSpecifierReplacement(false), 70272343Sngie RequiresImport(false) {} 71272343Sngie 72272343Sngie /// \brief Gets the DeclarationName of the typo correction 73272343Sngie DeclarationName getCorrection() const { return CorrectionName; } 74272343Sngie IdentifierInfo* getCorrectionAsIdentifierInfo() const { 75272343Sngie return CorrectionName.getAsIdentifierInfo(); 76272343Sngie } 77272343Sngie 78272343Sngie /// \brief Gets the NestedNameSpecifier needed to use the typo correction 79272343Sngie NestedNameSpecifier* getCorrectionSpecifier() const { 80272343Sngie return CorrectionNameSpec; 81272343Sngie } 82272343Sngie void setCorrectionSpecifier(NestedNameSpecifier* NNS) { 83272343Sngie CorrectionNameSpec = NNS; 84272343Sngie ForceSpecifierReplacement = (NNS != 0); 85272343Sngie } 86272343Sngie 87272343Sngie void WillReplaceSpecifier(bool ForceReplacement) { 88272343Sngie ForceSpecifierReplacement = ForceReplacement; 89272343Sngie } 90272343Sngie 91272343Sngie bool WillReplaceSpecifier() const { 92272343Sngie return ForceSpecifierReplacement; 93272343Sngie } 94272343Sngie 95272343Sngie void setQualifierDistance(unsigned ED) { 96272343Sngie QualifierDistance = ED; 97272343Sngie } 98272343Sngie 99272343Sngie void setCallbackDistance(unsigned ED) { 100272343Sngie CallbackDistance = ED; 101272343Sngie } 102272343Sngie 103272343Sngie // Convert the given weighted edit distance to a roughly equivalent number of 104272343Sngie // single-character edits (typically for comparison to the length of the 105272343Sngie // string being edited). 106272343Sngie static unsigned NormalizeEditDistance(unsigned ED) { 107272343Sngie if (ED > MaximumDistance) 108272343Sngie return InvalidDistance; 109272343Sngie return (ED + CharDistanceWeight / 2) / CharDistanceWeight; 110272343Sngie } 111272343Sngie 112272343Sngie /// \brief Gets the "edit distance" of the typo correction from the typo. 113272343Sngie /// If Normalized is true, scale the distance down by the CharDistanceWeight 114272343Sngie /// to return the edit distance in terms of single-character edits. 115272343Sngie unsigned getEditDistance(bool Normalized = true) const { 116272343Sngie if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance || 117272343Sngie CallbackDistance > MaximumDistance) 118272343Sngie return InvalidDistance; 119272343Sngie unsigned ED = 120272343Sngie CharDistance * CharDistanceWeight + 121272343Sngie QualifierDistance * QualifierDistanceWeight + 122272343Sngie CallbackDistance * CallbackDistanceWeight; 123272343Sngie if (ED > MaximumDistance) 124272343Sngie return InvalidDistance; 125272343Sngie // Half the CharDistanceWeight is added to ED to simulate rounding since 126272343Sngie // integer division truncates the value (i.e. round-to-nearest-int instead 127272343Sngie // of round-to-zero). 128272343Sngie return Normalized ? NormalizeEditDistance(ED) : ED; 129272343Sngie } 130272343Sngie 131272343Sngie /// \brief Gets the pointer to the declaration of the typo correction 132272343Sngie NamedDecl *getCorrectionDecl() const { 133272343Sngie return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0; 134272343Sngie } 135272343Sngie template <class DeclClass> 136272343Sngie DeclClass *getCorrectionDeclAs() const { 137272343Sngie return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); 138272343Sngie } 139272343Sngie 140272343Sngie /// \brief Clears the list of NamedDecls. 141272343Sngie void ClearCorrectionDecls() { 142272343Sngie CorrectionDecls.clear(); 143272343Sngie } 144272343Sngie 145272343Sngie /// \brief Clears the list of NamedDecls before adding the new one. 146272343Sngie void setCorrectionDecl(NamedDecl *CDecl) { 147272343Sngie CorrectionDecls.clear(); 148272343Sngie addCorrectionDecl(CDecl); 149272343Sngie } 150272343Sngie 151272343Sngie /// \brief Clears the list of NamedDecls and adds the given set. 152272343Sngie void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) { 153272343Sngie CorrectionDecls.clear(); 154272343Sngie CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end()); 155272343Sngie } 156272343Sngie 157272343Sngie /// \brief Add the given NamedDecl to the list of NamedDecls that are the 158272343Sngie /// declarations associated with the DeclarationName of this TypoCorrection 159272343Sngie void addCorrectionDecl(NamedDecl *CDecl); 160272343Sngie 161272343Sngie std::string getAsString(const LangOptions &LO) const; 162272343Sngie std::string getQuoted(const LangOptions &LO) const { 163272343Sngie return "'" + getAsString(LO) + "'"; 164272343Sngie } 165272343Sngie 166272343Sngie /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName 167272343Sngie LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); } 168272343Sngie 169272343Sngie /// \brief Mark this TypoCorrection as being a keyword. 170272343Sngie /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be 171272343Sngie /// added to the list of the correction's NamedDecl pointers, NULL is added 172272343Sngie /// as the only element in the list to mark this TypoCorrection as a keyword. 173272343Sngie void makeKeyword() { 174272343Sngie CorrectionDecls.clear(); 175272343Sngie CorrectionDecls.push_back(0); 176272343Sngie ForceSpecifierReplacement = true; 177272343Sngie } 178272343Sngie 179272343Sngie // Check if this TypoCorrection is a keyword by checking if the first 180272343Sngie // item in CorrectionDecls is NULL. 181272343Sngie bool isKeyword() const { 182272343Sngie return !CorrectionDecls.empty() && 183272343Sngie CorrectionDecls.front() == 0; 184272343Sngie } 185272343Sngie 186272343Sngie // Check if this TypoCorrection is the given keyword. 187272343Sngie template<std::size_t StrLen> 188272343Sngie bool isKeyword(const char (&Str)[StrLen]) const { 189272343Sngie return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str); 190272343Sngie } 191272343Sngie 192272343Sngie // Returns true if the correction either is a keyword or has a known decl. 193272343Sngie bool isResolved() const { return !CorrectionDecls.empty(); } 194272343Sngie 195272343Sngie bool isOverloaded() const { 196272343Sngie return CorrectionDecls.size() > 1; 197272343Sngie } 198272343Sngie 199272343Sngie void setCorrectionRange(CXXScopeSpec *SS, 200272343Sngie const DeclarationNameInfo &TypoName) { 201272343Sngie CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty() 202272343Sngie ? SS->getBeginLoc() 203272343Sngie : TypoName.getLoc()); 204272343Sngie CorrectionRange.setEnd(TypoName.getLoc()); 205272343Sngie } 206272343Sngie 207272343Sngie SourceRange getCorrectionRange() const { 208272343Sngie return CorrectionRange; 209272343Sngie } 210272343Sngie 211272343Sngie typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator; 212272343Sngie decl_iterator begin() { 213272343Sngie return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 214272343Sngie } 215272343Sngie decl_iterator end() { return CorrectionDecls.end(); } 216272343Sngie typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator; 217272343Sngie const_decl_iterator begin() const { 218272343Sngie return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); 219272343Sngie } 220272343Sngie const_decl_iterator end() const { return CorrectionDecls.end(); } 221272343Sngie 222272343Sngie /// \brief Returns whether this typo correction is correcting to a 223272343Sngie /// declaration that was declared in a module that has not been imported. 224272343Sngie bool requiresImport() const { return RequiresImport; } 225272343Sngie void setRequiresImport(bool Req) { RequiresImport = Req; } 226272343Sngie 227272343Sngieprivate: 228272343Sngie bool hasCorrectionDecl() const { 229272343Sngie return (!isKeyword() && !CorrectionDecls.empty()); 230272343Sngie } 231272343Sngie 232272343Sngie // Results. 233272343Sngie DeclarationName CorrectionName; 234272343Sngie NestedNameSpecifier *CorrectionNameSpec; 235272343Sngie SmallVector<NamedDecl *, 1> CorrectionDecls; 236272343Sngie unsigned CharDistance; 237272343Sngie unsigned QualifierDistance; 238272343Sngie unsigned CallbackDistance; 239272343Sngie SourceRange CorrectionRange; 240272343Sngie bool ForceSpecifierReplacement; 241272343Sngie bool RequiresImport; 242272343Sngie}; 243272343Sngie 244272343Sngie/// @brief Base class for callback objects used by Sema::CorrectTypo to check 245272343Sngie/// the validity of a potential typo correction. 246272343Sngieclass CorrectionCandidateCallback { 247272343Sngiepublic: 248272343Sngie static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; 249272343Sngie 250272343Sngie CorrectionCandidateCallback() 251272343Sngie : WantTypeSpecifiers(true), WantExpressionKeywords(true), 252272343Sngie WantCXXNamedCasts(true), WantRemainingKeywords(true), 253272343Sngie WantObjCSuper(false), 254272343Sngie IsObjCIvarLookup(false) {} 255272343Sngie 256272343Sngie virtual ~CorrectionCandidateCallback() {} 257272343Sngie 258272343Sngie /// \brief Simple predicate used by the default RankCandidate to 259272343Sngie /// determine whether to return an edit distance of 0 or InvalidDistance. 260272343Sngie /// This can be overrided by validators that only need to determine if a 261272343Sngie /// candidate is viable, without ranking potentially viable candidates. 262272343Sngie /// Only ValidateCandidate or RankCandidate need to be overriden by a 263272343Sngie /// callback wishing to check the viability of correction candidates. 264272343Sngie /// The default predicate always returns true if the candidate is not a type 265272343Sngie /// name or keyword, true for types if WantTypeSpecifiers is true, and true 266272343Sngie /// for keywords if WantTypeSpecifiers, WantExpressionKeywords, 267272343Sngie /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true. 268272343Sngie virtual bool ValidateCandidate(const TypoCorrection &candidate); 269272343Sngie 270272343Sngie /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank 271272343Sngie /// to a candidate (where a lower value represents a better candidate), or 272272343Sngie /// returning InvalidDistance if the candidate is not at all viable. For 273272343Sngie /// validation callbacks that only need to determine if a candidate is viable, 274272343Sngie /// the default RankCandidate returns either 0 or InvalidDistance depending 275272343Sngie /// whether ValidateCandidate returns true or false. 276272343Sngie virtual unsigned RankCandidate(const TypoCorrection &candidate) { 277272343Sngie return ValidateCandidate(candidate) ? 0 : InvalidDistance; 278272343Sngie } 279272343Sngie 280272343Sngie // Flags for context-dependent keywords. 281272343Sngie // TODO: Expand these to apply to non-keywords or possibly remove them. 282272343Sngie bool WantTypeSpecifiers; 283272343Sngie bool WantExpressionKeywords; 284272343Sngie bool WantCXXNamedCasts; 285272343Sngie bool WantRemainingKeywords; 286272343Sngie bool WantObjCSuper; 287272343Sngie // Temporary hack for the one case where a CorrectTypoContext enum is used 288272343Sngie // when looking up results. 289272343Sngie bool IsObjCIvarLookup; 290272343Sngie}; 291272343Sngie 292272343Sngie/// @brief Simple template class for restricting typo correction candidates 293272343Sngie/// to ones having a single Decl* of the given type. 294272343Sngietemplate <class C> 295272343Sngieclass DeclFilterCCC : public CorrectionCandidateCallback { 296272343Sngiepublic: 297272343Sngie virtual bool ValidateCandidate(const TypoCorrection &candidate) { 298272343Sngie return candidate.getCorrectionDeclAs<C>(); 299272343Sngie } 300272343Sngie}; 301272343Sngie 302272343Sngie// @brief Callback class to limit the allowed keywords and to only accept typo 303272343Sngie// corrections that are keywords or whose decls refer to functions (or template 304272343Sngie// functions) that accept the given number of arguments. 305272343Sngieclass FunctionCallFilterCCC : public CorrectionCandidateCallback { 306272343Sngiepublic: 307272343Sngie FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs, 308272343Sngie bool HasExplicitTemplateArgs); 309272343Sngie 310272343Sngie virtual bool ValidateCandidate(const TypoCorrection &candidate); 311272343Sngie 312272343Sngie private: 313272343Sngie unsigned NumArgs; 314272343Sngie bool HasExplicitTemplateArgs; 315272343Sngie}; 316272343Sngie 317272343Sngie// @brief Callback class that effectively disabled typo correction 318272343Sngieclass NoTypoCorrectionCCC : public CorrectionCandidateCallback { 319272343Sngiepublic: 320272343Sngie NoTypoCorrectionCCC() { 321272343Sngie WantTypeSpecifiers = false; 322272343Sngie WantExpressionKeywords = false; 323272343Sngie WantCXXNamedCasts = false; 324272343Sngie WantRemainingKeywords = false; 325272343Sngie } 326272343Sngie 327272343Sngie virtual bool ValidateCandidate(const TypoCorrection &candidate) { 328272343Sngie return false; 329272343Sngie } 330272343Sngie}; 331272343Sngie 332272343Sngie} 333272343Sngie 334272343Sngie#endif 335272343Sngie