1224135Sdim//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
2224135Sdim//
3224135Sdim//                     The LLVM Compiler Infrastructure
4224135Sdim//
5224135Sdim// This file is distributed under the University of Illinois Open Source
6224135Sdim// License. See LICENSE.TXT for details.
7224135Sdim//
8224135Sdim//===----------------------------------------------------------------------===//
9224135Sdim//
10224135Sdim// This file defines the TypoCorrection class, which stores the results of
11224135Sdim// Sema's typo correction (Sema::CorrectTypo).
12224135Sdim//
13224135Sdim//===----------------------------------------------------------------------===//
14224135Sdim
15224135Sdim#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
16224135Sdim#define LLVM_CLANG_SEMA_TYPOCORRECTION_H
17224135Sdim
18224135Sdim#include "clang/AST/DeclCXX.h"
19249423Sdim#include "clang/Sema/DeclSpec.h"
20226633Sdim#include "llvm/ADT/SmallVector.h"
21224135Sdim
22224135Sdimnamespace clang {
23224135Sdim
24224135Sdim/// @brief Simple class containing the result of Sema::CorrectTypo
25224135Sdimclass TypoCorrection {
26224135Sdimpublic:
27234353Sdim  // "Distance" for unusable corrections
28234353Sdim  static const unsigned InvalidDistance = ~0U;
29234353Sdim  // The largest distance still considered valid (larger edit distances are
30234353Sdim  // mapped to InvalidDistance by getEditDistance).
31234353Sdim  static const unsigned MaximumDistance = 10000U;
32234353Sdim
33234353Sdim  // Relative weightings of the "edit distance" components. The higher the
34234353Sdim  // weight, the more of a penalty to fitness the component will give (higher
35234353Sdim  // weights mean greater contribution to the total edit distance, with the
36234353Sdim  // best correction candidates having the lowest edit distance).
37234353Sdim  static const unsigned CharDistanceWeight = 100U;
38234353Sdim  static const unsigned QualifierDistanceWeight = 110U;
39234353Sdim  static const unsigned CallbackDistanceWeight = 150U;
40234353Sdim
41224135Sdim  TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
42234353Sdim                 NestedNameSpecifier *NNS=0, unsigned CharDistance=0,
43234353Sdim                 unsigned QualifierDistance=0)
44234353Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
45234353Sdim      CharDistance(CharDistance), QualifierDistance(QualifierDistance),
46234353Sdim      CallbackDistance(0) {
47226633Sdim    if (NameDecl)
48226633Sdim      CorrectionDecls.push_back(NameDecl);
49226633Sdim  }
50224135Sdim
51226633Sdim  TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=0,
52234353Sdim                 unsigned CharDistance=0)
53234353Sdim      : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
54234353Sdim      CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {
55226633Sdim    if (Name)
56226633Sdim      CorrectionDecls.push_back(Name);
57226633Sdim  }
58224135Sdim
59226633Sdim  TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=0,
60234353Sdim                 unsigned CharDistance=0)
61234353Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
62234353Sdim      CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0) {}
63224135Sdim
64224135Sdim  TypoCorrection()
65234353Sdim      : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
66234353Sdim      CallbackDistance(0) {}
67224135Sdim
68224135Sdim  /// \brief Gets the DeclarationName of the typo correction
69224135Sdim  DeclarationName getCorrection() const { return CorrectionName; }
70224135Sdim  IdentifierInfo* getCorrectionAsIdentifierInfo() const {
71224135Sdim    return CorrectionName.getAsIdentifierInfo();
72224135Sdim  }
73224135Sdim
74224135Sdim  /// \brief Gets the NestedNameSpecifier needed to use the typo correction
75224135Sdim  NestedNameSpecifier* getCorrectionSpecifier() const {
76224135Sdim    return CorrectionNameSpec;
77224135Sdim  }
78224135Sdim  void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
79224135Sdim    CorrectionNameSpec = NNS;
80224135Sdim  }
81224135Sdim
82234353Sdim  void setQualifierDistance(unsigned ED) {
83234353Sdim    QualifierDistance = ED;
84234353Sdim  }
85224135Sdim
86234353Sdim  void setCallbackDistance(unsigned ED) {
87234353Sdim    CallbackDistance = ED;
88234353Sdim  }
89234353Sdim
90234353Sdim  // Convert the given weighted edit distance to a roughly equivalent number of
91234353Sdim  // single-character edits (typically for comparison to the length of the
92234353Sdim  // string being edited).
93234353Sdim  static unsigned NormalizeEditDistance(unsigned ED) {
94234353Sdim    if (ED > MaximumDistance)
95234353Sdim      return InvalidDistance;
96234353Sdim    return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
97234353Sdim  }
98234353Sdim
99234353Sdim  /// \brief Gets the "edit distance" of the typo correction from the typo.
100234353Sdim  /// If Normalized is true, scale the distance down by the CharDistanceWeight
101234353Sdim  /// to return the edit distance in terms of single-character edits.
102234353Sdim  unsigned getEditDistance(bool Normalized = true) const {
103234353Sdim    if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
104234353Sdim        CallbackDistance > MaximumDistance)
105234353Sdim      return InvalidDistance;
106234353Sdim    unsigned ED =
107234353Sdim        CharDistance * CharDistanceWeight +
108234353Sdim        QualifierDistance * QualifierDistanceWeight +
109234353Sdim        CallbackDistance * CallbackDistanceWeight;
110234353Sdim    if (ED > MaximumDistance)
111234353Sdim      return InvalidDistance;
112234353Sdim    // Half the CharDistanceWeight is added to ED to simulate rounding since
113234353Sdim    // integer division truncates the value (i.e. round-to-nearest-int instead
114234353Sdim    // of round-to-zero).
115234353Sdim    return Normalized ? NormalizeEditDistance(ED) : ED;
116234353Sdim  }
117234353Sdim
118224135Sdim  /// \brief Gets the pointer to the declaration of the typo correction
119224135Sdim  NamedDecl* getCorrectionDecl() const {
120226633Sdim    return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
121224135Sdim  }
122224135Sdim  template <class DeclClass>
123224135Sdim  DeclClass *getCorrectionDeclAs() const {
124224135Sdim    return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
125224135Sdim  }
126224135Sdim
127226633Sdim  /// \brief Clears the list of NamedDecls before adding the new one.
128224135Sdim  void setCorrectionDecl(NamedDecl *CDecl) {
129226633Sdim    CorrectionDecls.clear();
130226633Sdim    addCorrectionDecl(CDecl);
131224135Sdim  }
132224135Sdim
133226633Sdim  /// \brief Add the given NamedDecl to the list of NamedDecls that are the
134226633Sdim  /// declarations associated with the DeclarationName of this TypoCorrection
135226633Sdim  void addCorrectionDecl(NamedDecl *CDecl);
136226633Sdim
137224135Sdim  std::string getAsString(const LangOptions &LO) const;
138224135Sdim  std::string getQuoted(const LangOptions &LO) const {
139224135Sdim    return "'" + getAsString(LO) + "'";
140224135Sdim  }
141224135Sdim
142226633Sdim  /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
143224135Sdim  operator bool() const { return bool(CorrectionName); }
144224135Sdim
145226633Sdim  /// \brief Mark this TypoCorrection as being a keyword.
146226633Sdim  /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
147226633Sdim  /// added to the list of the correction's NamedDecl pointers, NULL is added
148226633Sdim  /// as the only element in the list to mark this TypoCorrection as a keyword.
149226633Sdim  void makeKeyword() {
150226633Sdim    CorrectionDecls.clear();
151226633Sdim    CorrectionDecls.push_back(0);
152226633Sdim  }
153224135Sdim
154226633Sdim  // Check if this TypoCorrection is a keyword by checking if the first
155226633Sdim  // item in CorrectionDecls is NULL.
156226633Sdim  bool isKeyword() const {
157226633Sdim    return !CorrectionDecls.empty() &&
158226633Sdim        CorrectionDecls.front() == 0;
159226633Sdim  }
160226633Sdim
161234353Sdim  // Check if this TypoCorrection is the given keyword.
162234353Sdim  template<std::size_t StrLen>
163234353Sdim  bool isKeyword(const char (&Str)[StrLen]) const {
164234353Sdim    return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
165234353Sdim  }
166234353Sdim
167224135Sdim  // Returns true if the correction either is a keyword or has a known decl.
168226633Sdim  bool isResolved() const { return !CorrectionDecls.empty(); }
169224135Sdim
170226633Sdim  bool isOverloaded() const {
171226633Sdim    return CorrectionDecls.size() > 1;
172226633Sdim  }
173226633Sdim
174243830Sdim  void setCorrectionRange(CXXScopeSpec* SS,
175243830Sdim                          const DeclarationNameInfo &TypoName) {
176243830Sdim    CorrectionRange.setBegin(CorrectionNameSpec && SS ? SS->getBeginLoc()
177243830Sdim                                                      : TypoName.getLoc());
178243830Sdim    CorrectionRange.setEnd(TypoName.getLoc());
179243830Sdim  }
180243830Sdim
181243830Sdim  SourceRange getCorrectionRange() const {
182243830Sdim    return CorrectionRange;
183243830Sdim  }
184243830Sdim
185249423Sdim  typedef SmallVector<NamedDecl *, 1>::iterator decl_iterator;
186226633Sdim  decl_iterator begin() {
187226633Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
188226633Sdim  }
189226633Sdim  decl_iterator end() { return CorrectionDecls.end(); }
190249423Sdim  typedef SmallVector<NamedDecl *, 1>::const_iterator const_decl_iterator;
191234353Sdim  const_decl_iterator begin() const {
192234353Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
193234353Sdim  }
194234353Sdim  const_decl_iterator end() const { return CorrectionDecls.end(); }
195226633Sdim
196224135Sdimprivate:
197226633Sdim  bool hasCorrectionDecl() const {
198226633Sdim    return (!isKeyword() && !CorrectionDecls.empty());
199226633Sdim  }
200226633Sdim
201224135Sdim  // Results.
202224135Sdim  DeclarationName CorrectionName;
203224135Sdim  NestedNameSpecifier *CorrectionNameSpec;
204249423Sdim  SmallVector<NamedDecl *, 1> CorrectionDecls;
205234353Sdim  unsigned CharDistance;
206234353Sdim  unsigned QualifierDistance;
207234353Sdim  unsigned CallbackDistance;
208243830Sdim  SourceRange CorrectionRange;
209224135Sdim};
210224135Sdim
211234353Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check
212234353Sdim/// the validity of a potential typo correction.
213234353Sdimclass CorrectionCandidateCallback {
214234353Sdim public:
215234353Sdim  static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
216234353Sdim
217234353Sdim  CorrectionCandidateCallback()
218234353Sdim      : WantTypeSpecifiers(true), WantExpressionKeywords(true),
219234353Sdim        WantCXXNamedCasts(true), WantRemainingKeywords(true),
220234353Sdim        WantObjCSuper(false),
221234353Sdim        IsObjCIvarLookup(false) {}
222234353Sdim
223234353Sdim  virtual ~CorrectionCandidateCallback() {}
224234353Sdim
225234353Sdim  /// \brief Simple predicate used by the default RankCandidate to
226234353Sdim  /// determine whether to return an edit distance of 0 or InvalidDistance.
227234353Sdim  /// This can be overrided by validators that only need to determine if a
228234353Sdim  /// candidate is viable, without ranking potentially viable candidates.
229234353Sdim  /// Only ValidateCandidate or RankCandidate need to be overriden by a
230234353Sdim  /// callback wishing to check the viability of correction candidates.
231249423Sdim  /// The default predicate always returns true if the candidate is not a type
232249423Sdim  /// name or keyword, true for types if WantTypeSpecifiers is true, and true
233249423Sdim  /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
234249423Sdim  /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
235249423Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate);
236234353Sdim
237234353Sdim  /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
238234353Sdim  /// to a candidate (where a lower value represents a better candidate), or
239234353Sdim  /// returning InvalidDistance if the candidate is not at all viable. For
240234353Sdim  /// validation callbacks that only need to determine if a candidate is viable,
241234353Sdim  /// the default RankCandidate returns either 0 or InvalidDistance depending
242234353Sdim  /// whether ValidateCandidate returns true or false.
243234353Sdim  virtual unsigned RankCandidate(const TypoCorrection &candidate) {
244234353Sdim    return ValidateCandidate(candidate) ? 0 : InvalidDistance;
245234353Sdim  }
246234353Sdim
247234353Sdim  // Flags for context-dependent keywords.
248234353Sdim  // TODO: Expand these to apply to non-keywords or possibly remove them.
249234353Sdim  bool WantTypeSpecifiers;
250234353Sdim  bool WantExpressionKeywords;
251234353Sdim  bool WantCXXNamedCasts;
252234353Sdim  bool WantRemainingKeywords;
253234353Sdim  bool WantObjCSuper;
254234353Sdim  // Temporary hack for the one case where a CorrectTypoContext enum is used
255234353Sdim  // when looking up results.
256234353Sdim  bool IsObjCIvarLookup;
257234353Sdim};
258234353Sdim
259234353Sdim/// @brief Simple template class for restricting typo correction candidates
260234353Sdim/// to ones having a single Decl* of the given type.
261234353Sdimtemplate <class C>
262234353Sdimclass DeclFilterCCC : public CorrectionCandidateCallback {
263234353Sdim public:
264234353Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
265234353Sdim    return candidate.getCorrectionDeclAs<C>();
266234353Sdim  }
267234353Sdim};
268234353Sdim
269224135Sdim}
270224135Sdim
271224135Sdim#endif
272