clang API Documentation

TypoCorrection.h
Go to the documentation of this file.
00001 //===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file defines the TypoCorrection class, which stores the results of
00011 // Sema's typo correction (Sema::CorrectTypo).
00012 //
00013 //===----------------------------------------------------------------------===//
00014 
00015 #ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
00016 #define LLVM_CLANG_SEMA_TYPOCORRECTION_H
00017 
00018 #include "clang/AST/DeclCXX.h"
00019 #include "clang/Sema/DeclSpec.h"
00020 #include "clang/Sema/Ownership.h"
00021 #include "llvm/ADT/SmallVector.h"
00022 
00023 namespace clang {
00024 
00025 /// @brief Simple class containing the result of Sema::CorrectTypo
00026 class TypoCorrection {
00027 public:
00028   // "Distance" for unusable corrections
00029   static const unsigned InvalidDistance = ~0U;
00030   // The largest distance still considered valid (larger edit distances are
00031   // mapped to InvalidDistance by getEditDistance).
00032   static const unsigned MaximumDistance = 10000U;
00033 
00034   // Relative weightings of the "edit distance" components. The higher the
00035   // weight, the more of a penalty to fitness the component will give (higher
00036   // weights mean greater contribution to the total edit distance, with the
00037   // best correction candidates having the lowest edit distance).
00038   static const unsigned CharDistanceWeight = 100U;
00039   static const unsigned QualifierDistanceWeight = 110U;
00040   static const unsigned CallbackDistanceWeight = 150U;
00041 
00042   TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
00043                  NestedNameSpecifier *NNS = nullptr, unsigned CharDistance = 0,
00044                  unsigned QualifierDistance = 0)
00045       : CorrectionName(Name), CorrectionNameSpec(NNS),
00046         CharDistance(CharDistance), QualifierDistance(QualifierDistance),
00047         CallbackDistance(0), ForceSpecifierReplacement(false),
00048         RequiresImport(false) {
00049     if (NameDecl)
00050       CorrectionDecls.push_back(NameDecl);
00051   }
00052 
00053   TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = nullptr,
00054                  unsigned CharDistance = 0)
00055       : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
00056         CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
00057         ForceSpecifierReplacement(false), RequiresImport(false) {
00058     if (Name)
00059       CorrectionDecls.push_back(Name);
00060   }
00061 
00062   TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = nullptr,
00063                  unsigned CharDistance = 0)
00064       : CorrectionName(Name), CorrectionNameSpec(NNS),
00065         CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
00066         ForceSpecifierReplacement(false), RequiresImport(false) {}
00067 
00068   TypoCorrection()
00069       : CorrectionNameSpec(nullptr), CharDistance(0), QualifierDistance(0),
00070         CallbackDistance(0), ForceSpecifierReplacement(false),
00071         RequiresImport(false) {}
00072 
00073   /// \brief Gets the DeclarationName of the typo correction
00074   DeclarationName getCorrection() const { return CorrectionName; }
00075   IdentifierInfo* getCorrectionAsIdentifierInfo() const {
00076     return CorrectionName.getAsIdentifierInfo();
00077   }
00078 
00079   /// \brief Gets the NestedNameSpecifier needed to use the typo correction
00080   NestedNameSpecifier* getCorrectionSpecifier() const {
00081     return CorrectionNameSpec;
00082   }
00083   void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
00084     CorrectionNameSpec = NNS;
00085     ForceSpecifierReplacement = (NNS != nullptr);
00086   }
00087 
00088   void WillReplaceSpecifier(bool ForceReplacement) {
00089     ForceSpecifierReplacement = ForceReplacement;
00090   }
00091 
00092   bool WillReplaceSpecifier() const {
00093     return ForceSpecifierReplacement;
00094   }
00095 
00096   void setQualifierDistance(unsigned ED) {
00097     QualifierDistance = ED;
00098   }
00099 
00100   void setCallbackDistance(unsigned ED) {
00101     CallbackDistance = ED;
00102   }
00103 
00104   // Convert the given weighted edit distance to a roughly equivalent number of
00105   // single-character edits (typically for comparison to the length of the
00106   // string being edited).
00107   static unsigned NormalizeEditDistance(unsigned ED) {
00108     if (ED > MaximumDistance)
00109       return InvalidDistance;
00110     return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
00111   }
00112 
00113   /// \brief Gets the "edit distance" of the typo correction from the typo.
00114   /// If Normalized is true, scale the distance down by the CharDistanceWeight
00115   /// to return the edit distance in terms of single-character edits.
00116   unsigned getEditDistance(bool Normalized = true) const {
00117     if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
00118         CallbackDistance > MaximumDistance)
00119       return InvalidDistance;
00120     unsigned ED =
00121         CharDistance * CharDistanceWeight +
00122         QualifierDistance * QualifierDistanceWeight +
00123         CallbackDistance * CallbackDistanceWeight;
00124     if (ED > MaximumDistance)
00125       return InvalidDistance;
00126     // Half the CharDistanceWeight is added to ED to simulate rounding since
00127     // integer division truncates the value (i.e. round-to-nearest-int instead
00128     // of round-to-zero).
00129     return Normalized ? NormalizeEditDistance(ED) : ED;
00130   }
00131 
00132   /// \brief Gets the pointer to the declaration of the typo correction
00133   NamedDecl *getCorrectionDecl() const {
00134     return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : nullptr;
00135   }
00136   template <class DeclClass>
00137   DeclClass *getCorrectionDeclAs() const {
00138     return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
00139   }
00140 
00141   /// \brief Clears the list of NamedDecls.
00142   void ClearCorrectionDecls() {
00143     CorrectionDecls.clear();
00144   }
00145 
00146   /// \brief Clears the list of NamedDecls before adding the new one.
00147   void setCorrectionDecl(NamedDecl *CDecl) {
00148     CorrectionDecls.clear();
00149     addCorrectionDecl(CDecl);
00150   }
00151 
00152   /// \brief Clears the list of NamedDecls and adds the given set.
00153   void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
00154     CorrectionDecls.clear();
00155     CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
00156   }
00157 
00158   /// \brief Add the given NamedDecl to the list of NamedDecls that are the
00159   /// declarations associated with the DeclarationName of this TypoCorrection
00160   void addCorrectionDecl(NamedDecl *CDecl);
00161 
00162   std::string getAsString(const LangOptions &LO) const;
00163   std::string getQuoted(const LangOptions &LO) const {
00164     return "'" + getAsString(LO) + "'";
00165   }
00166 
00167   /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
00168   LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
00169 
00170   /// \brief Mark this TypoCorrection as being a keyword.
00171   /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
00172   /// added to the list of the correction's NamedDecl pointers, NULL is added
00173   /// as the only element in the list to mark this TypoCorrection as a keyword.
00174   void makeKeyword() {
00175     CorrectionDecls.clear();
00176     CorrectionDecls.push_back(nullptr);
00177     ForceSpecifierReplacement = true;
00178   }
00179 
00180   // Check if this TypoCorrection is a keyword by checking if the first
00181   // item in CorrectionDecls is NULL.
00182   bool isKeyword() const {
00183     return !CorrectionDecls.empty() &&
00184         CorrectionDecls.front() == nullptr;
00185   }
00186 
00187   // Check if this TypoCorrection is the given keyword.
00188   template<std::size_t StrLen>
00189   bool isKeyword(const char (&Str)[StrLen]) const {
00190     return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
00191   }
00192 
00193   // Returns true if the correction either is a keyword or has a known decl.
00194   bool isResolved() const { return !CorrectionDecls.empty(); }
00195 
00196   bool isOverloaded() const {
00197     return CorrectionDecls.size() > 1;
00198   }
00199 
00200   void setCorrectionRange(CXXScopeSpec *SS,
00201                           const DeclarationNameInfo &TypoName) {
00202     CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
00203                                  ? SS->getBeginLoc()
00204                                  : TypoName.getLoc());
00205     CorrectionRange.setEnd(TypoName.getLoc());
00206   }
00207 
00208   SourceRange getCorrectionRange() const {
00209     return CorrectionRange;
00210   }
00211 
00212   typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
00213   decl_iterator begin() {
00214     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
00215   }
00216   decl_iterator end() { return CorrectionDecls.end(); }
00217   typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
00218   const_decl_iterator begin() const {
00219     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
00220   }
00221   const_decl_iterator end() const { return CorrectionDecls.end(); }
00222 
00223   /// \brief Returns whether this typo correction is correcting to a
00224   /// declaration that was declared in a module that has not been imported.
00225   bool requiresImport() const { return RequiresImport; }
00226   void setRequiresImport(bool Req) { RequiresImport = Req; }
00227 
00228 private:
00229   bool hasCorrectionDecl() const {
00230     return (!isKeyword() && !CorrectionDecls.empty());
00231   }
00232 
00233   // Results.
00234   DeclarationName CorrectionName;
00235   NestedNameSpecifier *CorrectionNameSpec;
00236   SmallVector<NamedDecl *, 1> CorrectionDecls;
00237   unsigned CharDistance;
00238   unsigned QualifierDistance;
00239   unsigned CallbackDistance;
00240   SourceRange CorrectionRange;
00241   bool ForceSpecifierReplacement;
00242   bool RequiresImport;
00243 };
00244 
00245 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
00246 /// the validity of a potential typo correction.
00247 class CorrectionCandidateCallback {
00248 public:
00249   static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
00250 
00251   explicit CorrectionCandidateCallback(IdentifierInfo *Typo = nullptr,
00252                                        NestedNameSpecifier *TypoNNS = nullptr)
00253       : WantTypeSpecifiers(true), WantExpressionKeywords(true),
00254         WantCXXNamedCasts(true), WantFunctionLikeCasts(true),
00255         WantRemainingKeywords(true), WantObjCSuper(false),
00256         IsObjCIvarLookup(false), IsAddressOfOperand(false), Typo(Typo),
00257         TypoNNS(TypoNNS) {}
00258 
00259   virtual ~CorrectionCandidateCallback() {}
00260 
00261   /// \brief Simple predicate used by the default RankCandidate to
00262   /// determine whether to return an edit distance of 0 or InvalidDistance.
00263   /// This can be overrided by validators that only need to determine if a
00264   /// candidate is viable, without ranking potentially viable candidates.
00265   /// Only ValidateCandidate or RankCandidate need to be overriden by a
00266   /// callback wishing to check the viability of correction candidates.
00267   /// The default predicate always returns true if the candidate is not a type
00268   /// name or keyword, true for types if WantTypeSpecifiers is true, and true
00269   /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
00270   /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
00271   virtual bool ValidateCandidate(const TypoCorrection &candidate);
00272 
00273   /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
00274   /// to a candidate (where a lower value represents a better candidate), or
00275   /// returning InvalidDistance if the candidate is not at all viable. For
00276   /// validation callbacks that only need to determine if a candidate is viable,
00277   /// the default RankCandidate returns either 0 or InvalidDistance depending
00278   /// whether ValidateCandidate returns true or false.
00279   virtual unsigned RankCandidate(const TypoCorrection &candidate) {
00280     return (!MatchesTypo(candidate) && ValidateCandidate(candidate))
00281                ? 0
00282                : InvalidDistance;
00283   }
00284 
00285   void setTypoName(IdentifierInfo *II) { Typo = II; }
00286   void setTypoNNS(NestedNameSpecifier *NNS) { TypoNNS = NNS; }
00287 
00288   // Flags for context-dependent keywords. WantFunctionLikeCasts is only
00289   // used/meaningful when WantCXXNamedCasts is false.
00290   // TODO: Expand these to apply to non-keywords or possibly remove them.
00291   bool WantTypeSpecifiers;
00292   bool WantExpressionKeywords;
00293   bool WantCXXNamedCasts;
00294   bool WantFunctionLikeCasts;
00295   bool WantRemainingKeywords;
00296   bool WantObjCSuper;
00297   // Temporary hack for the one case where a CorrectTypoContext enum is used
00298   // when looking up results.
00299   bool IsObjCIvarLookup;
00300   bool IsAddressOfOperand;
00301 
00302 protected:
00303   bool MatchesTypo(const TypoCorrection &candidate) {
00304     return Typo && candidate.isResolved() && !candidate.requiresImport() &&
00305            candidate.getCorrectionAsIdentifierInfo() == Typo &&
00306            // FIXME: This probably does not return true when both
00307            // NestedNameSpecifiers have the same textual representation.
00308            candidate.getCorrectionSpecifier() == TypoNNS;
00309   }
00310 
00311   IdentifierInfo *Typo;
00312   NestedNameSpecifier *TypoNNS;
00313 };
00314 
00315 /// @brief Simple template class for restricting typo correction candidates
00316 /// to ones having a single Decl* of the given type.
00317 template <class C>
00318 class DeclFilterCCC : public CorrectionCandidateCallback {
00319 public:
00320   bool ValidateCandidate(const TypoCorrection &candidate) override {
00321     return candidate.getCorrectionDeclAs<C>();
00322   }
00323 };
00324 
00325 // @brief Callback class to limit the allowed keywords and to only accept typo
00326 // corrections that are keywords or whose decls refer to functions (or template
00327 // functions) that accept the given number of arguments.
00328 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
00329 public:
00330   FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
00331                         bool HasExplicitTemplateArgs,
00332                         MemberExpr *ME = nullptr);
00333 
00334   bool ValidateCandidate(const TypoCorrection &candidate) override;
00335 
00336  private:
00337   unsigned NumArgs;
00338   bool HasExplicitTemplateArgs;
00339   DeclContext *CurContext;
00340   MemberExpr *MemberFn;
00341 };
00342 
00343 // @brief Callback class that effectively disabled typo correction
00344 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
00345 public:
00346   NoTypoCorrectionCCC() {
00347     WantTypeSpecifiers = false;
00348     WantExpressionKeywords = false;
00349     WantCXXNamedCasts = false;
00350     WantFunctionLikeCasts = false;
00351     WantRemainingKeywords = false;
00352   }
00353 
00354   bool ValidateCandidate(const TypoCorrection &candidate) override {
00355     return false;
00356   }
00357 };
00358 
00359 }
00360 
00361 #endif