1 //===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines the TypoCorrection class, which stores the results of
11 // Sema's typo correction (Sema::CorrectTypo).
13 //===----------------------------------------------------------------------===//
15 #ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
16 #define LLVM_CLANG_SEMA_TYPOCORRECTION_H
18 #include "clang/AST/DeclCXX.h"
19 #include "llvm/ADT/SmallVector.h"
23 /// @brief Simple class containing the result of Sema::CorrectTypo
24 class TypoCorrection {
26 // "Distance" for unusable corrections
27 static const unsigned InvalidDistance = ~0U;
28 // The largest distance still considered valid (larger edit distances are
29 // mapped to InvalidDistance by getEditDistance).
30 static const unsigned MaximumDistance = 10000U;
32 // Relative weightings of the "edit distance" components. The higher the
33 // weight, the more of a penalty to fitness the component will give (higher
34 // weights mean greater contribution to the total edit distance, with the
35 // best correction candidates having the lowest edit distance).
36 static const unsigned CharDistanceWeight = 100U;
37 static const unsigned QualifierDistanceWeight = 110U;
38 static const unsigned CallbackDistanceWeight = 150U;
40 TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
41 NestedNameSpecifier *NNS=0, unsigned CharDistance=0,
42 unsigned QualifierDistance=0)
43 : CorrectionName(Name), CorrectionNameSpec(NNS),
44 CharDistance(CharDistance), QualifierDistance(QualifierDistance),
47 CorrectionDecls.push_back(NameDecl);
50 TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=0,
51 unsigned CharDistance=0)
52 : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
53 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {
55 CorrectionDecls.push_back(Name);
58 TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=0,
59 unsigned CharDistance=0)
60 : CorrectionName(Name), CorrectionNameSpec(NNS),
61 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {}
64 : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
65 CallbackDistance(0) {}
67 /// \brief Gets the DeclarationName of the typo correction
68 DeclarationName getCorrection() const { return CorrectionName; }
69 IdentifierInfo* getCorrectionAsIdentifierInfo() const {
70 return CorrectionName.getAsIdentifierInfo();
73 /// \brief Gets the NestedNameSpecifier needed to use the typo correction
74 NestedNameSpecifier* getCorrectionSpecifier() const {
75 return CorrectionNameSpec;
77 void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
78 CorrectionNameSpec = NNS;
81 void setQualifierDistance(unsigned ED) {
82 QualifierDistance = ED;
85 void setCallbackDistance(unsigned ED) {
86 CallbackDistance = ED;
89 // Convert the given weighted edit distance to a roughly equivalent number of
90 // single-character edits (typically for comparison to the length of the
91 // string being edited).
92 static unsigned NormalizeEditDistance(unsigned ED) {
93 if (ED > MaximumDistance)
94 return InvalidDistance;
95 return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
98 /// \brief Gets the "edit distance" of the typo correction from the typo.
99 /// If Normalized is true, scale the distance down by the CharDistanceWeight
100 /// to return the edit distance in terms of single-character edits.
101 unsigned getEditDistance(bool Normalized = true) const {
102 if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
103 CallbackDistance > MaximumDistance)
104 return InvalidDistance;
106 CharDistance * CharDistanceWeight +
107 QualifierDistance * QualifierDistanceWeight +
108 CallbackDistance * CallbackDistanceWeight;
109 if (ED > MaximumDistance)
110 return InvalidDistance;
111 // Half the CharDistanceWeight is added to ED to simulate rounding since
112 // integer division truncates the value (i.e. round-to-nearest-int instead
113 // of round-to-zero).
114 return Normalized ? NormalizeEditDistance(ED) : ED;
117 /// \brief Gets the pointer to the declaration of the typo correction
118 NamedDecl* getCorrectionDecl() const {
119 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
121 template <class DeclClass>
122 DeclClass *getCorrectionDeclAs() const {
123 return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
126 /// \brief Clears the list of NamedDecls before adding the new one.
127 void setCorrectionDecl(NamedDecl *CDecl) {
128 CorrectionDecls.clear();
129 addCorrectionDecl(CDecl);
132 /// \brief Add the given NamedDecl to the list of NamedDecls that are the
133 /// declarations associated with the DeclarationName of this TypoCorrection
134 void addCorrectionDecl(NamedDecl *CDecl);
136 std::string getAsString(const LangOptions &LO) const;
137 std::string getQuoted(const LangOptions &LO) const {
138 return "'" + getAsString(LO) + "'";
141 /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
142 operator bool() const { return bool(CorrectionName); }
144 /// \brief Mark this TypoCorrection as being a keyword.
145 /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
146 /// added to the list of the correction's NamedDecl pointers, NULL is added
147 /// as the only element in the list to mark this TypoCorrection as a keyword.
149 CorrectionDecls.clear();
150 CorrectionDecls.push_back(0);
153 // Check if this TypoCorrection is a keyword by checking if the first
154 // item in CorrectionDecls is NULL.
155 bool isKeyword() const {
156 return !CorrectionDecls.empty() &&
157 CorrectionDecls.front() == 0;
160 // Check if this TypoCorrection is the given keyword.
161 template<std::size_t StrLen>
162 bool isKeyword(const char (&Str)[StrLen]) const {
163 return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
166 // Returns true if the correction either is a keyword or has a known decl.
167 bool isResolved() const { return !CorrectionDecls.empty(); }
169 bool isOverloaded() const {
170 return CorrectionDecls.size() > 1;
173 typedef llvm::SmallVector<NamedDecl*, 1>::iterator decl_iterator;
174 decl_iterator begin() {
175 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
177 decl_iterator end() { return CorrectionDecls.end(); }
178 typedef llvm::SmallVector<NamedDecl*, 1>::const_iterator const_decl_iterator;
179 const_decl_iterator begin() const {
180 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
182 const_decl_iterator end() const { return CorrectionDecls.end(); }
185 bool hasCorrectionDecl() const {
186 return (!isKeyword() && !CorrectionDecls.empty());
190 DeclarationName CorrectionName;
191 NestedNameSpecifier *CorrectionNameSpec;
192 llvm::SmallVector<NamedDecl*, 1> CorrectionDecls;
193 unsigned CharDistance;
194 unsigned QualifierDistance;
195 unsigned CallbackDistance;
198 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
199 /// the validity of a potential typo correction.
200 class CorrectionCandidateCallback {
202 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
204 CorrectionCandidateCallback()
205 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
206 WantCXXNamedCasts(true), WantRemainingKeywords(true),
207 WantObjCSuper(false),
208 IsObjCIvarLookup(false) {}
210 virtual ~CorrectionCandidateCallback() {}
212 /// \brief Simple predicate used by the default RankCandidate to
213 /// determine whether to return an edit distance of 0 or InvalidDistance.
214 /// This can be overrided by validators that only need to determine if a
215 /// candidate is viable, without ranking potentially viable candidates.
216 /// Only ValidateCandidate or RankCandidate need to be overriden by a
217 /// callback wishing to check the viability of correction candidates.
218 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
222 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
223 /// to a candidate (where a lower value represents a better candidate), or
224 /// returning InvalidDistance if the candidate is not at all viable. For
225 /// validation callbacks that only need to determine if a candidate is viable,
226 /// the default RankCandidate returns either 0 or InvalidDistance depending
227 /// whether ValidateCandidate returns true or false.
228 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
229 return ValidateCandidate(candidate) ? 0 : InvalidDistance;
232 // Flags for context-dependent keywords.
233 // TODO: Expand these to apply to non-keywords or possibly remove them.
234 bool WantTypeSpecifiers;
235 bool WantExpressionKeywords;
236 bool WantCXXNamedCasts;
237 bool WantRemainingKeywords;
239 // Temporary hack for the one case where a CorrectTypoContext enum is used
240 // when looking up results.
241 bool IsObjCIvarLookup;
244 /// @brief Simple template class for restricting typo correction candidates
245 /// to ones having a single Decl* of the given type.
247 class DeclFilterCCC : public CorrectionCandidateCallback {
249 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
250 return candidate.getCorrectionDeclAs<C>();