clang API Documentation
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