1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H |
15 | #define LLVM_CLANG_SEMA_TYPOCORRECTION_H |
16 | |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/DeclarationName.h" |
19 | #include "clang/Basic/LLVM.h" |
20 | #include "clang/Basic/PartialDiagnostic.h" |
21 | #include "clang/Basic/SourceLocation.h" |
22 | #include "clang/Sema/DeclSpec.h" |
23 | #include "llvm/ADT/ArrayRef.h" |
24 | #include "llvm/ADT/SmallVector.h" |
25 | #include "llvm/Support/Casting.h" |
26 | #include <cstddef> |
27 | #include <limits> |
28 | #include <string> |
29 | #include <utility> |
30 | #include <vector> |
31 | |
32 | namespace clang { |
33 | |
34 | class DeclContext; |
35 | class IdentifierInfo; |
36 | class LangOptions; |
37 | class MemberExpr; |
38 | class NestedNameSpecifier; |
39 | class Sema; |
40 | |
41 | |
42 | class TypoCorrection { |
43 | public: |
44 | |
45 | static const unsigned InvalidDistance = std::numeric_limits<unsigned>::max(); |
46 | |
47 | |
48 | |
49 | static const unsigned MaximumDistance = 10000U; |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | static const unsigned CharDistanceWeight = 100U; |
56 | static const unsigned QualifierDistanceWeight = 110U; |
57 | static const unsigned CallbackDistanceWeight = 150U; |
58 | |
59 | TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, |
60 | NestedNameSpecifier *NNS = nullptr, unsigned CharDistance = 0, |
61 | unsigned QualifierDistance = 0) |
62 | : CorrectionName(Name), CorrectionNameSpec(NNS), |
63 | CharDistance(CharDistance), QualifierDistance(QualifierDistance) { |
64 | if (NameDecl) |
65 | CorrectionDecls.push_back(NameDecl); |
66 | } |
67 | |
68 | TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS = nullptr, |
69 | unsigned CharDistance = 0) |
70 | : CorrectionName(Name->getDeclName()), CorrectionNameSpec(NNS), |
71 | CharDistance(CharDistance) { |
72 | if (Name) |
73 | CorrectionDecls.push_back(Name); |
74 | } |
75 | |
76 | TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS = nullptr, |
77 | unsigned CharDistance = 0) |
78 | : CorrectionName(Name), CorrectionNameSpec(NNS), |
79 | CharDistance(CharDistance) {} |
80 | |
81 | TypoCorrection() = default; |
82 | |
83 | |
84 | DeclarationName getCorrection() const { return CorrectionName; } |
85 | |
86 | IdentifierInfo *getCorrectionAsIdentifierInfo() const { |
87 | return CorrectionName.getAsIdentifierInfo(); |
88 | } |
89 | |
90 | |
91 | NestedNameSpecifier *getCorrectionSpecifier() const { |
92 | return CorrectionNameSpec; |
93 | } |
94 | |
95 | void setCorrectionSpecifier(NestedNameSpecifier *NNS) { |
96 | CorrectionNameSpec = NNS; |
97 | ForceSpecifierReplacement = (NNS != nullptr); |
98 | } |
99 | |
100 | void WillReplaceSpecifier(bool ForceReplacement) { |
101 | ForceSpecifierReplacement = ForceReplacement; |
102 | } |
103 | |
104 | bool WillReplaceSpecifier() const { |
105 | return ForceSpecifierReplacement; |
106 | } |
107 | |
108 | void setQualifierDistance(unsigned ED) { |
109 | QualifierDistance = ED; |
110 | } |
111 | |
112 | void setCallbackDistance(unsigned ED) { |
113 | CallbackDistance = ED; |
114 | } |
115 | |
116 | |
117 | |
118 | |
119 | static unsigned NormalizeEditDistance(unsigned ED) { |
120 | if (ED > MaximumDistance) |
121 | return InvalidDistance; |
122 | return (ED + CharDistanceWeight / 2) / CharDistanceWeight; |
123 | } |
124 | |
125 | |
126 | |
127 | |
128 | unsigned getEditDistance(bool Normalized = true) const { |
129 | if (CharDistance > MaximumDistance || QualifierDistance > MaximumDistance || |
130 | CallbackDistance > MaximumDistance) |
131 | return InvalidDistance; |
132 | unsigned ED = |
133 | CharDistance * CharDistanceWeight + |
134 | QualifierDistance * QualifierDistanceWeight + |
135 | CallbackDistance * CallbackDistanceWeight; |
136 | if (ED > MaximumDistance) |
137 | return InvalidDistance; |
138 | |
139 | |
140 | |
141 | return Normalized ? NormalizeEditDistance(ED) : ED; |
142 | } |
143 | |
144 | |
145 | |
146 | NamedDecl *getFoundDecl() const { |
147 | return hasCorrectionDecl() ? *(CorrectionDecls.begin()) : nullptr; |
148 | } |
149 | |
150 | |
151 | NamedDecl *getCorrectionDecl() const { |
152 | auto *D = getFoundDecl(); |
153 | return D ? D->getUnderlyingDecl() : nullptr; |
154 | } |
155 | template <class DeclClass> |
156 | DeclClass *getCorrectionDeclAs() const { |
157 | return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); |
158 | } |
159 | |
160 | |
161 | void ClearCorrectionDecls() { |
162 | CorrectionDecls.clear(); |
163 | } |
164 | |
165 | |
166 | void setCorrectionDecl(NamedDecl *CDecl) { |
167 | CorrectionDecls.clear(); |
168 | addCorrectionDecl(CDecl); |
169 | } |
170 | |
171 | |
172 | void setCorrectionDecls(ArrayRef<NamedDecl*> Decls) { |
173 | CorrectionDecls.clear(); |
174 | CorrectionDecls.insert(CorrectionDecls.begin(), Decls.begin(), Decls.end()); |
175 | } |
176 | |
177 | |
178 | |
179 | void addCorrectionDecl(NamedDecl *CDecl); |
180 | |
181 | std::string getAsString(const LangOptions &LO) const; |
182 | |
183 | std::string getQuoted(const LangOptions &LO) const { |
184 | return "'" + getAsString(LO) + "'"; |
185 | } |
186 | |
187 | |
188 | explicit operator bool() const { return bool(CorrectionName); } |
189 | |
190 | |
191 | |
192 | |
193 | |
194 | void makeKeyword() { |
195 | CorrectionDecls.clear(); |
196 | CorrectionDecls.push_back(nullptr); |
197 | ForceSpecifierReplacement = true; |
198 | } |
199 | |
200 | |
201 | |
202 | bool isKeyword() const { |
203 | return !CorrectionDecls.empty() && CorrectionDecls.front() == nullptr; |
204 | } |
205 | |
206 | |
207 | template<std::size_t StrLen> |
208 | bool isKeyword(const char (&Str)[StrLen]) const { |
209 | return isKeyword() && getCorrectionAsIdentifierInfo()->isStr(Str); |
210 | } |
211 | |
212 | |
213 | bool isResolved() const { return !CorrectionDecls.empty(); } |
214 | |
215 | bool isOverloaded() const { |
216 | return CorrectionDecls.size() > 1; |
217 | } |
218 | |
219 | void setCorrectionRange(CXXScopeSpec *SS, |
220 | const DeclarationNameInfo &TypoName) { |
221 | CorrectionRange = TypoName.getSourceRange(); |
222 | if (ForceSpecifierReplacement && SS && !SS->isEmpty()) |
223 | CorrectionRange.setBegin(SS->getBeginLoc()); |
224 | } |
225 | |
226 | SourceRange getCorrectionRange() const { |
227 | return CorrectionRange; |
228 | } |
229 | |
230 | using decl_iterator = SmallVectorImpl<NamedDecl *>::iterator; |
231 | |
232 | decl_iterator begin() { |
233 | return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); |
234 | } |
235 | |
236 | decl_iterator end() { return CorrectionDecls.end(); } |
237 | |
238 | using const_decl_iterator = SmallVectorImpl<NamedDecl *>::const_iterator; |
239 | |
240 | const_decl_iterator begin() const { |
241 | return isKeyword() ? CorrectionDecls.end() : CorrectionDecls.begin(); |
242 | } |
243 | |
244 | const_decl_iterator end() const { return CorrectionDecls.end(); } |
245 | |
246 | |
247 | |
248 | bool requiresImport() const { return RequiresImport; } |
249 | void setRequiresImport(bool Req) { RequiresImport = Req; } |
250 | |
251 | |
252 | |
253 | void (PartialDiagnostic PD) { |
254 | ExtraDiagnostics.push_back(std::move(PD)); |
255 | } |
256 | ArrayRef<PartialDiagnostic> () const { |
257 | return ExtraDiagnostics; |
258 | } |
259 | |
260 | private: |
261 | bool hasCorrectionDecl() const { |
262 | return (!isKeyword() && !CorrectionDecls.empty()); |
263 | } |
264 | |
265 | |
266 | DeclarationName CorrectionName; |
267 | NestedNameSpecifier *CorrectionNameSpec = nullptr; |
268 | SmallVector<NamedDecl *, 1> CorrectionDecls; |
269 | unsigned CharDistance = 0; |
270 | unsigned QualifierDistance = 0; |
271 | unsigned CallbackDistance = 0; |
272 | SourceRange CorrectionRange; |
273 | bool ForceSpecifierReplacement = false; |
274 | bool RequiresImport = false; |
275 | |
276 | std::vector<PartialDiagnostic> ; |
277 | }; |
278 | |
279 | |
280 | |
281 | class CorrectionCandidateCallback { |
282 | public: |
283 | static const unsigned InvalidDistance = TypoCorrection::InvalidDistance; |
284 | |
285 | explicit CorrectionCandidateCallback(IdentifierInfo *Typo = nullptr, |
286 | NestedNameSpecifier *TypoNNS = nullptr) |
287 | : Typo(Typo), TypoNNS(TypoNNS) {} |
288 | |
289 | virtual ~CorrectionCandidateCallback() = default; |
290 | |
291 | |
292 | |
293 | |
294 | |
295 | |
296 | |
297 | |
298 | |
299 | |
300 | |
301 | virtual bool ValidateCandidate(const TypoCorrection &candidate); |
302 | |
303 | |
304 | |
305 | |
306 | |
307 | |
308 | |
309 | virtual unsigned RankCandidate(const TypoCorrection &candidate) { |
310 | return (!MatchesTypo(candidate) && ValidateCandidate(candidate)) |
311 | ? 0 |
312 | : InvalidDistance; |
313 | } |
314 | |
315 | |
316 | |
317 | |
318 | |
319 | |
320 | virtual std::unique_ptr<CorrectionCandidateCallback> clone() = 0; |
321 | |
322 | void setTypoName(IdentifierInfo *II) { Typo = II; } |
323 | void setTypoNNS(NestedNameSpecifier *NNS) { TypoNNS = NNS; } |
324 | |
325 | |
326 | |
327 | |
328 | bool WantTypeSpecifiers = true; |
329 | bool WantExpressionKeywords = true; |
330 | bool WantCXXNamedCasts = true; |
331 | bool WantFunctionLikeCasts = true; |
332 | bool WantRemainingKeywords = true; |
333 | bool WantObjCSuper = false; |
334 | |
335 | |
336 | bool IsObjCIvarLookup = false; |
337 | bool IsAddressOfOperand = false; |
338 | |
339 | protected: |
340 | bool MatchesTypo(const TypoCorrection &candidate) { |
341 | return Typo && candidate.isResolved() && !candidate.requiresImport() && |
342 | candidate.getCorrectionAsIdentifierInfo() == Typo && |
343 | |
344 | |
345 | candidate.getCorrectionSpecifier() == TypoNNS; |
346 | } |
347 | |
348 | IdentifierInfo *Typo; |
349 | NestedNameSpecifier *TypoNNS; |
350 | }; |
351 | |
352 | class DefaultFilterCCC final : public CorrectionCandidateCallback { |
353 | public: |
354 | explicit DefaultFilterCCC(IdentifierInfo *Typo = nullptr, |
355 | NestedNameSpecifier *TypoNNS = nullptr) |
356 | : CorrectionCandidateCallback(Typo, TypoNNS) {} |
357 | |
358 | std::unique_ptr<CorrectionCandidateCallback> clone() override { |
359 | return llvm::make_unique<DefaultFilterCCC>(*this); |
360 | } |
361 | }; |
362 | |
363 | |
364 | |
365 | template <class C> |
366 | class DeclFilterCCC final : public CorrectionCandidateCallback { |
367 | public: |
368 | bool ValidateCandidate(const TypoCorrection &candidate) override { |
369 | return candidate.getCorrectionDeclAs<C>(); |
370 | } |
371 | std::unique_ptr<CorrectionCandidateCallback> clone() override { |
372 | return llvm::make_unique<DeclFilterCCC>(*this); |
373 | } |
374 | }; |
375 | |
376 | |
377 | |
378 | |
379 | class FunctionCallFilterCCC : public CorrectionCandidateCallback { |
380 | public: |
381 | FunctionCallFilterCCC(Sema &SemaRef, unsigned NumArgs, |
382 | bool HasExplicitTemplateArgs, |
383 | MemberExpr *ME = nullptr); |
384 | |
385 | bool ValidateCandidate(const TypoCorrection &candidate) override; |
386 | std::unique_ptr<CorrectionCandidateCallback> clone() override { |
387 | return llvm::make_unique<FunctionCallFilterCCC>(*this); |
388 | } |
389 | |
390 | private: |
391 | unsigned NumArgs; |
392 | bool HasExplicitTemplateArgs; |
393 | DeclContext *CurContext; |
394 | MemberExpr *MemberFn; |
395 | }; |
396 | |
397 | |
398 | class NoTypoCorrectionCCC final : public CorrectionCandidateCallback { |
399 | public: |
400 | NoTypoCorrectionCCC() { |
401 | WantTypeSpecifiers = false; |
402 | WantExpressionKeywords = false; |
403 | WantCXXNamedCasts = false; |
404 | WantFunctionLikeCasts = false; |
405 | WantRemainingKeywords = false; |
406 | } |
407 | |
408 | bool ValidateCandidate(const TypoCorrection &candidate) override { |
409 | return false; |
410 | } |
411 | std::unique_ptr<CorrectionCandidateCallback> clone() override { |
412 | return llvm::make_unique<NoTypoCorrectionCCC>(*this); |
413 | } |
414 | }; |
415 | |
416 | } |
417 | |
418 | #endif |
419 | |