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 Get the correction declaration found by name lookup (before we
133 /// looked through using shadow declarations and the like).
134 NamedDecl *getFoundDecl() const {
135 return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : nullptr;
138 /// \brief Gets the pointer to the declaration of the typo correction
139 NamedDecl *getCorrectionDecl() const {
140 auto *D = getFoundDecl();
141 return D ? D->getUnderlyingDecl() : nullptr;
143 template <class DeclClass>
144 DeclClass *getCorrectionDeclAs() const {
145 return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
148 /// \brief Clears the list of NamedDecls.
149 void ClearCorrectionDecls() {
150 CorrectionDecls.clear();
153 /// \brief Clears the list of NamedDecls before adding the new one.
154 void setCorrectionDecl(NamedDecl *CDecl) {
155 CorrectionDecls.clear();
156 addCorrectionDecl(CDecl);
159 /// \brief Clears the list of NamedDecls and adds the given set.
160 void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
161 CorrectionDecls.clear();
162 CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
165 /// \brief Add the given NamedDecl to the list of NamedDecls that are the
166 /// declarations associated with the DeclarationName of this TypoCorrection
167 void addCorrectionDecl(NamedDecl *CDecl);
169 std::string getAsString(const LangOptions &LO) const;
170 std::string getQuoted(const LangOptions &LO) const {
171 return "'" + getAsString(LO) + "'";
174 /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
175 explicit operator bool() const { return bool(CorrectionName); }
177 /// \brief Mark this TypoCorrection as being a keyword.
178 /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
179 /// added to the list of the correction's NamedDecl pointers, NULL is added
180 /// as the only element in the list to mark this TypoCorrection as a keyword.
182 CorrectionDecls.clear();
183 CorrectionDecls.push_back(nullptr);
184 ForceSpecifierReplacement = true;
187 // Check if this TypoCorrection is a keyword by checking if the first
188 // item in CorrectionDecls is NULL.
189 bool isKeyword() const {
190 return !CorrectionDecls.empty() && CorrectionDecls.front() == nullptr;
193 // Check if this TypoCorrection is the given keyword.
194 template<std::size_t StrLen>
195 bool isKeyword(const char (&Str)[StrLen]) const {
196 return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
199 // Returns true if the correction either is a keyword or has a known decl.
200 bool isResolved() const { return !CorrectionDecls.empty(); }
202 bool isOverloaded() const {
203 return CorrectionDecls.size() > 1;
206 void setCorrectionRange(CXXScopeSpec *SS,
207 const DeclarationNameInfo &TypoName) {
208 CorrectionRange = TypoName.getSourceRange();
209 if (ForceSpecifierReplacement && SS && !SS->isEmpty())
210 CorrectionRange.setBegin(SS->getBeginLoc());
213 SourceRange getCorrectionRange() const {
214 return CorrectionRange;
217 typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
218 decl_iterator begin() {
219 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
221 decl_iterator end() { return CorrectionDecls.end(); }
222 typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
223 const_decl_iterator begin() const {
224 return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
226 const_decl_iterator end() const { return CorrectionDecls.end(); }
228 /// \brief Returns whether this typo correction is correcting to a
229 /// declaration that was declared in a module that has not been imported.
230 bool requiresImport() const { return RequiresImport; }
231 void setRequiresImport(bool Req) { RequiresImport = Req; }
234 bool hasCorrectionDecl() const {
235 return (!isKeyword() && !CorrectionDecls.empty());
239 DeclarationName CorrectionName;
240 NestedNameSpecifier *CorrectionNameSpec;
241 SmallVector<NamedDecl *, 1> CorrectionDecls;
242 unsigned CharDistance;
243 unsigned QualifierDistance;
244 unsigned CallbackDistance;
245 SourceRange CorrectionRange;
246 bool ForceSpecifierReplacement;
250 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
251 /// the validity of a potential typo correction.
252 class CorrectionCandidateCallback {
254 static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
256 explicit CorrectionCandidateCallback(IdentifierInfo *Typo = nullptr,
257 NestedNameSpecifier *TypoNNS = nullptr)
258 : WantTypeSpecifiers(true), WantExpressionKeywords(true),
259 WantCXXNamedCasts(true), WantFunctionLikeCasts(true),
260 WantRemainingKeywords(true), WantObjCSuper(false),
261 IsObjCIvarLookup(false), IsAddressOfOperand(false), Typo(Typo),
264 virtual ~CorrectionCandidateCallback() {}
266 /// \brief Simple predicate used by the default RankCandidate to
267 /// determine whether to return an edit distance of 0 or InvalidDistance.
268 /// This can be overrided by validators that only need to determine if a
269 /// candidate is viable, without ranking potentially viable candidates.
270 /// Only ValidateCandidate or RankCandidate need to be overriden by a
271 /// callback wishing to check the viability of correction candidates.
272 /// The default predicate always returns true if the candidate is not a type
273 /// name or keyword, true for types if WantTypeSpecifiers is true, and true
274 /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
275 /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
276 virtual bool ValidateCandidate(const TypoCorrection &candidate);
278 /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
279 /// to a candidate (where a lower value represents a better candidate), or
280 /// returning InvalidDistance if the candidate is not at all viable. For
281 /// validation callbacks that only need to determine if a candidate is viable,
282 /// the default RankCandidate returns either 0 or InvalidDistance depending
283 /// whether ValidateCandidate returns true or false.
284 virtual unsigned RankCandidate(const TypoCorrection &candidate) {
285 return (!MatchesTypo(candidate) && ValidateCandidate(candidate))
290 void setTypoName(IdentifierInfo *II) { Typo = II; }
291 void setTypoNNS(NestedNameSpecifier *NNS) { TypoNNS = NNS; }
293 // Flags for context-dependent keywords. WantFunctionLikeCasts is only
294 // used/meaningful when WantCXXNamedCasts is false.
295 // TODO: Expand these to apply to non-keywords or possibly remove them.
296 bool WantTypeSpecifiers;
297 bool WantExpressionKeywords;
298 bool WantCXXNamedCasts;
299 bool WantFunctionLikeCasts;
300 bool WantRemainingKeywords;
302 // Temporary hack for the one case where a CorrectTypoContext enum is used
303 // when looking up results.
304 bool IsObjCIvarLookup;
305 bool IsAddressOfOperand;
308 bool MatchesTypo(const TypoCorrection &candidate) {
309 return Typo && candidate.isResolved() && !candidate.requiresImport() &&
310 candidate.getCorrectionAsIdentifierInfo() == Typo &&
311 // FIXME: This probably does not return true when both
312 // NestedNameSpecifiers have the same textual representation.
313 candidate.getCorrectionSpecifier() == TypoNNS;
316 IdentifierInfo *Typo;
317 NestedNameSpecifier *TypoNNS;
320 /// @brief Simple template class for restricting typo correction candidates
321 /// to ones having a single Decl* of the given type.
323 class DeclFilterCCC : public CorrectionCandidateCallback {
325 bool ValidateCandidate(const TypoCorrection &candidate) override {
326 return candidate.getCorrectionDeclAs<C>();
330 // @brief Callback class to limit the allowed keywords and to only accept typo
331 // corrections that are keywords or whose decls refer to functions (or template
332 // functions) that accept the given number of arguments.
333 class FunctionCallFilterCCC : public CorrectionCandidateCallback {
335 FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
336 bool HasExplicitTemplateArgs,
337 MemberExpr *ME = nullptr);
339 bool ValidateCandidate(const TypoCorrection &candidate) override;
343 bool HasExplicitTemplateArgs;
344 DeclContext *CurContext;
345 MemberExpr *MemberFn;
348 // @brief Callback class that effectively disabled typo correction
349 class NoTypoCorrectionCCC : public CorrectionCandidateCallback {
351 NoTypoCorrectionCCC() {
352 WantTypeSpecifiers = false;
353 WantExpressionKeywords = false;
354 WantCXXNamedCasts = false;
355 WantFunctionLikeCasts = false;
356 WantRemainingKeywords = false;
359 bool ValidateCandidate(const TypoCorrection &candidate) override {