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),
46 CallbackDistance(0), ForceSpecifierReplacement(false),
47 RequiresImport(false) {
49 CorrectionDecls.push_back(NameDecl);
52 TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
53 unsigned CharDistance = 0)
54 : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
55 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
56 ForceSpecifierReplacement(false), RequiresImport(false) {
58 CorrectionDecls.push_back(Name);
61 TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
62 unsigned CharDistance = 0)
63 : CorrectionName(Name), CorrectionNameSpec(NNS),
64 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
65 ForceSpecifierReplacement(false), RequiresImport(false) {}
68 : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
69 CallbackDistance(0), ForceSpecifierReplacement(false),
70 RequiresImport(false) {}
72 /// \brief Gets the DeclarationName of the typo correction
73 DeclarationName getCorrection() const { return CorrectionName; }
74 IdentifierInfo* getCorrectionAsIdentifierInfo() const {
75 return CorrectionName.getAsIdentifierInfo();
78 /// \brief Gets the NestedNameSpecifier needed to use the typo correction
79 NestedNameSpecifier* getCorrectionSpecifier() const {
80 return CorrectionNameSpec;
82 void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
83 CorrectionNameSpec = NNS;
84 ForceSpecifierReplacement = (NNS != 0);
87 void WillReplaceSpecifier(bool ForceReplacement) {
88 ForceSpecifierReplacement = ForceReplacement;
91 bool WillReplaceSpecifier() const {
92 return ForceSpecifierReplacement;
95 void setQualifierDistance(unsigned ED) {
96 QualifierDistance = ED;
99 void setCallbackDistance(unsigned ED) {
100 CallbackDistance = ED;
103 // Convert the given weighted edit distance to a roughly equivalent number of
104 // single-character edits (typically for comparison to the length of the
105 // string being edited).
106 static unsigned NormalizeEditDistance(unsigned ED) {
107 if (ED > MaximumDistance)
108 return InvalidDistance;
109 return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
112 /// \brief Gets the "edit distance" of the typo correction from the typo.
113 /// If Normalized is true, scale the distance down by the CharDistanceWeight
114 /// to return the edit distance in terms of single-character edits.
115 unsigned getEditDistance(bool Normalized = true) const {
116 if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
117 CallbackDistance > MaximumDistance)
118 return InvalidDistance;
120 CharDistance * CharDistanceWeight +
121 QualifierDistance * QualifierDistanceWeight +
122 CallbackDistance * CallbackDistanceWeight;
123 if (ED > MaximumDistance)
124 return InvalidDistance;
125 // Half the CharDistanceWeight is added to ED to simulate rounding since
126 // integer division truncates the value (i.e. round-to-nearest-int instead
127 // of round-to-zero).
128 return Normalized ? NormalizeEditDistance(ED) : ED;
131 /// \brief Gets the pointer to the declaration of the typo correction
132 NamedDecl *getCorrectionDecl() const {
133 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
135 template <class DeclClass>
136 DeclClass *getCorrectionDeclAs() const {
137 return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
140 /// \brief Clears the list of NamedDecls.
141 void ClearCorrectionDecls() {
142 CorrectionDecls.clear();
145 /// \brief Clears the list of NamedDecls before adding the new one.
146 void setCorrectionDecl(NamedDecl *CDecl) {
147 CorrectionDecls.clear();
148 addCorrectionDecl(CDecl);
151 /// \brief Clears the list of NamedDecls and adds the given set.
152 void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
153 CorrectionDecls.clear();
154 CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
157 /// \brief Add the given NamedDecl to the list of NamedDecls that are the
158 /// declarations associated with the DeclarationName of this TypoCorrection
159 void addCorrectionDecl(NamedDecl *CDecl);
161 std::string getAsString(const LangOptions &LO) const;
162 std::string getQuoted(const LangOptions &LO) const {
163 return "'" + getAsString(LO) + "'";
166 /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
167 LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
169 /// \brief Mark this TypoCorrection as being a keyword.
170 /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
171 /// added to the list of the correction's NamedDecl pointers, NULL is added
172 /// as the only element in the list to mark this TypoCorrection as a keyword.
174 CorrectionDecls.clear();
175 CorrectionDecls.push_back(0);
176 ForceSpecifierReplacement = true;
179 // Check if this TypoCorrection is a keyword by checking if the first
180 // item in CorrectionDecls is NULL.
181 bool isKeyword() const {
182 return !CorrectionDecls.empty() &&
183 CorrectionDecls.front() == 0;
186 // Check if this TypoCorrection is the given keyword.
187 template<std::size_t StrLen>
188 bool isKeyword(const char (&Str)[StrLen]) const {
189 return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
192 // Returns true if the correction either is a keyword or has a known decl.
193 bool isResolved() const { return !CorrectionDecls.empty(); }
195 bool isOverloaded() const {
196 return CorrectionDecls.size() > 1;
199 void setCorrectionRange(CXXScopeSpec *SS,
200 const DeclarationNameInfo &TypoName) {
201 CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
203 : TypoName.getLoc());
204 CorrectionRange.setEnd(TypoName.getLoc());
207 SourceRange getCorrectionRange() const {
208 return CorrectionRange;
211 typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
212 decl_iterator begin() {
213 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
215 decl_iterator end() { return CorrectionDecls.end(); }
216 typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
217 const_decl_iterator begin() const {
218 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
220 const_decl_iterator end() const { return CorrectionDecls.end(); }
222 /// \brief Returns whether this typo correction is correcting to a
223 /// declaration that was declared in a module that has not been imported.
224 bool requiresImport() const { return RequiresImport; }
225 void setRequiresImport(bool Req) { RequiresImport = Req; }
228 bool hasCorrectionDecl() const {
229 return (!isKeyword() && !CorrectionDecls.empty());
233 DeclarationName CorrectionName;
234 NestedNameSpecifier *CorrectionNameSpec;
235 SmallVector<NamedDecl *, 1> CorrectionDecls;
236 unsigned CharDistance;
237 unsigned QualifierDistance;
238 unsigned CallbackDistance;
239 SourceRange CorrectionRange;
240 bool ForceSpecifierReplacement;
244 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
245 /// the validity of a potential typo correction.
246 class CorrectionCandidateCallback {
248 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
250 CorrectionCandidateCallback()
251 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
252 WantCXXNamedCasts(true), WantRemainingKeywords(true),
253 WantObjCSuper(false),
254 IsObjCIvarLookup(false) {}
256 virtual ~CorrectionCandidateCallback() {}
258 /// \brief Simple predicate used by the default RankCandidate to
259 /// determine whether to return an edit distance of 0 or InvalidDistance.
260 /// This can be overrided by validators that only need to determine if a
261 /// candidate is viable, without ranking potentially viable candidates.
262 /// Only ValidateCandidate or RankCandidate need to be overriden by a
263 /// callback wishing to check the viability of correction candidates.
264 /// The default predicate always returns true if the candidate is not a type
265 /// name or keyword, true for types if WantTypeSpecifiers is true, and true
266 /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
267 /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
268 virtual bool ValidateCandidate(const TypoCorrection &candidate);
270 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
271 /// to a candidate (where a lower value represents a better candidate), or
272 /// returning InvalidDistance if the candidate is not at all viable. For
273 /// validation callbacks that only need to determine if a candidate is viable,
274 /// the default RankCandidate returns either 0 or InvalidDistance depending
275 /// whether ValidateCandidate returns true or false.
276 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
277 return ValidateCandidate(candidate) ? 0 : InvalidDistance;
280 // Flags for context-dependent keywords.
281 // TODO: Expand these to apply to non-keywords or possibly remove them.
282 bool WantTypeSpecifiers;
283 bool WantExpressionKeywords;
284 bool WantCXXNamedCasts;
285 bool WantRemainingKeywords;
287 // Temporary hack for the one case where a CorrectTypoContext enum is used
288 // when looking up results.
289 bool IsObjCIvarLookup;
292 /// @brief Simple template class for restricting typo correction candidates
293 /// to ones having a single Decl* of the given type.
295 class DeclFilterCCC : public CorrectionCandidateCallback {
297 virtual bool ValidateCandidate(const TypoCorrection &candidate) {
298 return candidate.getCorrectionDeclAs<C>();
302 // @brief Callback class to limit the allowed keywords and to only accept typo
303 // corrections that are keywords or whose decls refer to functions (or template
304 // functions) that accept the given number of arguments.
305 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
307 FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
308 bool HasExplicitTemplateArgs);
310 virtual bool ValidateCandidate(const TypoCorrection &candidate);
314 bool HasExplicitTemplateArgs;
317 // @brief Callback class that effectively disabled typo correction
318 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
320 NoTypoCorrectionCCC() {
321 WantTypeSpecifiers = false;
322 WantExpressionKeywords = false;
323 WantCXXNamedCasts = false;
324 WantRemainingKeywords = false;
327 virtual bool ValidateCandidate(const TypoCorrection &candidate) {