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"
19252723Sdim#include "clang/Sema/DeclSpec.h"
20226890Sdim#include "llvm/ADT/SmallVector.h"
21224135Sdim
22224135Sdimnamespace clang {
23224135Sdim
24224135Sdim/// @brief Simple class containing the result of Sema::CorrectTypo
25224135Sdimclass TypoCorrection {
26224135Sdimpublic:
27235633Sdim  // "Distance" for unusable corrections
28235633Sdim  static const unsigned InvalidDistance = ~0U;
29235633Sdim  // The largest distance still considered valid (larger edit distances are
30235633Sdim  // mapped to InvalidDistance by getEditDistance).
31235633Sdim  static const unsigned MaximumDistance = 10000U;
32235633Sdim
33235633Sdim  // Relative weightings of the "edit distance" components. The higher the
34235633Sdim  // weight, the more of a penalty to fitness the component will give (higher
35235633Sdim  // weights mean greater contribution to the total edit distance, with the
36235633Sdim  // best correction candidates having the lowest edit distance).
37235633Sdim  static const unsigned CharDistanceWeight = 100U;
38235633Sdim  static const unsigned QualifierDistanceWeight = 110U;
39235633Sdim  static const unsigned CallbackDistanceWeight = 150U;
40235633Sdim
41224135Sdim  TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
42263509Sdim                 NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0,
43263509Sdim                 unsigned QualifierDistance = 0)
44235633Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
45263509Sdim        CharDistance(CharDistance), QualifierDistance(QualifierDistance),
46263509Sdim        CallbackDistance(0), ForceSpecifierReplacement(false),
47263509Sdim        RequiresImport(false) {
48226890Sdim    if (NameDecl)
49226890Sdim      CorrectionDecls.push_back(NameDecl);
50226890Sdim  }
51224135Sdim
52263509Sdim  TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
53263509Sdim                 unsigned CharDistance = 0)
54235633Sdim      : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
55263509Sdim        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
56263509Sdim        ForceSpecifierReplacement(false), RequiresImport(false) {
57226890Sdim    if (Name)
58226890Sdim      CorrectionDecls.push_back(Name);
59226890Sdim  }
60224135Sdim
61263509Sdim  TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
62263509Sdim                 unsigned CharDistance = 0)
63235633Sdim      : CorrectionName(Name), CorrectionNameSpec(NNS),
64263509Sdim        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
65263509Sdim        ForceSpecifierReplacement(false), RequiresImport(false) {}
66224135Sdim
67224135Sdim  TypoCorrection()
68235633Sdim      : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
69263509Sdim        CallbackDistance(0), ForceSpecifierReplacement(false),
70263509Sdim        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;
84263509Sdim    ForceSpecifierReplacement = (NNS != 0);
85224135Sdim  }
86224135Sdim
87263509Sdim  void WillReplaceSpecifier(bool ForceReplacement) {
88263509Sdim    ForceSpecifierReplacement = ForceReplacement;
89263509Sdim  }
90263509Sdim
91263509Sdim  bool WillReplaceSpecifier() const {
92263509Sdim    return ForceSpecifierReplacement;
93263509Sdim  }
94263509Sdim
95235633Sdim  void setQualifierDistance(unsigned ED) {
96235633Sdim    QualifierDistance = ED;
97235633Sdim  }
98224135Sdim
99235633Sdim  void setCallbackDistance(unsigned ED) {
100235633Sdim    CallbackDistance = ED;
101235633Sdim  }
102235633Sdim
103235633Sdim  // Convert the given weighted edit distance to a roughly equivalent number of
104235633Sdim  // single-character edits (typically for comparison to the length of the
105235633Sdim  // string being edited).
106235633Sdim  static unsigned NormalizeEditDistance(unsigned ED) {
107235633Sdim    if (ED > MaximumDistance)
108235633Sdim      return InvalidDistance;
109235633Sdim    return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
110235633Sdim  }
111235633Sdim
112235633Sdim  /// \brief Gets the "edit distance" of the typo correction from the typo.
113235633Sdim  /// If Normalized is true, scale the distance down by the CharDistanceWeight
114235633Sdim  /// to return the edit distance in terms of single-character edits.
115235633Sdim  unsigned getEditDistance(bool Normalized = true) const {
116235633Sdim    if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
117235633Sdim        CallbackDistance > MaximumDistance)
118235633Sdim      return InvalidDistance;
119235633Sdim    unsigned ED =
120235633Sdim        CharDistance * CharDistanceWeight +
121235633Sdim        QualifierDistance * QualifierDistanceWeight +
122235633Sdim        CallbackDistance * CallbackDistanceWeight;
123235633Sdim    if (ED > MaximumDistance)
124235633Sdim      return InvalidDistance;
125235633Sdim    // Half the CharDistanceWeight is added to ED to simulate rounding since
126235633Sdim    // integer division truncates the value (i.e. round-to-nearest-int instead
127235633Sdim    // of round-to-zero).
128235633Sdim    return Normalized ? NormalizeEditDistance(ED) : ED;
129235633Sdim  }
130235633Sdim
131224135Sdim  /// \brief Gets the pointer to the declaration of the typo correction
132263509Sdim  NamedDecl *getCorrectionDecl() const {
133226890Sdim    return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
134224135Sdim  }
135224135Sdim  template <class DeclClass>
136224135Sdim  DeclClass *getCorrectionDeclAs() const {
137224135Sdim    return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
138224135Sdim  }
139263509Sdim
140263509Sdim  /// \brief Clears the list of NamedDecls.
141263509Sdim  void ClearCorrectionDecls() {
142263509Sdim    CorrectionDecls.clear();
143263509Sdim  }
144263509Sdim
145226890Sdim  /// \brief Clears the list of NamedDecls before adding the new one.
146224135Sdim  void setCorrectionDecl(NamedDecl *CDecl) {
147226890Sdim    CorrectionDecls.clear();
148226890Sdim    addCorrectionDecl(CDecl);
149224135Sdim  }
150224135Sdim
151263509Sdim  /// \brief Clears the list of NamedDecls and adds the given set.
152263509Sdim  void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
153263509Sdim    CorrectionDecls.clear();
154263509Sdim    CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
155263509Sdim  }
156263509Sdim
157226890Sdim  /// \brief Add the given NamedDecl to the list of NamedDecls that are the
158226890Sdim  /// declarations associated with the DeclarationName of this TypoCorrection
159226890Sdim  void addCorrectionDecl(NamedDecl *CDecl);
160226890Sdim
161224135Sdim  std::string getAsString(const LangOptions &LO) const;
162224135Sdim  std::string getQuoted(const LangOptions &LO) const {
163224135Sdim    return "'" + getAsString(LO) + "'";
164224135Sdim  }
165224135Sdim
166226890Sdim  /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
167263509Sdim  LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
168224135Sdim
169226890Sdim  /// \brief Mark this TypoCorrection as being a keyword.
170226890Sdim  /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
171226890Sdim  /// added to the list of the correction's NamedDecl pointers, NULL is added
172226890Sdim  /// as the only element in the list to mark this TypoCorrection as a keyword.
173226890Sdim  void makeKeyword() {
174226890Sdim    CorrectionDecls.clear();
175226890Sdim    CorrectionDecls.push_back(0);
176263509Sdim    ForceSpecifierReplacement = true;
177226890Sdim  }
178224135Sdim
179226890Sdim  // Check if this TypoCorrection is a keyword by checking if the first
180226890Sdim  // item in CorrectionDecls is NULL.
181226890Sdim  bool isKeyword() const {
182226890Sdim    return !CorrectionDecls.empty() &&
183226890Sdim        CorrectionDecls.front() == 0;
184226890Sdim  }
185226890Sdim
186235633Sdim  // Check if this TypoCorrection is the given keyword.
187235633Sdim  template<std::size_t StrLen>
188235633Sdim  bool isKeyword(const char (&Str)[StrLen]) const {
189235633Sdim    return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
190235633Sdim  }
191235633Sdim
192224135Sdim  // Returns true if the correction either is a keyword or has a known decl.
193226890Sdim  bool isResolved() const { return !CorrectionDecls.empty(); }
194224135Sdim
195226890Sdim  bool isOverloaded() const {
196226890Sdim    return CorrectionDecls.size() > 1;
197226890Sdim  }
198226890Sdim
199263509Sdim  void setCorrectionRange(CXXScopeSpec *SS,
200245431Sdim                          const DeclarationNameInfo &TypoName) {
201263509Sdim    CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
202263509Sdim                                 ? SS->getBeginLoc()
203263509Sdim                                 : TypoName.getLoc());
204245431Sdim    CorrectionRange.setEnd(TypoName.getLoc());
205245431Sdim  }
206245431Sdim
207245431Sdim  SourceRange getCorrectionRange() const {
208245431Sdim    return CorrectionRange;
209245431Sdim  }
210245431Sdim
211263509Sdim  typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
212226890Sdim  decl_iterator begin() {
213226890Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
214226890Sdim  }
215226890Sdim  decl_iterator end() { return CorrectionDecls.end(); }
216263509Sdim  typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
217235633Sdim  const_decl_iterator begin() const {
218235633Sdim    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
219235633Sdim  }
220235633Sdim  const_decl_iterator end() const { return CorrectionDecls.end(); }
221226890Sdim
222263509Sdim  /// \brief Returns whether this typo correction is correcting to a
223263509Sdim  /// declaration that was declared in a module that has not been imported.
224263509Sdim  bool requiresImport() const { return RequiresImport; }
225263509Sdim  void setRequiresImport(bool Req) { RequiresImport = Req; }
226263509Sdim
227224135Sdimprivate:
228226890Sdim  bool hasCorrectionDecl() const {
229226890Sdim    return (!isKeyword() && !CorrectionDecls.empty());
230226890Sdim  }
231226890Sdim
232224135Sdim  // Results.
233224135Sdim  DeclarationName CorrectionName;
234224135Sdim  NestedNameSpecifier *CorrectionNameSpec;
235252723Sdim  SmallVector<NamedDecl *, 1> CorrectionDecls;
236235633Sdim  unsigned CharDistance;
237235633Sdim  unsigned QualifierDistance;
238235633Sdim  unsigned CallbackDistance;
239245431Sdim  SourceRange CorrectionRange;
240263509Sdim  bool ForceSpecifierReplacement;
241263509Sdim  bool RequiresImport;
242224135Sdim};
243224135Sdim
244235633Sdim/// @brief Base class for callback objects used by Sema::CorrectTypo to check
245235633Sdim/// the validity of a potential typo correction.
246235633Sdimclass CorrectionCandidateCallback {
247263509Sdimpublic:
248235633Sdim  static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
249235633Sdim
250235633Sdim  CorrectionCandidateCallback()
251235633Sdim      : WantTypeSpecifiers(true), WantExpressionKeywords(true),
252235633Sdim        WantCXXNamedCasts(true), WantRemainingKeywords(true),
253235633Sdim        WantObjCSuper(false),
254235633Sdim        IsObjCIvarLookup(false) {}
255235633Sdim
256235633Sdim  virtual ~CorrectionCandidateCallback() {}
257235633Sdim
258235633Sdim  /// \brief Simple predicate used by the default RankCandidate to
259235633Sdim  /// determine whether to return an edit distance of 0 or InvalidDistance.
260235633Sdim  /// This can be overrided by validators that only need to determine if a
261235633Sdim  /// candidate is viable, without ranking potentially viable candidates.
262235633Sdim  /// Only ValidateCandidate or RankCandidate need to be overriden by a
263235633Sdim  /// callback wishing to check the viability of correction candidates.
264252723Sdim  /// The default predicate always returns true if the candidate is not a type
265252723Sdim  /// name or keyword, true for types if WantTypeSpecifiers is true, and true
266252723Sdim  /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
267252723Sdim  /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
268252723Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate);
269235633Sdim
270235633Sdim  /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
271235633Sdim  /// to a candidate (where a lower value represents a better candidate), or
272235633Sdim  /// returning InvalidDistance if the candidate is not at all viable. For
273235633Sdim  /// validation callbacks that only need to determine if a candidate is viable,
274235633Sdim  /// the default RankCandidate returns either 0 or InvalidDistance depending
275235633Sdim  /// whether ValidateCandidate returns true or false.
276235633Sdim  virtual unsigned RankCandidate(const TypoCorrection &candidate) {
277235633Sdim    return ValidateCandidate(candidate) ? 0 : InvalidDistance;
278235633Sdim  }
279235633Sdim
280235633Sdim  // Flags for context-dependent keywords.
281235633Sdim  // TODO: Expand these to apply to non-keywords or possibly remove them.
282235633Sdim  bool WantTypeSpecifiers;
283235633Sdim  bool WantExpressionKeywords;
284235633Sdim  bool WantCXXNamedCasts;
285235633Sdim  bool WantRemainingKeywords;
286235633Sdim  bool WantObjCSuper;
287235633Sdim  // Temporary hack for the one case where a CorrectTypoContext enum is used
288235633Sdim  // when looking up results.
289235633Sdim  bool IsObjCIvarLookup;
290235633Sdim};
291235633Sdim
292235633Sdim/// @brief Simple template class for restricting typo correction candidates
293235633Sdim/// to ones having a single Decl* of the given type.
294235633Sdimtemplate <class C>
295235633Sdimclass DeclFilterCCC : public CorrectionCandidateCallback {
296263509Sdimpublic:
297235633Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
298235633Sdim    return candidate.getCorrectionDeclAs<C>();
299235633Sdim  }
300235633Sdim};
301235633Sdim
302263509Sdim// @brief Callback class to limit the allowed keywords and to only accept typo
303263509Sdim// corrections that are keywords or whose decls refer to functions (or template
304263509Sdim// functions) that accept the given number of arguments.
305263509Sdimclass FunctionCallFilterCCC : public CorrectionCandidateCallback {
306263509Sdimpublic:
307263509Sdim  FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
308263509Sdim                        bool HasExplicitTemplateArgs);
309263509Sdim
310263509Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate);
311263509Sdim
312263509Sdim private:
313263509Sdim  unsigned NumArgs;
314263509Sdim  bool HasExplicitTemplateArgs;
315263509Sdim};
316263509Sdim
317263509Sdim// @brief Callback class that effectively disabled typo correction
318263509Sdimclass NoTypoCorrectionCCC : public CorrectionCandidateCallback {
319263509Sdimpublic:
320263509Sdim  NoTypoCorrectionCCC() {
321263509Sdim    WantTypeSpecifiers = false;
322263509Sdim    WantExpressionKeywords = false;
323263509Sdim    WantCXXNamedCasts = false;
324263509Sdim    WantRemainingKeywords = false;
325263509Sdim  }
326263509Sdim
327263509Sdim  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
328263509Sdim    return false;
329263509Sdim  }
330263509Sdim};
331263509Sdim
332224135Sdim}
333224135Sdim
334224135Sdim#endif
335