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,
42263508Sdim                 NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0,
43263508Sdim                 unsigned QualifierDistance = 0)
44234353Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
45263508Sdim        CharDistance(CharDistance), QualifierDistance(QualifierDistance),
46263508Sdim        CallbackDistance(0), ForceSpecifierReplacement(false),
47263508Sdim        RequiresImport(false) {
48226633Sdim    if (NameDecl)
49226633Sdim      CorrectionDecls.push_back(NameDecl);
50226633Sdim  }
51224135Sdim
52263508Sdim  TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
53263508Sdim                 unsigned CharDistance = 0)
54234353Sdim      : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
55263508Sdim        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
56263508Sdim        ForceSpecifierReplacement(false), RequiresImport(false) {
57226633Sdim    if (Name)
58226633Sdim      CorrectionDecls.push_back(Name);
59226633Sdim  }
60224135Sdim
61263508Sdim  TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
62263508Sdim                 unsigned CharDistance = 0)
63234353Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
64263508Sdim        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
65263508Sdim        ForceSpecifierReplacement(false), RequiresImport(false) {}
66224135Sdim
67224135Sdim  TypoCorrection()
68234353Sdim      : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
69263508Sdim        CallbackDistance(0), ForceSpecifierReplacement(false),
70263508Sdim        RequiresImport(false) {}
71224135Sdim
72224135Sdim  /// \brief Gets the DeclarationName of the typo correction
73224135Sdim  DeclarationName getCorrection() const { return CorrectionName; }
74224135Sdim  IdentifierInfo* getCorrectionAsIdentifierInfo() const {
75224135Sdim    return CorrectionName.getAsIdentifierInfo();
76224135Sdim  }
77224135Sdim
78224135Sdim  /// \brief Gets the NestedNameSpecifier needed to use the typo correction
79224135Sdim  NestedNameSpecifier* getCorrectionSpecifier() const {
80224135Sdim    return CorrectionNameSpec;
81224135Sdim  }
82224135Sdim  void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
83224135Sdim    CorrectionNameSpec = NNS;
84263508Sdim    ForceSpecifierReplacement = (NNS != 0);
85224135Sdim  }
86224135Sdim
87263508Sdim  void WillReplaceSpecifier(bool ForceReplacement) {
88263508Sdim    ForceSpecifierReplacement = ForceReplacement;
89263508Sdim  }
90263508Sdim
91263508Sdim  bool WillReplaceSpecifier() const {
92263508Sdim    return ForceSpecifierReplacement;
93263508Sdim  }
94263508Sdim
95234353Sdim  void setQualifierDistance(unsigned ED) {
96234353Sdim    QualifierDistance = ED;
97234353Sdim  }
98224135Sdim
99234353Sdim  void setCallbackDistance(unsigned ED) {
100234353Sdim    CallbackDistance = ED;
101234353Sdim  }
102234353Sdim
103234353Sdim  // Convert the given weighted edit distance to a roughly equivalent number of
104234353Sdim  // single-character edits (typically for comparison to the length of the
105234353Sdim  // string being edited).
106234353Sdim  static unsigned NormalizeEditDistance(unsigned ED) {
107234353Sdim    if (ED > MaximumDistance)
108234353Sdim      return InvalidDistance;
109234353Sdim    return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
110234353Sdim  }
111234353Sdim
112234353Sdim  /// \brief Gets the "edit distance" of the typo correction from the typo.
113234353Sdim  /// If Normalized is true, scale the distance down by the CharDistanceWeight
114234353Sdim  /// to return the edit distance in terms of single-character edits.
115234353Sdim  unsigned getEditDistance(bool Normalized = true) const {
116234353Sdim    if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
117234353Sdim        CallbackDistance > MaximumDistance)
118234353Sdim      return InvalidDistance;
119234353Sdim    unsigned ED =
120234353Sdim        CharDistance * CharDistanceWeight +
121234353Sdim        QualifierDistance * QualifierDistanceWeight +
122234353Sdim        CallbackDistance * CallbackDistanceWeight;
123234353Sdim    if (ED > MaximumDistance)
124234353Sdim      return InvalidDistance;
125234353Sdim    // Half the CharDistanceWeight is added to ED to simulate rounding since
126234353Sdim    // integer division truncates the value (i.e. round-to-nearest-int instead
127234353Sdim    // of round-to-zero).
128234353Sdim    return Normalized ? NormalizeEditDistance(ED) : ED;
129234353Sdim  }
130234353Sdim
131224135Sdim  /// \brief Gets the pointer to the declaration of the typo correction
132263508Sdim  NamedDecl *getCorrectionDecl() const {
133226633Sdim    return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
134224135Sdim  }
135224135Sdim  template <class DeclClass>
136224135Sdim  DeclClass *getCorrectionDeclAs() const {
137224135Sdim    return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
138224135Sdim  }
139263508Sdim
140263508Sdim  /// \brief Clears the list of NamedDecls.
141263508Sdim  void ClearCorrectionDecls() {
142263508Sdim    CorrectionDecls.clear();
143263508Sdim  }
144263508Sdim
145226633Sdim  /// \brief Clears the list of NamedDecls before adding the new one.
146224135Sdim  void setCorrectionDecl(NamedDecl *CDecl) {
147226633Sdim    CorrectionDecls.clear();
148226633Sdim    addCorrectionDecl(CDecl);
149224135Sdim  }
150224135Sdim
151263508Sdim  /// \brief Clears the list of NamedDecls and adds the given set.
152263508Sdim  void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
153263508Sdim    CorrectionDecls.clear();
154263508Sdim    CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
155263508Sdim  }
156263508Sdim
157226633Sdim  /// \brief Add the given NamedDecl to the list of NamedDecls that are the
158226633Sdim  /// declarations associated with the DeclarationName of this TypoCorrection
159226633Sdim  void addCorrectionDecl(NamedDecl *CDecl);
160226633Sdim
161224135Sdim  std::string getAsString(const LangOptions &LO) const;
162224135Sdim  std::string getQuoted(const LangOptions &LO) const {
163224135Sdim    return "'" + getAsString(LO) + "'";
164224135Sdim  }
165224135Sdim
166226633Sdim  /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
167263508Sdim  LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
168224135Sdim
169226633Sdim  /// \brief Mark this TypoCorrection as being a keyword.
170226633Sdim  /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
171226633Sdim  /// added to the list of the correction's NamedDecl pointers, NULL is added
172226633Sdim  /// as the only element in the list to mark this TypoCorrection as a keyword.
173226633Sdim  void makeKeyword() {
174226633Sdim    CorrectionDecls.clear();
175226633Sdim    CorrectionDecls.push_back(0);
176263508Sdim    ForceSpecifierReplacement = true;
177226633Sdim  }
178224135Sdim
179226633Sdim  // Check if this TypoCorrection is a keyword by checking if the first
180226633Sdim  // item in CorrectionDecls is NULL.
181226633Sdim  bool isKeyword() const {
182226633Sdim    return !CorrectionDecls.empty() &&
183226633Sdim        CorrectionDecls.front() == 0;
184226633Sdim  }
185226633Sdim
186234353Sdim  // Check if this TypoCorrection is the given keyword.
187234353Sdim  template<std::size_t StrLen>
188234353Sdim  bool isKeyword(const char (&Str)[StrLen]) const {
189234353Sdim    return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
190234353Sdim  }
191234353Sdim
192224135Sdim  // Returns true if the correction either is a keyword or has a known decl.
193226633Sdim  bool isResolved() const { return !CorrectionDecls.empty(); }
194224135Sdim
195226633Sdim  bool isOverloaded() const {
196226633Sdim    return CorrectionDecls.size() > 1;
197226633Sdim  }
198226633Sdim
199263508Sdim  void setCorrectionRange(CXXScopeSpec *SS,
200243830Sdim                          const DeclarationNameInfo &TypoName) {
201263508Sdim    CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
202263508Sdim                                 ? SS->getBeginLoc()
203263508Sdim                                 : TypoName.getLoc());
204243830Sdim    CorrectionRange.setEnd(TypoName.getLoc());
205243830Sdim  }
206243830Sdim
207243830Sdim  SourceRange getCorrectionRange() const {
208243830Sdim    return CorrectionRange;
209243830Sdim  }
210243830Sdim
211263508Sdim  typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
212226633Sdim  decl_iterator begin() {
213226633Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
214226633Sdim  }
215226633Sdim  decl_iterator end() { return CorrectionDecls.end(); }
216263508Sdim  typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
217234353Sdim  const_decl_iterator begin() const {
218234353Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
219234353Sdim  }
220234353Sdim  const_decl_iterator end() const { return CorrectionDecls.end(); }
221226633Sdim
222263508Sdim  /// \brief Returns whether this typo correction is correcting to a
223263508Sdim  /// declaration that was declared in a module that has not been imported.
224263508Sdim  bool requiresImport() const { return RequiresImport; }
225263508Sdim  void setRequiresImport(bool Req) { RequiresImport = Req; }
226263508Sdim
227224135Sdimprivate:
228226633Sdim  bool hasCorrectionDecl() const {
229226633Sdim    return (!isKeyword() && !CorrectionDecls.empty());
230226633Sdim  }
231226633Sdim
232224135Sdim  // Results.
233224135Sdim  DeclarationName CorrectionName;
234224135Sdim  NestedNameSpecifier *CorrectionNameSpec;
235249423Sdim  SmallVector<NamedDecl *, 1> CorrectionDecls;
236234353Sdim  unsigned CharDistance;
237234353Sdim  unsigned QualifierDistance;
238234353Sdim  unsigned CallbackDistance;
239243830Sdim  SourceRange CorrectionRange;
240263508Sdim  bool ForceSpecifierReplacement;
241263508Sdim  bool RequiresImport;
242224135Sdim};
243224135Sdim
244234353Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check
245234353Sdim/// the validity of a potential typo correction.
246234353Sdimclass CorrectionCandidateCallback {
247263508Sdimpublic:
248234353Sdim  static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
249234353Sdim
250234353Sdim  CorrectionCandidateCallback()
251234353Sdim      : WantTypeSpecifiers(true), WantExpressionKeywords(true),
252234353Sdim        WantCXXNamedCasts(true), WantRemainingKeywords(true),
253234353Sdim        WantObjCSuper(false),
254234353Sdim        IsObjCIvarLookup(false) {}
255234353Sdim
256234353Sdim  virtual ~CorrectionCandidateCallback() {}
257234353Sdim
258234353Sdim  /// \brief Simple predicate used by the default RankCandidate to
259234353Sdim  /// determine whether to return an edit distance of 0 or InvalidDistance.
260234353Sdim  /// This can be overrided by validators that only need to determine if a
261234353Sdim  /// candidate is viable, without ranking potentially viable candidates.
262234353Sdim  /// Only ValidateCandidate or RankCandidate need to be overriden by a
263234353Sdim  /// callback wishing to check the viability of correction candidates.
264249423Sdim  /// The default predicate always returns true if the candidate is not a type
265249423Sdim  /// name or keyword, true for types if WantTypeSpecifiers is true, and true
266249423Sdim  /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
267249423Sdim  /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
268249423Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate);
269234353Sdim
270234353Sdim  /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
271234353Sdim  /// to a candidate (where a lower value represents a better candidate), or
272234353Sdim  /// returning InvalidDistance if the candidate is not at all viable. For
273234353Sdim  /// validation callbacks that only need to determine if a candidate is viable,
274234353Sdim  /// the default RankCandidate returns either 0 or InvalidDistance depending
275234353Sdim  /// whether ValidateCandidate returns true or false.
276234353Sdim  virtual unsigned RankCandidate(const TypoCorrection &candidate) {
277234353Sdim    return ValidateCandidate(candidate) ? 0 : InvalidDistance;
278234353Sdim  }
279234353Sdim
280234353Sdim  // Flags for context-dependent keywords.
281234353Sdim  // TODO: Expand these to apply to non-keywords or possibly remove them.
282234353Sdim  bool WantTypeSpecifiers;
283234353Sdim  bool WantExpressionKeywords;
284234353Sdim  bool WantCXXNamedCasts;
285234353Sdim  bool WantRemainingKeywords;
286234353Sdim  bool WantObjCSuper;
287234353Sdim  // Temporary hack for the one case where a CorrectTypoContext enum is used
288234353Sdim  // when looking up results.
289234353Sdim  bool IsObjCIvarLookup;
290234353Sdim};
291234353Sdim
292234353Sdim/// @brief Simple template class for restricting typo correction candidates
293234353Sdim/// to ones having a single Decl* of the given type.
294234353Sdimtemplate <class C>
295234353Sdimclass DeclFilterCCC : public CorrectionCandidateCallback {
296263508Sdimpublic:
297234353Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
298234353Sdim    return candidate.getCorrectionDeclAs<C>();
299234353Sdim  }
300234353Sdim};
301234353Sdim
302263508Sdim// @brief Callback class to limit the allowed keywords and to only accept typo
303263508Sdim// corrections that are keywords or whose decls refer to functions (or template
304263508Sdim// functions) that accept the given number of arguments.
305263508Sdimclass FunctionCallFilterCCC : public CorrectionCandidateCallback {
306263508Sdimpublic:
307263508Sdim  FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
308263508Sdim                        bool HasExplicitTemplateArgs);
309263508Sdim
310263508Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate);
311263508Sdim
312263508Sdim private:
313263508Sdim  unsigned NumArgs;
314263508Sdim  bool HasExplicitTemplateArgs;
315263508Sdim};
316263508Sdim
317263508Sdim// @brief Callback class that effectively disabled typo correction
318263508Sdimclass NoTypoCorrectionCCC : public CorrectionCandidateCallback {
319263508Sdimpublic:
320263508Sdim  NoTypoCorrectionCCC() {
321263508Sdim    WantTypeSpecifiers = false;
322263508Sdim    WantExpressionKeywords = false;
323263508Sdim    WantCXXNamedCasts = false;
324263508Sdim    WantRemainingKeywords = false;
325263508Sdim  }
326263508Sdim
327263508Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
328263508Sdim    return false;
329263508Sdim  }
330263508Sdim};
331263508Sdim
332224135Sdim}
333224135Sdim
334224135Sdim#endif
335