TypoCorrection.h revision 263508
1272343Sngie//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
2272343Sngie//
3272343Sngie//                     The LLVM Compiler Infrastructure
4272343Sngie//
5272343Sngie// This file is distributed under the University of Illinois Open Source
6272343Sngie// License. See LICENSE.TXT for details.
7272343Sngie//
8272343Sngie//===----------------------------------------------------------------------===//
9272343Sngie//
10272343Sngie// This file defines the TypoCorrection class, which stores the results of
11272343Sngie// Sema's typo correction (Sema::CorrectTypo).
12272343Sngie//
13272343Sngie//===----------------------------------------------------------------------===//
14272343Sngie
15272343Sngie#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
16272343Sngie#define LLVM_CLANG_SEMA_TYPOCORRECTION_H
17272343Sngie
18272343Sngie#include "clang/AST/DeclCXX.h"
19272343Sngie#include "clang/Sema/DeclSpec.h"
20272343Sngie#include "llvm/ADT/SmallVector.h"
21272343Sngie
22272343Sngienamespace clang {
23272343Sngie
24272343Sngie/// @brief Simple class containing the result of Sema::CorrectTypo
25272343Sngieclass TypoCorrection {
26272343Sngiepublic:
27272343Sngie  // "Distance" for unusable corrections
28272343Sngie  static const unsigned InvalidDistance = ~0U;
29272343Sngie  // The largest distance still considered valid (larger edit distances are
30272343Sngie  // mapped to InvalidDistance by getEditDistance).
31272343Sngie  static const unsigned MaximumDistance = 10000U;
32272343Sngie
33272343Sngie  // Relative weightings of the "edit distance" components. The higher the
34272343Sngie  // weight, the more of a penalty to fitness the component will give (higher
35272343Sngie  // weights mean greater contribution to the total edit distance, with the
36272343Sngie  // best correction candidates having the lowest edit distance).
37272343Sngie  static const unsigned CharDistanceWeight = 100U;
38272343Sngie  static const unsigned QualifierDistanceWeight = 110U;
39272343Sngie  static const unsigned CallbackDistanceWeight = 150U;
40272343Sngie
41272343Sngie  TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
42272343Sngie                 NestedNameSpecifier *NNS = 0, unsigned CharDistance = 0,
43272343Sngie                 unsigned QualifierDistance = 0)
44272343Sngie      : CorrectionName(Name), CorrectionNameSpec(NNS),
45272343Sngie        CharDistance(CharDistance), QualifierDistance(QualifierDistance),
46272343Sngie        CallbackDistance(0), ForceSpecifierReplacement(false),
47272343Sngie        RequiresImport(false) {
48272343Sngie    if (NameDecl)
49272343Sngie      CorrectionDecls.push_back(NameDecl);
50272343Sngie  }
51272343Sngie
52272343Sngie  TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = 0,
53272343Sngie                 unsigned CharDistance = 0)
54272343Sngie      : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS),
55272343Sngie        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
56272343Sngie        ForceSpecifierReplacement(false), RequiresImport(false) {
57272343Sngie    if (Name)
58272343Sngie      CorrectionDecls.push_back(Name);
59272343Sngie  }
60272343Sngie
61272343Sngie  TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = 0,
62272343Sngie                 unsigned CharDistance = 0)
63272343Sngie      : CorrectionName(Name), CorrectionNameSpec(NNS),
64272343Sngie        CharDistance(CharDistance), QualifierDistance(0), CallbackDistance(0),
65272343Sngie        ForceSpecifierReplacement(false), RequiresImport(false) {}
66272343Sngie
67272343Sngie  TypoCorrection()
68272343Sngie      : CorrectionNameSpec(0), CharDistance(0), QualifierDistance(0),
69272343Sngie        CallbackDistance(0), ForceSpecifierReplacement(false),
70272343Sngie        RequiresImport(false) {}
71272343Sngie
72272343Sngie  /// \brief Gets the DeclarationName of the typo correction
73272343Sngie  DeclarationName getCorrection() const { return CorrectionName; }
74272343Sngie  IdentifierInfo* getCorrectionAsIdentifierInfo() const {
75272343Sngie    return CorrectionName.getAsIdentifierInfo();
76272343Sngie  }
77272343Sngie
78272343Sngie  /// \brief Gets the NestedNameSpecifier needed to use the typo correction
79272343Sngie  NestedNameSpecifier* getCorrectionSpecifier() const {
80272343Sngie    return CorrectionNameSpec;
81272343Sngie  }
82272343Sngie  void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
83272343Sngie    CorrectionNameSpec = NNS;
84272343Sngie    ForceSpecifierReplacement = (NNS != 0);
85272343Sngie  }
86272343Sngie
87272343Sngie  void WillReplaceSpecifier(bool ForceReplacement) {
88272343Sngie    ForceSpecifierReplacement = ForceReplacement;
89272343Sngie  }
90272343Sngie
91272343Sngie  bool WillReplaceSpecifier() const {
92272343Sngie    return ForceSpecifierReplacement;
93272343Sngie  }
94272343Sngie
95272343Sngie  void setQualifierDistance(unsigned ED) {
96272343Sngie    QualifierDistance = ED;
97272343Sngie  }
98272343Sngie
99272343Sngie  void setCallbackDistance(unsigned ED) {
100272343Sngie    CallbackDistance = ED;
101272343Sngie  }
102272343Sngie
103272343Sngie  // Convert the given weighted edit distance to a roughly equivalent number of
104272343Sngie  // single-character edits (typically for comparison to the length of the
105272343Sngie  // string being edited).
106272343Sngie  static unsigned NormalizeEditDistance(unsigned ED) {
107272343Sngie    if (ED > MaximumDistance)
108272343Sngie      return InvalidDistance;
109272343Sngie    return (ED + CharDistanceWeight / 2) / CharDistanceWeight;
110272343Sngie  }
111272343Sngie
112272343Sngie  /// \brief Gets the "edit distance" of the typo correction from the typo.
113272343Sngie  /// If Normalized is true, scale the distance down by the CharDistanceWeight
114272343Sngie  /// to return the edit distance in terms of single-character edits.
115272343Sngie  unsigned getEditDistance(bool Normalized = true) const {
116272343Sngie    if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance ||
117272343Sngie        CallbackDistance > MaximumDistance)
118272343Sngie      return InvalidDistance;
119272343Sngie    unsigned ED =
120272343Sngie        CharDistance * CharDistanceWeight +
121272343Sngie        QualifierDistance * QualifierDistanceWeight +
122272343Sngie        CallbackDistance * CallbackDistanceWeight;
123272343Sngie    if (ED > MaximumDistance)
124272343Sngie      return InvalidDistance;
125272343Sngie    // Half the CharDistanceWeight is added to ED to simulate rounding since
126272343Sngie    // integer division truncates the value (i.e. round-to-nearest-int instead
127272343Sngie    // of round-to-zero).
128272343Sngie    return Normalized ? NormalizeEditDistance(ED) : ED;
129272343Sngie  }
130272343Sngie
131272343Sngie  /// \brief Gets the pointer to the declaration of the typo correction
132272343Sngie  NamedDecl *getCorrectionDecl() const {
133272343Sngie    return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : 0;
134272343Sngie  }
135272343Sngie  template <class DeclClass>
136272343Sngie  DeclClass *getCorrectionDeclAs() const {
137272343Sngie    return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
138272343Sngie  }
139272343Sngie
140272343Sngie  /// \brief Clears the list of NamedDecls.
141272343Sngie  void ClearCorrectionDecls() {
142272343Sngie    CorrectionDecls.clear();
143272343Sngie  }
144272343Sngie
145272343Sngie  /// \brief Clears the list of NamedDecls before adding the new one.
146272343Sngie  void setCorrectionDecl(NamedDecl *CDecl) {
147272343Sngie    CorrectionDecls.clear();
148272343Sngie    addCorrectionDecl(CDecl);
149272343Sngie  }
150272343Sngie
151272343Sngie  /// \brief Clears the list of NamedDecls and adds the given set.
152272343Sngie  void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) {
153272343Sngie    CorrectionDecls.clear();
154272343Sngie    CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end());
155272343Sngie  }
156272343Sngie
157272343Sngie  /// \brief Add the given NamedDecl to the list of NamedDecls that are the
158272343Sngie  /// declarations associated with the DeclarationName of this TypoCorrection
159272343Sngie  void addCorrectionDecl(NamedDecl *CDecl);
160272343Sngie
161272343Sngie  std::string getAsString(const LangOptions &LO) const;
162272343Sngie  std::string getQuoted(const LangOptions &LO) const {
163272343Sngie    return "'" + getAsString(LO) + "'";
164272343Sngie  }
165272343Sngie
166272343Sngie  /// \brief Returns whether this TypoCorrection has a non-empty DeclarationName
167272343Sngie  LLVM_EXPLICIT operator bool() const { return bool(CorrectionName); }
168272343Sngie
169272343Sngie  /// \brief Mark this TypoCorrection as being a keyword.
170272343Sngie  /// Since addCorrectionDeclsand setCorrectionDecl don't allow NULL to be
171272343Sngie  /// added to the list of the correction's NamedDecl pointers, NULL is added
172272343Sngie  /// as the only element in the list to mark this TypoCorrection as a keyword.
173272343Sngie  void makeKeyword() {
174272343Sngie    CorrectionDecls.clear();
175272343Sngie    CorrectionDecls.push_back(0);
176272343Sngie    ForceSpecifierReplacement = true;
177272343Sngie  }
178272343Sngie
179272343Sngie  // Check if this TypoCorrection is a keyword by checking if the first
180272343Sngie  // item in CorrectionDecls is NULL.
181272343Sngie  bool isKeyword() const {
182272343Sngie    return !CorrectionDecls.empty() &&
183272343Sngie        CorrectionDecls.front() == 0;
184272343Sngie  }
185272343Sngie
186272343Sngie  // Check if this TypoCorrection is the given keyword.
187272343Sngie  template<std::size_t StrLen>
188272343Sngie  bool isKeyword(const char (&Str)[StrLen]) const {
189272343Sngie    return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str);
190272343Sngie  }
191272343Sngie
192272343Sngie  // Returns true if the correction either is a keyword or has a known decl.
193272343Sngie  bool isResolved() const { return !CorrectionDecls.empty(); }
194272343Sngie
195272343Sngie  bool isOverloaded() const {
196272343Sngie    return CorrectionDecls.size() > 1;
197272343Sngie  }
198272343Sngie
199272343Sngie  void setCorrectionRange(CXXScopeSpec *SS,
200272343Sngie                          const DeclarationNameInfo &TypoName) {
201272343Sngie    CorrectionRange.setBegin(ForceSpecifierReplacement && SS && !SS->isEmpty()
202272343Sngie                                 ? SS->getBeginLoc()
203272343Sngie                                 : TypoName.getLoc());
204272343Sngie    CorrectionRange.setEnd(TypoName.getLoc());
205272343Sngie  }
206272343Sngie
207272343Sngie  SourceRange getCorrectionRange() const {
208272343Sngie    return CorrectionRange;
209272343Sngie  }
210272343Sngie
211272343Sngie  typedef SmallVectorImpl<NamedDecl *>::iterator decl_iterator;
212272343Sngie  decl_iterator begin() {
213272343Sngie    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
214272343Sngie  }
215272343Sngie  decl_iterator end() { return CorrectionDecls.end(); }
216272343Sngie  typedef SmallVectorImpl<NamedDecl *>::const_iterator const_decl_iterator;
217272343Sngie  const_decl_iterator begin() const {
218272343Sngie    return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin();
219272343Sngie  }
220272343Sngie  const_decl_iterator end() const { return CorrectionDecls.end(); }
221272343Sngie
222272343Sngie  /// \brief Returns whether this typo correction is correcting to a
223272343Sngie  /// declaration that was declared in a module that has not been imported.
224272343Sngie  bool requiresImport() const { return RequiresImport; }
225272343Sngie  void setRequiresImport(bool Req) { RequiresImport = Req; }
226272343Sngie
227272343Sngieprivate:
228272343Sngie  bool hasCorrectionDecl() const {
229272343Sngie    return (!isKeyword() && !CorrectionDecls.empty());
230272343Sngie  }
231272343Sngie
232272343Sngie  // Results.
233272343Sngie  DeclarationName CorrectionName;
234272343Sngie  NestedNameSpecifier *CorrectionNameSpec;
235272343Sngie  SmallVector<NamedDecl *, 1> CorrectionDecls;
236272343Sngie  unsigned CharDistance;
237272343Sngie  unsigned QualifierDistance;
238272343Sngie  unsigned CallbackDistance;
239272343Sngie  SourceRange CorrectionRange;
240272343Sngie  bool ForceSpecifierReplacement;
241272343Sngie  bool RequiresImport;
242272343Sngie};
243272343Sngie
244272343Sngie/// @brief Base class for callback objects used by Sema::CorrectTypo to check
245272343Sngie/// the validity of a potential typo correction.
246272343Sngieclass CorrectionCandidateCallback {
247272343Sngiepublic:
248272343Sngie  static const unsigned InvalidDistance = TypoCorrection::InvalidDistance;
249272343Sngie
250272343Sngie  CorrectionCandidateCallback()
251272343Sngie      : WantTypeSpecifiers(true), WantExpressionKeywords(true),
252272343Sngie        WantCXXNamedCasts(true), WantRemainingKeywords(true),
253272343Sngie        WantObjCSuper(false),
254272343Sngie        IsObjCIvarLookup(false) {}
255272343Sngie
256272343Sngie  virtual ~CorrectionCandidateCallback() {}
257272343Sngie
258272343Sngie  /// \brief Simple predicate used by the default RankCandidate to
259272343Sngie  /// determine whether to return an edit distance of 0 or InvalidDistance.
260272343Sngie  /// This can be overrided by validators that only need to determine if a
261272343Sngie  /// candidate is viable, without ranking potentially viable candidates.
262272343Sngie  /// Only ValidateCandidate or RankCandidate need to be overriden by a
263272343Sngie  /// callback wishing to check the viability of correction candidates.
264272343Sngie  /// The default predicate always returns true if the candidate is not a type
265272343Sngie  /// name or keyword, true for types if WantTypeSpecifiers is true, and true
266272343Sngie  /// for keywords if WantTypeSpecifiers, WantExpressionKeywords,
267272343Sngie  /// WantCXXNamedCasts, WantRemainingKeywords, or WantObjCSuper is true.
268272343Sngie  virtual bool ValidateCandidate(const TypoCorrection &candidate);
269272343Sngie
270272343Sngie  /// \brief Method used by Sema::CorrectTypo to assign an "edit distance" rank
271272343Sngie  /// to a candidate (where a lower value represents a better candidate), or
272272343Sngie  /// returning InvalidDistance if the candidate is not at all viable. For
273272343Sngie  /// validation callbacks that only need to determine if a candidate is viable,
274272343Sngie  /// the default RankCandidate returns either 0 or InvalidDistance depending
275272343Sngie  /// whether ValidateCandidate returns true or false.
276272343Sngie  virtual unsigned RankCandidate(const TypoCorrection &candidate) {
277272343Sngie    return ValidateCandidate(candidate) ? 0 : InvalidDistance;
278272343Sngie  }
279272343Sngie
280272343Sngie  // Flags for context-dependent keywords.
281272343Sngie  // TODO: Expand these to apply to non-keywords or possibly remove them.
282272343Sngie  bool WantTypeSpecifiers;
283272343Sngie  bool WantExpressionKeywords;
284272343Sngie  bool WantCXXNamedCasts;
285272343Sngie  bool WantRemainingKeywords;
286272343Sngie  bool WantObjCSuper;
287272343Sngie  // Temporary hack for the one case where a CorrectTypoContext enum is used
288272343Sngie  // when looking up results.
289272343Sngie  bool IsObjCIvarLookup;
290272343Sngie};
291272343Sngie
292272343Sngie/// @brief Simple template class for restricting typo correction candidates
293272343Sngie/// to ones having a single Decl* of the given type.
294272343Sngietemplate <class C>
295272343Sngieclass DeclFilterCCC : public CorrectionCandidateCallback {
296272343Sngiepublic:
297272343Sngie  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
298272343Sngie    return candidate.getCorrectionDeclAs<C>();
299272343Sngie  }
300272343Sngie};
301272343Sngie
302272343Sngie// @brief Callback class to limit the allowed keywords and to only accept typo
303272343Sngie// corrections that are keywords or whose decls refer to functions (or template
304272343Sngie// functions) that accept the given number of arguments.
305272343Sngieclass FunctionCallFilterCCC : public CorrectionCandidateCallback {
306272343Sngiepublic:
307272343Sngie  FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs,
308272343Sngie                        bool HasExplicitTemplateArgs);
309272343Sngie
310272343Sngie  virtual bool ValidateCandidate(const TypoCorrection &candidate);
311272343Sngie
312272343Sngie private:
313272343Sngie  unsigned NumArgs;
314272343Sngie  bool HasExplicitTemplateArgs;
315272343Sngie};
316272343Sngie
317272343Sngie// @brief Callback class that effectively disabled typo correction
318272343Sngieclass NoTypoCorrectionCCC : public CorrectionCandidateCallback {
319272343Sngiepublic:
320272343Sngie  NoTypoCorrectionCCC() {
321272343Sngie    WantTypeSpecifiers = false;
322272343Sngie    WantExpressionKeywords = false;
323272343Sngie    WantCXXNamedCasts = false;
324272343Sngie    WantRemainingKeywords = false;
325272343Sngie  }
326272343Sngie
327272343Sngie  virtual bool ValidateCandidate(const TypoCorrection &candidate) {
328272343Sngie    return false;
329272343Sngie  }
330272343Sngie};
331272343Sngie
332272343Sngie}
333272343Sngie
334272343Sngie#endif
335272343Sngie