Home | History | Annotate | Download | only in Sema
      1 //===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file defines the TypoCorrection class, which stores the results of
     11 // Sema's typo correction (Sema::CorrectTypo).
     12 //
     13 //===----------------------------------------------------------------------===//
     14 
     15 #ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
     16 #define LLVM_CLANG_SEMA_TYPOCORRECTION_H
     17 
     18 #include "clang/AST/DeclCXX.h"
     19 #include "clang/Sema/DeclSpec.h"
     20 #include "llvm/ADT/SmallVector.h"
     21 
     22 namespace clang {
     23 
     24 /// @brief Simple class containing the result of Sema::CorrectTypo
     25 class TypoCorrection {
     26 public:
     27   // "Distance" for unusable corrections
     28   static const unsigned InvalidDistance = ~0U;
     29   // The largest distance still considered valid (larger edit distances are
     30   // mapped to InvalidDistance by getEditDistance).
     31   static const unsigned MaximumDistance = 10000U;
     32 
     33   // Relative weightings of the "edit distance" components. The higher the
     34   // weight, the more of a penalty to fitness the component will give (higher
     35   // weights mean greater contribution to the total edit distance, with the
     36   // best correction candidates having the lowest edit distance).
     37   static const unsigned CharDistanceWeight = 100U;
     38   static const unsigned QualifierDistanceWeight = 110U;
     39   static const unsigned CallbackDistanceWeight = 150U;
     40 
     41   TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
     42                  NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0,
     43                  unsigned QualifierDistance = 0)
     44       : CorrectionName(Name), CorrectionNameSpec(NNS),
     45         CharDistance(CharDistance), QualifierDistance(QualifierDistance),
     46         CallbackDistance(0), ForceSpecifierReplacement(false) {
     47     if (NameDecl)
     48       CorrectionDecls.push_back(NameDecl);
     49   }
     50 
     51   TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
     52                  unsigned CharDistance = 0)
     53       : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
     54         CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
     55         ForceSpecifierReplacement(false) {
     56     if (Name)
     57       CorrectionDecls.push_back(Name);
     58   }
     59 
     60   TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
     61                  unsigned CharDistance = 0)
     62       : CorrectionName(Name), CorrectionNameSpec(NNS),
     63         CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
     64         ForceSpecifierReplacement(false) {}
     65 
     66   TypoCorrection()
     67       : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
     68         CallbackDistance(0), ForceSpecifierReplacement(false) {}
     69 
     70   /// \brief Gets the DeclarationName of the typo correction
     71   DeclarationName getCorrection() const { return CorrectionName; }
     72   IdentifierInfo* getCorrectionAsIdentifierInfo() const {
     73     return CorrectionName.getAsIdentifierInfo();
     74   }
     75 
     76   /// \brief Gets the NestedNameSpecifier needed to use the typo correction
     77   NestedNameSpecifier* getCorrectionSpecifier() const {
     78     return CorrectionNameSpec;
     79   }
     80   void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
     81     CorrectionNameSpec = NNS;
     82     ForceSpecifierReplacement = (NNS != 0);
     83   }
     84 
     85   void WillReplaceSpecifier(bool ForceReplacement) {
     86     ForceSpecifierReplacement = ForceReplacement;
     87   }
     88 
     89   bool WillReplaceSpecifier() const {
     90     return ForceSpecifierReplacement;
     91   }
     92 
     93   void setQualifierDistance(unsigned ED) {
     94     QualifierDistance = ED;
     95   }
     96 
     97   void setCallbackDistance(unsigned ED) {
     98     CallbackDistance = ED;
     99   }
    100 
    101   // Convert the given weighted edit distance to a roughly equivalent number of
    102   // single-character edits (typically for comparison to the length of the
    103   // string being edited).
    104   static unsigned NormalizeEditDistance(unsigned ED) {
    105     if (ED > MaximumDistance)
    106       return InvalidDistance;
    107     return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
    108   }
    109 
    110   /// \brief Gets the "edit distance" of the typo correction from the typo.
    111   /// If Normalized is true, scale the distance down by the CharDistanceWeight
    112   /// to return the edit distance in terms of single-character edits.
    113   unsigned getEditDistance(bool Normalized = true) const {
    114     if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
    115         CallbackDistance > MaximumDistance)
    116       return InvalidDistance;
    117     unsigned ED =
    118         CharDistance * CharDistanceWeight +
    119         QualifierDistance * QualifierDistanceWeight +
    120         CallbackDistance * CallbackDistanceWeight;
    121     if (ED > MaximumDistance)
    122       return InvalidDistance;
    123     // Half the CharDistanceWeight is added to ED to simulate rounding since
    124     // integer division truncates the value (i.e. round-to-nearest-int instead
    125     // of round-to-zero).
    126     return Normalized ? NormalizeEditDistance(ED) : ED;
    127   }
    128 
    129   /// \brief Gets the pointer to the declaration of the typo correction
    130   NamedDecl *getCorrectionDecl() const {
    131     return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
    132   }
    133   template <class DeclClass>
    134   DeclClass *getCorrectionDeclAs() const {
    135     return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
    136   }
    137 
    138   /// \brief Clears the list of NamedDecls before adding the new one.
    139   void setCorrectionDecl(NamedDecl *CDecl) {
    140     CorrectionDecls.clear();
    141     addCorrectionDecl(CDecl);
    142   }
    143 
    144   /// \brief Add the given NamedDecl to the list of NamedDecls that are the
    145   /// declarations associated with the DeclarationName of this TypoCorrection
    146   void addCorrectionDecl(NamedDecl *CDecl);
    147 
    148   std::string getAsString(const LangOptions &LO) const;
    149   std::string getQuoted(const LangOptions &LO) const {
    150     return "'" + getAsString(LO) + "'";
    151   }
    152 
    153   /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
    154   LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
    155 
    156   /// \brief Mark this TypoCorrection as being a keyword.
    157   /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
    158   /// added to the list of the correction's NamedDecl pointers, NULL is added
    159   /// as the only element in the list to mark this TypoCorrection as a keyword.
    160   void makeKeyword() {
    161     CorrectionDecls.clear();
    162     CorrectionDecls.push_back(0);
    163     ForceSpecifierReplacement = true;
    164   }
    165 
    166   // Check if this TypoCorrection is a keyword by checking if the first
    167   // item in CorrectionDecls is NULL.
    168   bool isKeyword() const {
    169     return !CorrectionDecls.empty() &&
    170         CorrectionDecls.front() == 0;
    171   }
    172 
    173   // Check if this TypoCorrection is the given keyword.
    174   template<std::size_t StrLen>
    175   bool isKeyword(const char (&Str)[StrLen]) const {
    176     return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
    177   }
    178 
    179   // Returns true if the correction either is a keyword or has a known decl.
    180   bool isResolved() const { return !CorrectionDecls.empty(); }
    181 
    182   bool isOverloaded() const {
    183     return CorrectionDecls.size() > 1;
    184   }
    185 
    186   void setCorrectionRange(CXXScopeSpec* SS,
    187                           const DeclarationNameInfo &TypoName) {
    188     CorrectionRange.setBegin(ForceSpecifierReplacement && SS
    189                                  ? SS->getBeginLoc()
    190                                  : TypoName.getLoc());
    191     CorrectionRange.setEnd(TypoName.getLoc());
    192   }
    193 
    194   SourceRange getCorrectionRange() const {
    195     return CorrectionRange;
    196   }
    197 
    198   typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
    199   decl_iterator begin() {
    200     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
    201   }
    202   decl_iterator end() { return CorrectionDecls.end(); }
    203   typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
    204   const_decl_iterator begin() const {
    205     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
    206   }
    207   const_decl_iterator end() const { return CorrectionDecls.end(); }
    208 
    209 private:
    210   bool hasCorrectionDecl() const {
    211     return (!isKeyword() && !CorrectionDecls.empty());
    212   }
    213 
    214   // Results.
    215   DeclarationName CorrectionName;
    216   NestedNameSpecifier *CorrectionNameSpec;
    217   SmallVector<NamedDecl *, 1> CorrectionDecls;
    218   unsigned CharDistance;
    219   unsigned QualifierDistance;
    220   unsigned CallbackDistance;
    221   SourceRange CorrectionRange;
    222   bool ForceSpecifierReplacement;
    223 };
    224 
    225 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
    226 /// the validity of a potential typo correction.
    227 class CorrectionCandidateCallback {
    228 public:
    229   static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
    230 
    231   CorrectionCandidateCallback()
    232       : WantTypeSpecifiers(true), WantExpressionKeywords(true),
    233         WantCXXNamedCasts(true), WantRemainingKeywords(true),
    234         WantObjCSuper(false),
    235         IsObjCIvarLookup(false) {}
    236 
    237   virtual ~CorrectionCandidateCallback() {}
    238 
    239   /// \brief Simple predicate used by the default RankCandidate to
    240   /// determine whether to return an edit distance of 0 or InvalidDistance.
    241   /// This can be overrided by validators that only need to determine if a
    242   /// candidate is viable, without ranking potentially viable candidates.
    243   /// Only ValidateCandidate or RankCandidate need to be overriden by a
    244   /// callback wishing to check the viability of correction candidates.
    245   /// The default predicate always returns true if the candidate is not a type
    246   /// name or keyword, true for types if WantTypeSpecifiers is true, and true
    247   /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
    248   /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
    249   virtual bool ValidateCandidate(const TypoCorrection &candidate);
    250 
    251   /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
    252   /// to a candidate (where a lower value represents a better candidate), or
    253   /// returning InvalidDistance if the candidate is not at all viable. For
    254   /// validation callbacks that only need to determine if a candidate is viable,
    255   /// the default RankCandidate returns either 0 or InvalidDistance depending
    256   /// whether ValidateCandidate returns true or false.
    257   virtual unsigned RankCandidate(const TypoCorrection &candidate) {
    258     return ValidateCandidate(candidate) ? 0 : InvalidDistance;
    259   }
    260 
    261   // Flags for context-dependent keywords.
    262   // TODO: Expand these to apply to non-keywords or possibly remove them.
    263   bool WantTypeSpecifiers;
    264   bool WantExpressionKeywords;
    265   bool WantCXXNamedCasts;
    266   bool WantRemainingKeywords;
    267   bool WantObjCSuper;
    268   // Temporary hack for the one case where a CorrectTypoContext enum is used
    269   // when looking up results.
    270   bool IsObjCIvarLookup;
    271 };
    272 
    273 /// @brief Simple template class for restricting typo correction candidates
    274 /// to ones having a single Decl* of the given type.
    275 template <class C>
    276 class DeclFilterCCC : public CorrectionCandidateCallback {
    277 public:
    278   virtual bool ValidateCandidate(const TypoCorrection &candidate) {
    279     return candidate.getCorrectionDeclAs<C>();
    280   }
    281 };
    282 
    283 // @brief Callback class to limit the allowed keywords and to only accept typo
    284 // corrections that are keywords or whose decls refer to functions (or template
    285 // functions) that accept the given number of arguments.
    286 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
    287 public:
    288   FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
    289                         bool HasExplicitTemplateArgs);
    290 
    291   virtual bool ValidateCandidate(const TypoCorrection &candidate);
    292 
    293  private:
    294   unsigned NumArgs;
    295   bool HasExplicitTemplateArgs;
    296 };
    297 
    298 // @brief Callback class that effectively disabled typo correction
    299 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
    300 public:
    301   NoTypoCorrectionCCC() {
    302     WantTypeSpecifiers = false;
    303     WantExpressionKeywords = false;
    304     WantCXXNamedCasts = false;
    305     WantRemainingKeywords = false;
    306   }
    307 
    308   virtual bool ValidateCandidate(const TypoCorrection &candidate) {
    309     return false;
    310   }
    311 };
    312 
    313 }
    314 
    315 #endif
    316