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 "clang/Sema/DeclSpec.h"
20 #include "llvm/ADT/SmallVector.h"
24 /// @brief Simple class containing the result of Sema::CorrectTypo
25 class TypoCorrection {
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;
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;
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),
48 CorrectionDecls.push_back(NameDecl);
51 TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=0,
52 unsigned CharDistance=0)
53 : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
54 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {
56 CorrectionDecls.push_back(Name);
59 TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=0,
60 unsigned CharDistance=0)
61 : CorrectionName(Name), CorrectionNameSpec(NNS),
62 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {}
65 : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
66 CallbackDistance(0) {}
68 /// \brief Gets the DeclarationName of the typo correction
69 DeclarationName getCorrection() const { return CorrectionName; }
70 IdentifierInfo* getCorrectionAsIdentifierInfo() const {
71 return CorrectionName.getAsIdentifierInfo();
74 /// \brief Gets the NestedNameSpecifier needed to use the typo correction
75 NestedNameSpecifier* getCorrectionSpecifier() const {
76 return CorrectionNameSpec;
78 void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
79 CorrectionNameSpec = NNS;
82 void setQualifierDistance(unsigned ED) {
83 QualifierDistance = ED;
86 void setCallbackDistance(unsigned ED) {
87 CallbackDistance = ED;
90 // Convert the given weighted edit distance to a roughly equivalent number of
91 // single-character edits (typically for comparison to the length of the
92 // string being edited).
93 static unsigned NormalizeEditDistance(unsigned ED) {
94 if (ED > MaximumDistance)
95 return InvalidDistance;
96 return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
99 /// \brief Gets the "edit distance" of the typo correction from the typo.
100 /// If Normalized is true, scale the distance down by the CharDistanceWeight
101 /// to return the edit distance in terms of single-character edits.
102 unsigned getEditDistance(bool Normalized = true) const {
103 if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
104 CallbackDistance > MaximumDistance)
105 return InvalidDistance;
107 CharDistance * CharDistanceWeight +
108 QualifierDistance * QualifierDistanceWeight +
109 CallbackDistance * CallbackDistanceWeight;
110 if (ED > MaximumDistance)
111 return InvalidDistance;
112 // Half the CharDistanceWeight is added to ED to simulate rounding since
113 // integer division truncates the value (i.e. round-to-nearest-int instead
114 // of round-to-zero).
115 return Normalized ? NormalizeEditDistance(ED) : ED;
118 /// \brief Gets the pointer to the declaration of the typo correction
119 NamedDecl* getCorrectionDecl() const {
120 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
122 template <class DeclClass>
123 DeclClass *getCorrectionDeclAs() const {
124 return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
127 /// \brief Clears the list of NamedDecls before adding the new one.
128 void setCorrectionDecl(NamedDecl *CDecl) {
129 CorrectionDecls.clear();
130 addCorrectionDecl(CDecl);
133 /// \brief Add the given NamedDecl to the list of NamedDecls that are the
134 /// declarations associated with the DeclarationName of this TypoCorrection
135 void addCorrectionDecl(NamedDecl *CDecl);
137 std::string getAsString(const LangOptions &LO) const;
138 std::string getQuoted(const LangOptions &LO) const {
139 return "'" + getAsString(LO) + "'";
142 /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
143 operator bool() const { return bool(CorrectionName); }
145 /// \brief Mark this TypoCorrection as being a keyword.
146 /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
147 /// added to the list of the correction's NamedDecl pointers, NULL is added
148 /// as the only element in the list to mark this TypoCorrection as a keyword.
150 CorrectionDecls.clear();
151 CorrectionDecls.push_back(0);
154 // Check if this TypoCorrection is a keyword by checking if the first
155 // item in CorrectionDecls is NULL.
156 bool isKeyword() const {
157 return !CorrectionDecls.empty() &&
158 CorrectionDecls.front() == 0;
161 // Check if this TypoCorrection is the given keyword.
162 template<std::size_t StrLen>
163 bool isKeyword(const char (&Str)[StrLen]) const {
164 return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
167 // Returns true if the correction either is a keyword or has a known decl.
168 bool isResolved() const { return !CorrectionDecls.empty(); }
170 bool isOverloaded() const {
171 return CorrectionDecls.size() > 1;
174 void setCorrectionRange(CXXScopeSpec* SS,
175 const DeclarationNameInfo &TypoName) {
176 CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc()
177 : TypoName.getLoc());
178 CorrectionRange.setEnd(TypoName.getLoc());
181 SourceRange getCorrectionRange() const {
182 return CorrectionRange;
185 typedef SmallVector<NamedDecl *, 1>::iterator decl_iterator;
186 decl_iterator begin() {
187 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
189 decl_iterator end() { return CorrectionDecls.end(); }
190 typedef SmallVector<NamedDecl *, 1>::const_iterator const_decl_iterator;
191 const_decl_iterator begin() const {
192 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
194 const_decl_iterator end() const { return CorrectionDecls.end(); }
197 bool hasCorrectionDecl() const {
198 return (!isKeyword() && !CorrectionDecls.empty());
202 DeclarationName CorrectionName;
203 NestedNameSpecifier *CorrectionNameSpec;
204 SmallVector<NamedDecl *, 1> CorrectionDecls;
205 unsigned CharDistance;
206 unsigned QualifierDistance;
207 unsigned CallbackDistance;
208 SourceRange CorrectionRange;
211 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
212 /// the validity of a potential typo correction.
213 class CorrectionCandidateCallback {
215 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
217 CorrectionCandidateCallback()
218 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
219 WantCXXNamedCasts(true), WantRemainingKeywords(true),
220 WantObjCSuper(false),
221 IsObjCIvarLookup(false) {}
223 virtual ~CorrectionCandidateCallback() {}
225 /// \brief Simple predicate used by the default RankCandidate to
226 /// determine whether to return an edit distance of 0 or InvalidDistance.
227 /// This can be overrided by validators that only need to determine if a
228 /// candidate is viable, without ranking potentially viable candidates.
229 /// Only ValidateCandidate or RankCandidate need to be overriden by a
230 /// callback wishing to check the viability of correction candidates.
231 /// The default predicate always returns true if the candidate is not a type
232 /// name or keyword, true for types if WantTypeSpecifiers is true, and true
233 /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
234 /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
235 virtual bool ValidateCandidate(const TypoCorrection &candidate);
237 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
238 /// to a candidate (where a lower value represents a better candidate), or
239 /// returning InvalidDistance if the candidate is not at all viable. For
240 /// validation callbacks that only need to determine if a candidate is viable,
241 /// the default RankCandidate returns either 0 or InvalidDistance depending
242 /// whether ValidateCandidate returns true or false.
243 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
244 return ValidateCandidate(candidate) ? 0 : InvalidDistance;
247 // Flags for context-dependent keywords.
248 // TODO: Expand these to apply to non-keywords or possibly remove them.
249 bool WantTypeSpecifiers;
250 bool WantExpressionKeywords;
251 bool WantCXXNamedCasts;
252 bool WantRemainingKeywords;
254 // Temporary hack for the one case where a CorrectTypoContext enum is used
255 // when looking up results.
256 bool IsObjCIvarLookup;
259 /// @brief Simple template class for restricting typo correction candidates
260 /// to ones having a single Decl* of the given type.
262 class DeclFilterCCC : public CorrectionCandidateCallback {
264 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
265 return candidate.getCorrectionDeclAs<C>();