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 "clang/Sema/Ownership.h"
21 #include "llvm/ADT/SmallVector.h"
25 /// @brief Simple class containing the result of Sema::CorrectTypo
26 class TypoCorrection {
28 // "Distance" for unusable corrections
29 static const unsigned InvalidDistance = ~0U;
30 // The largest distance still considered valid (larger edit distances are
31 // mapped to InvalidDistance by getEditDistance).
32 static const unsigned MaximumDistance = 10000U;
34 // Relative weightings of the "edit distance" components. The higher the
35 // weight, the more of a penalty to fitness the component will give (higher
36 // weights mean greater contribution to the total edit distance, with the
37 // best correction candidates having the lowest edit distance).
38 static const unsigned CharDistanceWeight = 100U;
39 static const unsigned QualifierDistanceWeight = 110U;
40 static const unsigned CallbackDistanceWeight = 150U;
42 TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
43 NestedNameSpecifier *NNS = nullptr, unsigned CharDistance = 0,
44 unsigned QualifierDistance = 0)
45 : CorrectionName(Name), CorrectionNameSpec(NNS),
46 CharDistance(CharDistance), QualifierDistance(QualifierDistance),
47 CallbackDistance(0), ForceSpecifierReplacement(false),
48 RequiresImport(false) {
50 CorrectionDecls.push_back(NameDecl);
53 TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = nullptr,
54 unsigned CharDistance = 0)
55 : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
56 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
57 ForceSpecifierReplacement(false), RequiresImport(false) {
59 CorrectionDecls.push_back(Name);
62 TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = nullptr,
63 unsigned CharDistance = 0)
64 : CorrectionName(Name), CorrectionNameSpec(NNS),
65 CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
66 ForceSpecifierReplacement(false), RequiresImport(false) {}
69 : CorrectionNameSpec(nullptr), CharDistance(0), QualifierDistance(0),
70 CallbackDistance(0), ForceSpecifierReplacement(false),
71 RequiresImport(false) {}
73 /// \brief Gets the DeclarationName of the typo correction
74 DeclarationName getCorrection() const { return CorrectionName; }
75 IdentifierInfo* getCorrectionAsIdentifierInfo() const {
76 return CorrectionName.getAsIdentifierInfo();
79 /// \brief Gets the NestedNameSpecifier needed to use the typo correction
80 NestedNameSpecifier* getCorrectionSpecifier() const {
81 return CorrectionNameSpec;
83 void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
84 CorrectionNameSpec = NNS;
85 ForceSpecifierReplacement = (NNS != nullptr);
88 void WillReplaceSpecifier(bool ForceReplacement) {
89 ForceSpecifierReplacement = ForceReplacement;
92 bool WillReplaceSpecifier() const {
93 return ForceSpecifierReplacement;
96 void setQualifierDistance(unsigned ED) {
97 QualifierDistance = ED;
100 void setCallbackDistance(unsigned ED) {
101 CallbackDistance = ED;
104 // Convert the given weighted edit distance to a roughly equivalent number of
105 // single-character edits (typically for comparison to the length of the
106 // string being edited).
107 static unsigned NormalizeEditDistance(unsigned ED) {
108 if (ED > MaximumDistance)
109 return InvalidDistance;
110 return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
113 /// \brief Gets the "edit distance" of the typo correction from the typo.
114 /// If Normalized is true, scale the distance down by the CharDistanceWeight
115 /// to return the edit distance in terms of single-character edits.
116 unsigned getEditDistance(bool Normalized = true) const {
117 if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
118 CallbackDistance > MaximumDistance)
119 return InvalidDistance;
121 CharDistance * CharDistanceWeight +
122 QualifierDistance * QualifierDistanceWeight +
123 CallbackDistance * CallbackDistanceWeight;
124 if (ED > MaximumDistance)
125 return InvalidDistance;
126 // Half the CharDistanceWeight is added to ED to simulate rounding since
127 // integer division truncates the value (i.e. round-to-nearest-int instead
128 // of round-to-zero).
129 return Normalized ? NormalizeEditDistance(ED) : ED;
132 /// \brief Gets the pointer to the declaration of the typo correction
133 NamedDecl *getCorrectionDecl() const {
134 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : nullptr;
136 template <class DeclClass>
137 DeclClass *getCorrectionDeclAs() const {
138 return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
141 /// \brief Clears the list of NamedDecls.
142 void ClearCorrectionDecls() {
143 CorrectionDecls.clear();
146 /// \brief Clears the list of NamedDecls before adding the new one.
147 void setCorrectionDecl(NamedDecl *CDecl) {
148 CorrectionDecls.clear();
149 addCorrectionDecl(CDecl);
152 /// \brief Clears the list of NamedDecls and adds the given set.
153 void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
154 CorrectionDecls.clear();
155 CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
158 /// \brief Add the given NamedDecl to the list of NamedDecls that are the
159 /// declarations associated with the DeclarationName of this TypoCorrection
160 void addCorrectionDecl(NamedDecl *CDecl);
162 std::string getAsString(const LangOptions &LO) const;
163 std::string getQuoted(const LangOptions &LO) const {
164 return "'" + getAsString(LO) + "'";
167 /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
168 explicit operator bool() const { return bool(CorrectionName); }
170 /// \brief Mark this TypoCorrection as being a keyword.
171 /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
172 /// added to the list of the correction's NamedDecl pointers, NULL is added
173 /// as the only element in the list to mark this TypoCorrection as a keyword.
175 CorrectionDecls.clear();
176 CorrectionDecls.push_back(nullptr);
177 ForceSpecifierReplacement = true;
180 // Check if this TypoCorrection is a keyword by checking if the first
181 // item in CorrectionDecls is NULL.
182 bool isKeyword() const {
183 return !CorrectionDecls.empty() &&
184 CorrectionDecls.front() == nullptr;
187 // Check if this TypoCorrection is the given keyword.
188 template<std::size_t StrLen>
189 bool isKeyword(const char (&Str)[StrLen]) const {
190 return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
193 // Returns true if the correction either is a keyword or has a known decl.
194 bool isResolved() const { return !CorrectionDecls.empty(); }
196 bool isOverloaded() const {
197 return CorrectionDecls.size() > 1;
200 void setCorrectionRange(CXXScopeSpec *SS,
201 const DeclarationNameInfo &TypoName) {
202 CorrectionRange = TypoName.getSourceRange();
203 if (ForceSpecifierReplacement && SS && !SS->isEmpty())
204 CorrectionRange.setBegin(SS->getBeginLoc());
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 explicit CorrectionCandidateCallback(IdentifierInfo *Typo = nullptr,
251 NestedNameSpecifier *TypoNNS = nullptr)
252 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
253 WantCXXNamedCasts(true), WantFunctionLikeCasts(true),
254 WantRemainingKeywords(true), WantObjCSuper(false),
255 IsObjCIvarLookup(false), IsAddressOfOperand(false), Typo(Typo),
258 virtual ~CorrectionCandidateCallback() {}
260 /// \brief Simple predicate used by the default RankCandidate to
261 /// determine whether to return an edit distance of 0 or InvalidDistance.
262 /// This can be overrided by validators that only need to determine if a
263 /// candidate is viable, without ranking potentially viable candidates.
264 /// Only ValidateCandidate or RankCandidate need to be overriden by a
265 /// callback wishing to check the viability of correction candidates.
266 /// The default predicate always returns true if the candidate is not a type
267 /// name or keyword, true for types if WantTypeSpecifiers is true, and true
268 /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
269 /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
270 virtual bool ValidateCandidate(const TypoCorrection &candidate);
272 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
273 /// to a candidate (where a lower value represents a better candidate), or
274 /// returning InvalidDistance if the candidate is not at all viable. For
275 /// validation callbacks that only need to determine if a candidate is viable,
276 /// the default RankCandidate returns either 0 or InvalidDistance depending
277 /// whether ValidateCandidate returns true or false.
278 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
279 return (!MatchesTypo(candidate) && ValidateCandidate(candidate))
284 void setTypoName(IdentifierInfo *II) { Typo = II; }
285 void setTypoNNS(NestedNameSpecifier *NNS) { TypoNNS = NNS; }
287 // Flags for context-dependent keywords. WantFunctionLikeCasts is only
288 // used/meaningful when WantCXXNamedCasts is false.
289 // TODO: Expand these to apply to non-keywords or possibly remove them.
290 bool WantTypeSpecifiers;
291 bool WantExpressionKeywords;
292 bool WantCXXNamedCasts;
293 bool WantFunctionLikeCasts;
294 bool WantRemainingKeywords;
296 // Temporary hack for the one case where a CorrectTypoContext enum is used
297 // when looking up results.
298 bool IsObjCIvarLookup;
299 bool IsAddressOfOperand;
302 bool MatchesTypo(const TypoCorrection &candidate) {
303 return Typo && candidate.isResolved() && !candidate.requiresImport() &&
304 candidate.getCorrectionAsIdentifierInfo() == Typo &&
305 // FIXME: This probably does not return true when both
306 // NestedNameSpecifiers have the same textual representation.
307 candidate.getCorrectionSpecifier() == TypoNNS;
310 IdentifierInfo *Typo;
311 NestedNameSpecifier *TypoNNS;
314 /// @brief Simple template class for restricting typo correction candidates
315 /// to ones having a single Decl* of the given type.
317 class DeclFilterCCC : public CorrectionCandidateCallback {
319 bool ValidateCandidate(const TypoCorrection &candidate) override {
320 return candidate.getCorrectionDeclAs<C>();
324 // @brief Callback class to limit the allowed keywords and to only accept typo
325 // corrections that are keywords or whose decls refer to functions (or template
326 // functions) that accept the given number of arguments.
327 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
329 FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
330 bool HasExplicitTemplateArgs,
331 MemberExpr *ME = nullptr);
333 bool ValidateCandidate(const TypoCorrection &candidate) override;
337 bool HasExplicitTemplateArgs;
338 DeclContext *CurContext;
339 MemberExpr *MemberFn;
342 // @brief Callback class that effectively disabled typo correction
343 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
345 NoTypoCorrectionCCC() {
346 WantTypeSpecifiers = false;
347 WantExpressionKeywords = false;
348 WantCXXNamedCasts = false;
349 WantFunctionLikeCasts = false;
350 WantRemainingKeywords = false;
353 bool ValidateCandidate(const TypoCorrection &candidate) override {