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 void setCorrectionRange(CXXScopeSpec* SS,
174 const DeclarationNameInfo &TypoName) {
175 CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc()
176 : TypoName.getLoc());
177 CorrectionRange.setEnd(TypoName.getLoc());
180 SourceRange getCorrectionRange() const {
181 return CorrectionRange;
184 typedef llvm::SmallVector<NamedDecl*, 1>::iterator decl_iterator;
185 decl_iterator begin() {
186 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
188 decl_iterator end() { return CorrectionDecls.end(); }
189 typedef llvm::SmallVector<NamedDecl*, 1>::const_iterator const_decl_iterator;
190 const_decl_iterator begin() const {
191 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
193 const_decl_iterator end() const { return CorrectionDecls.end(); }
196 bool hasCorrectionDecl() const {
197 return (!isKeyword() && !CorrectionDecls.empty());
201 DeclarationName CorrectionName;
202 NestedNameSpecifier *CorrectionNameSpec;
203 llvm::SmallVector<NamedDecl*, 1> CorrectionDecls;
204 unsigned CharDistance;
205 unsigned QualifierDistance;
206 unsigned CallbackDistance;
207 SourceRange CorrectionRange;
210 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
211 /// the validity of a potential typo correction.
212 class CorrectionCandidateCallback {
214 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
216 CorrectionCandidateCallback()
217 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
218 WantCXXNamedCasts(true), WantRemainingKeywords(true),
219 WantObjCSuper(false),
220 IsObjCIvarLookup(false) {}
222 virtual ~CorrectionCandidateCallback() {}
224 /// \brief Simple predicate used by the default RankCandidate to
225 /// determine whether to return an edit distance of 0 or InvalidDistance.
226 /// This can be overrided by validators that only need to determine if a
227 /// candidate is viable, without ranking potentially viable candidates.
228 /// Only ValidateCandidate or RankCandidate need to be overriden by a
229 /// callback wishing to check the viability of correction candidates.
230 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
234 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
235 /// to a candidate (where a lower value represents a better candidate), or
236 /// returning InvalidDistance if the candidate is not at all viable. For
237 /// validation callbacks that only need to determine if a candidate is viable,
238 /// the default RankCandidate returns either 0 or InvalidDistance depending
239 /// whether ValidateCandidate returns true or false.
240 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
241 return ValidateCandidate(candidate) ? 0 : InvalidDistance;
244 // Flags for context-dependent keywords.
245 // TODO: Expand these to apply to non-keywords or possibly remove them.
246 bool WantTypeSpecifiers;
247 bool WantExpressionKeywords;
248 bool WantCXXNamedCasts;
249 bool WantRemainingKeywords;
251 // Temporary hack for the one case where a CorrectTypoContext enum is used
252 // when looking up results.
253 bool IsObjCIvarLookup;
256 /// @brief Simple template class for restricting typo correction candidates
257 /// to ones having a single Decl* of the given type.
259 class DeclFilterCCC : public CorrectionCandidateCallback {
261 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
262 return candidate.getCorrectionDeclAs<C>();