Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:37:05

0001 //===- TypoCorrection.h - Class for typo correction results -----*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // This file defines the TypoCorrection class, which stores the results of
0010 // Sema's typo correction (Sema::CorrectTypo).
0011 //
0012 //===----------------------------------------------------------------------===//
0013 
0014 #ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
0015 #define LLVM_CLANG_SEMA_TYPOCORRECTION_H
0016 
0017 #include "clang/AST/Decl.h"
0018 #include "clang/AST/DeclarationName.h"
0019 #include "clang/Basic/LLVM.h"
0020 #include "clang/Basic/PartialDiagnostic.h"
0021 #include "clang/Basic/SourceLocation.h"
0022 #include "clang/Sema/DeclSpec.h"
0023 #include "llvm/ADT/ArrayRef.h"
0024 #include "llvm/ADT/SmallVector.h"
0025 #include "llvm/Support/Casting.h"
0026 #include <cstddef>
0027 #include <limits>
0028 #include <string>
0029 #include <utility>
0030 #include <vector>
0031 
0032 namespace clang {
0033 
0034 class DeclContext;
0035 class IdentifierInfo;
0036 class LangOptions;
0037 class MemberExpr;
0038 class NestedNameSpecifier;
0039 class Sema;
0040 
0041 /// Simple class containing the result of Sema::CorrectTypo
0042 class TypoCorrection {
0043 public:
0044   // "Distance" for unusable corrections
0045   static const unsigned InvalidDistance = std::numeric_limits<unsigned>::max();
0046 
0047   // The largest distance still considered valid (larger edit distances are
0048   // mapped to InvalidDistance by getEditDistance).
0049   static const unsigned MaximumDistance = 10000U;
0050 
0051   // Relative weightings of the "edit distance" components. The higher the
0052   // weight, the more of a penalty to fitness the component will give (higher
0053   // weights mean greater contribution to the total edit distance, with the
0054   // best correction candidates having the lowest edit distance).
0055   static const unsigned CharDistanceWeight = 100U;
0056   static const unsigned QualifierDistanceWeight = 110U;
0057   static const unsigned CallbackDistanceWeight = 150U;
0058 
0059   TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
0060                  NestedNameSpecifier *NNS = nullptr, unsigned CharDistance = 0,
0061                  unsigned QualifierDistance = 0)
0062       : CorrectionName(Name), CorrectionNameSpec(NNS),
0063         CharDistance(CharDistance), QualifierDistance(QualifierDistance) {
0064     if (NameDecl)
0065       CorrectionDecls.push_back(NameDecl);
0066   }
0067 
0068   TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = nullptr,
0069                  unsigned CharDistance = 0)
0070       : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
0071         CharDistance(CharDistance) {
0072     if (Name)
0073       CorrectionDecls.push_back(Name);
0074   }
0075 
0076   TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = nullptr,
0077                  unsigned CharDistance = 0)
0078       : CorrectionName(Name), CorrectionNameSpec(NNS),
0079         CharDistance(CharDistance) {}
0080 
0081   TypoCorrection() = default;
0082 
0083   /// Gets the DeclarationName of the typo correction
0084   DeclarationName getCorrection() const { return CorrectionName; }
0085 
0086   IdentifierInfo *getCorrectionAsIdentifierInfo() const {
0087     return CorrectionName.getAsIdentifierInfo();
0088   }
0089 
0090   /// Gets the NestedNameSpecifier needed to use the typo correction
0091   NestedNameSpecifier *getCorrectionSpecifier() const {
0092     return CorrectionNameSpec;
0093   }
0094 
0095   void setCorrectionSpecifier(NestedNameSpecifier *NNS) {
0096     CorrectionNameSpec = NNS;
0097     ForceSpecifierReplacement = (NNS != nullptr);
0098   }
0099 
0100   void WillReplaceSpecifier(bool ForceReplacement) {
0101     ForceSpecifierReplacement = ForceReplacement;
0102   }
0103 
0104   bool WillReplaceSpecifier() const {
0105     return ForceSpecifierReplacement;
0106   }
0107 
0108   void setQualifierDistance(unsigned ED) {
0109     QualifierDistance = ED;
0110   }
0111 
0112   void setCallbackDistance(unsigned ED) {
0113     CallbackDistance = ED;
0114   }
0115 
0116   // Convert the given weighted edit distance to a roughly equivalent number of
0117   // single-character edits (typically for comparison to the length of the
0118   // string being edited).
0119   static unsigned NormalizeEditDistance(unsigned ED) {
0120     if (ED > MaximumDistance)
0121       return InvalidDistance;
0122     return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
0123   }
0124 
0125   /// Gets the "edit distance" of the typo correction from the typo.
0126   /// If Normalized is true, scale the distance down by the CharDistanceWeight
0127   /// to return the edit distance in terms of single-character edits.
0128   unsigned getEditDistance(bool Normalized = true) const {
0129     if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
0130         CallbackDistance > MaximumDistance)
0131       return InvalidDistance;
0132     unsigned ED =
0133         CharDistance * CharDistanceWeight +
0134         QualifierDistance * QualifierDistanceWeight +
0135         CallbackDistance * CallbackDistanceWeight;
0136     if (ED > MaximumDistance)
0137       return InvalidDistance;
0138     // Half the CharDistanceWeight is added to ED to simulate rounding since
0139     // integer division truncates the value (i.e. round-to-nearest-int instead
0140     // of round-to-zero).
0141     return Normalized ? NormalizeEditDistance(ED) : ED;
0142   }
0143 
0144   /// Get the correction declaration found by name lookup (before we
0145   /// looked through using shadow declarations and the like).
0146   NamedDecl *getFoundDecl() const {
0147     return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : nullptr;
0148   }
0149 
0150   /// Gets the pointer to the declaration of the typo correction
0151   NamedDecl *getCorrectionDecl() const {
0152     auto *D = getFoundDecl();
0153     return D ? D->getUnderlyingDecl() : nullptr;
0154   }
0155   template <class DeclClass>
0156   DeclClass *getCorrectionDeclAs() const {
0157     return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
0158   }
0159 
0160   /// Clears the list of NamedDecls.
0161   void ClearCorrectionDecls() {
0162     CorrectionDecls.clear();
0163   }
0164 
0165   /// Clears the list of NamedDecls before adding the new one.
0166   void setCorrectionDecl(NamedDecl *CDecl) {
0167     CorrectionDecls.clear();
0168     addCorrectionDecl(CDecl);
0169   }
0170 
0171   /// Clears the list of NamedDecls and adds the given set.
0172   void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
0173     CorrectionDecls.clear();
0174     CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
0175   }
0176 
0177   /// Add the given NamedDecl to the list of NamedDecls that are the
0178   /// declarations associated with the DeclarationName of this TypoCorrection
0179   void addCorrectionDecl(NamedDecl *CDecl);
0180 
0181   std::string getAsString(const LangOptions &LO) const;
0182 
0183   std::string getQuoted(const LangOptions &LO) const {
0184     return "'" + getAsString(LO) + "'";
0185   }
0186 
0187   /// Returns whether this TypoCorrection has a non-empty DeclarationName
0188   explicit operator bool() const { return bool(CorrectionName); }
0189 
0190   /// Mark this TypoCorrection as being a keyword.
0191   /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
0192   /// added to the list of the correction's NamedDecl pointers, NULL is added
0193   /// as the only element in the list to mark this TypoCorrection as a keyword.
0194   void makeKeyword() {
0195     CorrectionDecls.clear();
0196     CorrectionDecls.push_back(nullptr);
0197     ForceSpecifierReplacement = true;
0198   }
0199 
0200   // Check if this TypoCorrection is a keyword by checking if the first
0201   // item in CorrectionDecls is NULL.
0202   bool isKeyword() const {
0203     return !CorrectionDecls.empty() && CorrectionDecls.front() == nullptr;
0204   }
0205 
0206   // Check if this TypoCorrection is the given keyword.
0207   template<std::size_t StrLen>
0208   bool isKeyword(const char (&Str)[StrLen]) const {
0209     return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
0210   }
0211 
0212   // Returns true if the correction either is a keyword or has a known decl.
0213   bool isResolved() const { return !CorrectionDecls.empty(); }
0214 
0215   bool isOverloaded() const {
0216     return CorrectionDecls.size() > 1;
0217   }
0218 
0219   void setCorrectionRange(CXXScopeSpec *SS,
0220                           const DeclarationNameInfo &TypoName) {
0221     CorrectionRange = TypoName.getSourceRange();
0222     if (ForceSpecifierReplacement && SS && !SS->isEmpty())
0223       CorrectionRange.setBegin(SS->getBeginLoc());
0224   }
0225 
0226   SourceRange getCorrectionRange() const {
0227     return CorrectionRange;
0228   }
0229 
0230   using decl_iterator = SmallVectorImpl<NamedDecl *>::iterator;
0231 
0232   decl_iterator begin() {
0233     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
0234   }
0235 
0236   decl_iterator end() { return CorrectionDecls.end(); }
0237 
0238   using const_decl_iterator = SmallVectorImpl<NamedDecl *>::const_iterator;
0239 
0240   const_decl_iterator begin() const {
0241     return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
0242   }
0243 
0244   const_decl_iterator end() const { return CorrectionDecls.end(); }
0245 
0246   /// Returns whether this typo correction is correcting to a
0247   /// declaration that was declared in a module that has not been imported.
0248   bool requiresImport() const { return RequiresImport; }
0249   void setRequiresImport(bool Req) { RequiresImport = Req; }
0250 
0251   /// Extra diagnostics are printed after the first diagnostic for the typo.
0252   /// This can be used to attach external notes to the diag.
0253   void addExtraDiagnostic(PartialDiagnostic PD) {
0254     ExtraDiagnostics.push_back(std::move(PD));
0255   }
0256   ArrayRef<PartialDiagnostic> getExtraDiagnostics() const {
0257     return ExtraDiagnostics;
0258   }
0259 
0260 private:
0261   bool hasCorrectionDecl() const {
0262     return (!isKeyword() && !CorrectionDecls.empty());
0263   }
0264 
0265   // Results.
0266   DeclarationName CorrectionName;
0267   NestedNameSpecifier *CorrectionNameSpec = nullptr;
0268   SmallVector<NamedDecl *, 1> CorrectionDecls;
0269   unsigned CharDistance = 0;
0270   unsigned QualifierDistance = 0;
0271   unsigned CallbackDistance = 0;
0272   SourceRange CorrectionRange;
0273   bool ForceSpecifierReplacement = false;
0274   bool RequiresImport = false;
0275 
0276   std::vector<PartialDiagnostic> ExtraDiagnostics;
0277 };
0278 
0279 /// Base class for callback objects used by Sema::CorrectTypo to check
0280 /// the validity of a potential typo correction.
0281 class CorrectionCandidateCallback {
0282 public:
0283   static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
0284 
0285   explicit CorrectionCandidateCallback(const IdentifierInfo *Typo = nullptr,
0286                                        NestedNameSpecifier *TypoNNS = nullptr)
0287       : Typo(Typo), TypoNNS(TypoNNS) {}
0288 
0289   virtual ~CorrectionCandidateCallback() = default;
0290 
0291   /// Simple predicate used by the default RankCandidate to
0292   /// determine whether to return an edit distance of 0 or InvalidDistance.
0293   /// This can be overridden by validators that only need to determine if a
0294   /// candidate is viable, without ranking potentially viable candidates.
0295   /// Only ValidateCandidate or RankCandidate need to be overridden by a
0296   /// callback wishing to check the viability of correction candidates.
0297   /// The default predicate always returns true if the candidate is not a type
0298   /// name or keyword, true for types if WantTypeSpecifiers is true, and true
0299   /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
0300   /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
0301   virtual bool ValidateCandidate(const TypoCorrection &candidate);
0302 
0303   /// Method used by Sema::CorrectTypo to assign an "edit distance" rank
0304   /// to a candidate (where a lower value represents a better candidate), or
0305   /// returning InvalidDistance if the candidate is not at all viable. For
0306   /// validation callbacks that only need to determine if a candidate is viable,
0307   /// the default RankCandidate returns either 0 or InvalidDistance depending
0308   /// whether ValidateCandidate returns true or false.
0309   virtual unsigned RankCandidate(const TypoCorrection &candidate) {
0310     return (!MatchesTypo(candidate) && ValidateCandidate(candidate))
0311                ? 0
0312                : InvalidDistance;
0313   }
0314 
0315   /// Clone this CorrectionCandidateCallback. CorrectionCandidateCallbacks are
0316   /// initially stack-allocated. However in case where delayed typo-correction
0317   /// is done we need to move the callback to storage with a longer lifetime.
0318   /// Every class deriving from CorrectionCandidateCallback must implement
0319   /// this method.
0320   virtual std::unique_ptr<CorrectionCandidateCallback> clone() = 0;
0321 
0322   void setTypoName(const IdentifierInfo *II) { Typo = II; }
0323   void setTypoNNS(NestedNameSpecifier *NNS) { TypoNNS = NNS; }
0324 
0325   // Flags for context-dependent keywords. WantFunctionLikeCasts is only
0326   // used/meaningful when WantCXXNamedCasts is false.
0327   // TODO: Expand these to apply to non-keywords or possibly remove them.
0328   bool WantTypeSpecifiers = true;
0329   bool WantExpressionKeywords = true;
0330   bool WantCXXNamedCasts = true;
0331   bool WantFunctionLikeCasts = true;
0332   bool WantRemainingKeywords = true;
0333   bool WantObjCSuper = false;
0334   // Temporary hack for the one case where a CorrectTypoContext enum is used
0335   // when looking up results.
0336   bool IsObjCIvarLookup = false;
0337   bool IsAddressOfOperand = false;
0338 
0339 protected:
0340   bool MatchesTypo(const TypoCorrection &candidate) {
0341     return Typo && candidate.isResolved() && !candidate.requiresImport() &&
0342            candidate.getCorrectionAsIdentifierInfo() == Typo &&
0343            // FIXME: This probably does not return true when both
0344            // NestedNameSpecifiers have the same textual representation.
0345            candidate.getCorrectionSpecifier() == TypoNNS;
0346   }
0347 
0348   const IdentifierInfo *Typo;
0349   NestedNameSpecifier *TypoNNS;
0350 };
0351 
0352 class DefaultFilterCCC final : public CorrectionCandidateCallback {
0353 public:
0354   explicit DefaultFilterCCC(const IdentifierInfo *Typo = nullptr,
0355                             NestedNameSpecifier *TypoNNS = nullptr)
0356       : CorrectionCandidateCallback(Typo, TypoNNS) {}
0357 
0358   std::unique_ptr<CorrectionCandidateCallback> clone() override {
0359     return std::make_unique<DefaultFilterCCC>(*this);
0360   }
0361 };
0362 
0363 /// Simple template class for restricting typo correction candidates
0364 /// to ones having a single Decl* of the given type.
0365 template <class C>
0366 class DeclFilterCCC final : public CorrectionCandidateCallback {
0367 public:
0368   explicit DeclFilterCCC(const IdentifierInfo *Typo = nullptr,
0369                          NestedNameSpecifier *TypoNNS = nullptr)
0370       : CorrectionCandidateCallback(Typo, TypoNNS) {}
0371 
0372   bool ValidateCandidate(const TypoCorrection &candidate) override {
0373     return candidate.getCorrectionDeclAs<C>();
0374   }
0375   std::unique_ptr<CorrectionCandidateCallback> clone() override {
0376     return std::make_unique<DeclFilterCCC>(*this);
0377   }
0378 };
0379 
0380 // Callback class to limit the allowed keywords and to only accept typo
0381 // corrections that are keywords or whose decls refer to functions (or template
0382 // functions) that accept the given number of arguments.
0383 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
0384 public:
0385   FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
0386                         bool HasExplicitTemplateArgs,
0387                         MemberExpr *ME = nullptr);
0388 
0389   bool ValidateCandidate(const TypoCorrection &candidate) override;
0390   std::unique_ptr<CorrectionCandidateCallback> clone() override {
0391     return std::make_unique<FunctionCallFilterCCC>(*this);
0392   }
0393 
0394 private:
0395   unsigned NumArgs;
0396   bool HasExplicitTemplateArgs;
0397   DeclContext *CurContext;
0398   MemberExpr *MemberFn;
0399 };
0400 
0401 // Callback class that effectively disabled typo correction
0402 class NoTypoCorrectionCCC final : public CorrectionCandidateCallback {
0403 public:
0404   NoTypoCorrectionCCC() {
0405     WantTypeSpecifiers = false;
0406     WantExpressionKeywords = false;
0407     WantCXXNamedCasts = false;
0408     WantFunctionLikeCasts = false;
0409     WantRemainingKeywords = false;
0410   }
0411 
0412   bool ValidateCandidate(const TypoCorrection &candidate) override {
0413     return false;
0414   }
0415   std::unique_ptr<CorrectionCandidateCallback> clone() override {
0416     return std::make_unique<NoTypoCorrectionCCC>(*this);
0417   }
0418 };
0419 
0420 } // namespace clang
0421 
0422 #endif // LLVM_CLANG_SEMA_TYPOCORRECTION_H