1 | //===--- RenamerClangTidyCheck.cpp - clang-tidy ---------------------------===// |
---|---|
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "RenamerClangTidyCheck.h" |
10 | #include "ASTUtils.h" |
11 | #include "clang/AST/CXXInheritance.h" |
12 | #include "clang/AST/RecursiveASTVisitor.h" |
13 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
14 | #include "clang/Basic/CharInfo.h" |
15 | #include "clang/Frontend/CompilerInstance.h" |
16 | #include "clang/Lex/PPCallbacks.h" |
17 | #include "clang/Lex/Preprocessor.h" |
18 | #include "llvm/ADT/DenseMapInfo.h" |
19 | #include "llvm/ADT/PointerIntPair.h" |
20 | #include <optional> |
21 | |
22 | #define DEBUG_TYPE "clang-tidy" |
23 | |
24 | using namespace clang::ast_matchers; |
25 | |
26 | namespace llvm { |
27 | |
28 | /// Specialization of DenseMapInfo to allow NamingCheckId objects in DenseMaps |
29 | template <> |
30 | struct DenseMapInfo<clang::tidy::RenamerClangTidyCheck::NamingCheckId> { |
31 | using NamingCheckId = clang::tidy::RenamerClangTidyCheck::NamingCheckId; |
32 | |
33 | static inline NamingCheckId getEmptyKey() { |
34 | return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(), "EMPTY"}; |
35 | } |
36 | |
37 | static inline NamingCheckId getTombstoneKey() { |
38 | return {DenseMapInfo<clang::SourceLocation>::getTombstoneKey(), |
39 | "TOMBSTONE"}; |
40 | } |
41 | |
42 | static unsigned getHashValue(NamingCheckId Val) { |
43 | assert(Val != getEmptyKey() && "Cannot hash the empty key!"); |
44 | assert(Val != getTombstoneKey() && "Cannot hash the tombstone key!"); |
45 | |
46 | return DenseMapInfo<clang::SourceLocation>::getHashValue(Loc: Val.first) + |
47 | DenseMapInfo<StringRef>::getHashValue(Val: Val.second); |
48 | } |
49 | |
50 | static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS) { |
51 | if (RHS == getEmptyKey()) |
52 | return LHS == getEmptyKey(); |
53 | if (RHS == getTombstoneKey()) |
54 | return LHS == getTombstoneKey(); |
55 | return LHS == RHS; |
56 | } |
57 | }; |
58 | |
59 | } // namespace llvm |
60 | |
61 | namespace clang::tidy { |
62 | |
63 | namespace { |
64 | |
65 | class NameLookup { |
66 | llvm::PointerIntPair<const NamedDecl *, 1, bool> Data; |
67 | |
68 | public: |
69 | explicit NameLookup(const NamedDecl *ND) : Data(ND, false) {} |
70 | explicit NameLookup(std::nullopt_t) : Data(nullptr, true) {} |
71 | explicit NameLookup(std::nullptr_t) : Data(nullptr, false) {} |
72 | NameLookup() : NameLookup(nullptr) {} |
73 | |
74 | bool hasMultipleResolutions() const { return Data.getInt(); } |
75 | const NamedDecl *getDecl() const { |
76 | assert(!hasMultipleResolutions() && "Found multiple decls"); |
77 | return Data.getPointer(); |
78 | } |
79 | operator bool() const { return !hasMultipleResolutions(); } |
80 | const NamedDecl *operator*() const { return getDecl(); } |
81 | }; |
82 | |
83 | } // namespace |
84 | |
85 | static const NamedDecl *findDecl(const RecordDecl &RecDecl, |
86 | StringRef DeclName) { |
87 | for (const Decl *D : RecDecl.decls()) { |
88 | if (const auto *ND = dyn_cast<NamedDecl>(D)) { |
89 | if (ND->getDeclName().isIdentifier() && ND->getName() == DeclName) |
90 | return ND; |
91 | } |
92 | } |
93 | return nullptr; |
94 | } |
95 | |
96 | /// Returns the function that \p Method is overridding. If There are none or |
97 | /// multiple overrides it returns nullptr. If the overridden function itself is |
98 | /// overridding then it will recurse up to find the first decl of the function. |
99 | static const CXXMethodDecl *getOverrideMethod(const CXXMethodDecl *Method) { |
100 | if (Method->size_overridden_methods() != 1) |
101 | return nullptr; |
102 | |
103 | while (true) { |
104 | Method = *Method->begin_overridden_methods(); |
105 | assert(Method && "Overridden method shouldn't be null"); |
106 | unsigned NumOverrides = Method->size_overridden_methods(); |
107 | if (NumOverrides == 0) |
108 | return Method; |
109 | if (NumOverrides > 1) |
110 | return nullptr; |
111 | } |
112 | } |
113 | |
114 | static bool hasNoName(const NamedDecl *Decl) { |
115 | return !Decl->getIdentifier() || Decl->getName().empty(); |
116 | } |
117 | |
118 | static const NamedDecl *getFailureForNamedDecl(const NamedDecl *ND) { |
119 | const auto *Canonical = cast<NamedDecl>(ND->getCanonicalDecl()); |
120 | if (Canonical != ND) |
121 | return Canonical; |
122 | |
123 | if (const auto *Method = dyn_cast<CXXMethodDecl>(Val: ND)) { |
124 | if (const CXXMethodDecl *Overridden = getOverrideMethod(Method)) |
125 | Canonical = cast<NamedDecl>(Val: Overridden->getCanonicalDecl()); |
126 | else if (const FunctionTemplateDecl *Primary = Method->getPrimaryTemplate()) |
127 | if (const FunctionDecl *TemplatedDecl = Primary->getTemplatedDecl()) |
128 | Canonical = cast<NamedDecl>(Val: TemplatedDecl->getCanonicalDecl()); |
129 | |
130 | if (Canonical != ND) |
131 | return Canonical; |
132 | } |
133 | |
134 | return ND; |
135 | } |
136 | |
137 | /// Returns a decl matching the \p DeclName in \p Parent or one of its base |
138 | /// classes. If \p AggressiveTemplateLookup is `true` then it will check |
139 | /// template dependent base classes as well. |
140 | /// If a matching decl is found in multiple base classes then it will return a |
141 | /// flag indicating the multiple resolutions. |
142 | static NameLookup findDeclInBases(const CXXRecordDecl &Parent, |
143 | StringRef DeclName, |
144 | bool AggressiveTemplateLookup) { |
145 | if (!Parent.hasDefinition()) |
146 | return NameLookup(nullptr); |
147 | if (const NamedDecl *InClassRef = findDecl(Parent, DeclName)) |
148 | return NameLookup(InClassRef); |
149 | const NamedDecl *Found = nullptr; |
150 | |
151 | for (CXXBaseSpecifier Base : Parent.bases()) { |
152 | const auto *Record = Base.getType()->getAsCXXRecordDecl(); |
153 | if (!Record && AggressiveTemplateLookup) { |
154 | if (const auto *TST = |
155 | Base.getType()->getAs<TemplateSpecializationType>()) { |
156 | if (const auto *TD = llvm::dyn_cast_or_null<ClassTemplateDecl>( |
157 | Val: TST->getTemplateName().getAsTemplateDecl())) |
158 | Record = TD->getTemplatedDecl(); |
159 | } |
160 | } |
161 | if (!Record) |
162 | continue; |
163 | if (auto Search = |
164 | findDeclInBases(Parent: *Record, DeclName, AggressiveTemplateLookup)) { |
165 | if (*Search) { |
166 | if (Found) |
167 | return NameLookup( |
168 | std::nullopt); // Multiple decls found in different base classes. |
169 | Found = *Search; |
170 | continue; |
171 | } |
172 | } else |
173 | return NameLookup(std::nullopt); // Propagate multiple resolution back up. |
174 | } |
175 | return NameLookup(Found); // If nullptr, decl wasn't found. |
176 | } |
177 | |
178 | namespace { |
179 | |
180 | /// Callback supplies macros to RenamerClangTidyCheck::checkMacro |
181 | class RenamerClangTidyCheckPPCallbacks : public PPCallbacks { |
182 | public: |
183 | RenamerClangTidyCheckPPCallbacks(const SourceManager &SM, |
184 | RenamerClangTidyCheck *Check) |
185 | : SM(SM), Check(Check) {} |
186 | |
187 | /// MacroDefined calls checkMacro for macros in the main file |
188 | void MacroDefined(const Token &MacroNameTok, |
189 | const MacroDirective *MD) override { |
190 | const MacroInfo *Info = MD->getMacroInfo(); |
191 | if (Info->isBuiltinMacro()) |
192 | return; |
193 | if (SM.isWrittenInBuiltinFile(Loc: MacroNameTok.getLocation())) |
194 | return; |
195 | if (SM.isWrittenInCommandLineFile(Loc: MacroNameTok.getLocation())) |
196 | return; |
197 | Check->checkMacro(MacroNameTok, MI: Info, SourceMgr: SM); |
198 | } |
199 | |
200 | /// MacroExpands calls expandMacro for macros in the main file |
201 | void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, |
202 | SourceRange /*Range*/, |
203 | const MacroArgs * /*Args*/) override { |
204 | Check->expandMacro(MacroNameTok, MI: MD.getMacroInfo(), SourceMgr: SM); |
205 | } |
206 | |
207 | private: |
208 | const SourceManager &SM; |
209 | RenamerClangTidyCheck *Check; |
210 | }; |
211 | |
212 | class RenamerClangTidyVisitor |
213 | : public RecursiveASTVisitor<RenamerClangTidyVisitor> { |
214 | public: |
215 | RenamerClangTidyVisitor(RenamerClangTidyCheck *Check, const SourceManager &SM, |
216 | bool AggressiveDependentMemberLookup) |
217 | : Check(Check), SM(SM), |
218 | AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {} |
219 | |
220 | bool shouldVisitTemplateInstantiations() const { return true; } |
221 | |
222 | bool shouldVisitImplicitCode() const { return false; } |
223 | |
224 | bool VisitCXXConstructorDecl(CXXConstructorDecl *Decl) { |
225 | if (Decl->isImplicit()) |
226 | return true; |
227 | Check->addUsage(Decl->getParent(), Decl->getNameInfo().getSourceRange(), |
228 | SM); |
229 | |
230 | for (const auto *Init : Decl->inits()) { |
231 | if (!Init->isWritten() || Init->isInClassMemberInitializer()) |
232 | continue; |
233 | if (const FieldDecl *FD = Init->getAnyMember()) |
234 | Check->addUsage(FD, SourceRange(Init->getMemberLocation()), SM); |
235 | // Note: delegating constructors and base class initializers are handled |
236 | // via the "typeLoc" matcher. |
237 | } |
238 | |
239 | return true; |
240 | } |
241 | |
242 | bool VisitCXXDestructorDecl(CXXDestructorDecl *Decl) { |
243 | if (Decl->isImplicit()) |
244 | return true; |
245 | SourceRange Range = Decl->getNameInfo().getSourceRange(); |
246 | if (Range.getBegin().isInvalid()) |
247 | return true; |
248 | |
249 | // The first token that will be found is the ~ (or the equivalent trigraph), |
250 | // we want instead to replace the next token, that will be the identifier. |
251 | Range.setBegin(CharSourceRange::getTokenRange(R: Range).getEnd()); |
252 | Check->addUsage(Decl->getParent(), Range, SM); |
253 | return true; |
254 | } |
255 | |
256 | bool VisitUsingDecl(UsingDecl *Decl) { |
257 | for (const auto *Shadow : Decl->shadows()) |
258 | Check->addUsage(Shadow->getTargetDecl(), |
259 | Decl->getNameInfo().getSourceRange(), SM); |
260 | return true; |
261 | } |
262 | |
263 | bool VisitUsingDirectiveDecl(UsingDirectiveDecl *Decl) { |
264 | Check->addUsage(Decl: Decl->getNominatedNamespaceAsWritten(), |
265 | Range: Decl->getIdentLocation(), SourceMgr: SM); |
266 | return true; |
267 | } |
268 | |
269 | bool VisitNamedDecl(NamedDecl *Decl) { |
270 | SourceRange UsageRange = |
271 | DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation()) |
272 | .getSourceRange(); |
273 | Check->addUsage(Decl, Range: UsageRange, SourceMgr: SM); |
274 | return true; |
275 | } |
276 | |
277 | bool VisitDeclRefExpr(DeclRefExpr *DeclRef) { |
278 | SourceRange Range = DeclRef->getNameInfo().getSourceRange(); |
279 | Check->addUsage(DeclRef->getDecl(), Range, SM); |
280 | return true; |
281 | } |
282 | |
283 | bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) { |
284 | if (const NestedNameSpecifier *Spec = Loc.getNestedNameSpecifier()) { |
285 | if (const NamespaceDecl *Decl = Spec->getAsNamespace()) |
286 | Check->addUsage(Decl, Loc.getLocalSourceRange(), SM); |
287 | } |
288 | |
289 | using Base = RecursiveASTVisitor<RenamerClangTidyVisitor>; |
290 | return Base::TraverseNestedNameSpecifierLoc(NNS: Loc); |
291 | } |
292 | |
293 | bool VisitMemberExpr(MemberExpr *MemberRef) { |
294 | SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange(); |
295 | Check->addUsage(MemberRef->getMemberDecl(), Range, SM); |
296 | return true; |
297 | } |
298 | |
299 | bool |
300 | VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *DepMemberRef) { |
301 | QualType BaseType = DepMemberRef->isArrow() |
302 | ? DepMemberRef->getBaseType()->getPointeeType() |
303 | : DepMemberRef->getBaseType(); |
304 | if (BaseType.isNull()) |
305 | return true; |
306 | const CXXRecordDecl *Base = BaseType.getTypePtr()->getAsCXXRecordDecl(); |
307 | if (!Base) |
308 | return true; |
309 | DeclarationName DeclName = DepMemberRef->getMemberNameInfo().getName(); |
310 | if (!DeclName.isIdentifier()) |
311 | return true; |
312 | StringRef DependentName = DeclName.getAsIdentifierInfo()->getName(); |
313 | |
314 | if (NameLookup Resolved = findDeclInBases( |
315 | Parent: *Base, DeclName: DependentName, AggressiveTemplateLookup: AggressiveDependentMemberLookup)) { |
316 | if (*Resolved) |
317 | Check->addUsage(Decl: *Resolved, |
318 | Range: DepMemberRef->getMemberNameInfo().getSourceRange(), SourceMgr: SM); |
319 | } |
320 | |
321 | return true; |
322 | } |
323 | |
324 | bool VisitTypedefTypeLoc(const TypedefTypeLoc &Loc) { |
325 | Check->addUsage(Loc.getTypedefNameDecl(), Loc.getSourceRange(), SM); |
326 | return true; |
327 | } |
328 | |
329 | bool VisitTagTypeLoc(const TagTypeLoc &Loc) { |
330 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
331 | return true; |
332 | } |
333 | |
334 | bool VisitInjectedClassNameTypeLoc(const InjectedClassNameTypeLoc &Loc) { |
335 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
336 | return true; |
337 | } |
338 | |
339 | bool VisitUnresolvedUsingTypeLoc(const UnresolvedUsingTypeLoc &Loc) { |
340 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
341 | return true; |
342 | } |
343 | |
344 | bool VisitTemplateTypeParmTypeLoc(const TemplateTypeParmTypeLoc &Loc) { |
345 | Check->addUsage(Loc.getDecl(), Loc.getSourceRange(), SM); |
346 | return true; |
347 | } |
348 | |
349 | bool |
350 | VisitTemplateSpecializationTypeLoc(const TemplateSpecializationTypeLoc &Loc) { |
351 | const TemplateDecl *Decl = |
352 | Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(); |
353 | |
354 | SourceRange Range(Loc.getTemplateNameLoc(), Loc.getTemplateNameLoc()); |
355 | if (const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl)) { |
356 | if (const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl()) |
357 | Check->addUsage(Decl: TemplDecl, Range, SourceMgr: SM); |
358 | } |
359 | |
360 | return true; |
361 | } |
362 | |
363 | bool VisitDependentTemplateSpecializationTypeLoc( |
364 | const DependentTemplateSpecializationTypeLoc &Loc) { |
365 | if (const TagDecl *Decl = Loc.getTypePtr()->getAsTagDecl()) |
366 | Check->addUsage(Decl, Loc.getSourceRange(), SM); |
367 | |
368 | return true; |
369 | } |
370 | |
371 | bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) { |
372 | for (const DesignatedInitExpr::Designator &D : Expr->designators()) { |
373 | if (!D.isFieldDesignator()) |
374 | continue; |
375 | const FieldDecl *FD = D.getFieldDecl(); |
376 | if (!FD) |
377 | continue; |
378 | const IdentifierInfo *II = FD->getIdentifier(); |
379 | if (!II) |
380 | continue; |
381 | SourceRange FixLocation{D.getFieldLoc(), D.getFieldLoc()}; |
382 | Check->addUsage(FD, FixLocation, SM); |
383 | } |
384 | |
385 | return true; |
386 | } |
387 | |
388 | private: |
389 | RenamerClangTidyCheck *Check; |
390 | const SourceManager &SM; |
391 | const bool AggressiveDependentMemberLookup; |
392 | }; |
393 | |
394 | } // namespace |
395 | |
396 | RenamerClangTidyCheck::RenamerClangTidyCheck(StringRef CheckName, |
397 | ClangTidyContext *Context) |
398 | : ClangTidyCheck(CheckName, Context), |
399 | AggressiveDependentMemberLookup( |
400 | Options.get(LocalName: "AggressiveDependentMemberLookup", Default: false)) {} |
401 | RenamerClangTidyCheck::~RenamerClangTidyCheck() = default; |
402 | |
403 | void RenamerClangTidyCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
404 | Options.store(Options&: Opts, LocalName: "AggressiveDependentMemberLookup", |
405 | Value: AggressiveDependentMemberLookup); |
406 | } |
407 | |
408 | void RenamerClangTidyCheck::registerMatchers(MatchFinder *Finder) { |
409 | Finder->addMatcher(NodeMatch: translationUnitDecl(), Action: this); |
410 | } |
411 | |
412 | void RenamerClangTidyCheck::registerPPCallbacks( |
413 | const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { |
414 | ModuleExpanderPP->addPPCallbacks( |
415 | C: std::make_unique<RenamerClangTidyCheckPPCallbacks>(args: SM, args: this)); |
416 | } |
417 | |
418 | std::pair<RenamerClangTidyCheck::NamingCheckFailureMap::iterator, bool> |
419 | RenamerClangTidyCheck::addUsage( |
420 | const RenamerClangTidyCheck::NamingCheckId &FailureId, |
421 | SourceRange UsageRange, const SourceManager &SourceMgr) { |
422 | // Do nothing if the provided range is invalid. |
423 | if (UsageRange.isInvalid()) |
424 | return {NamingCheckFailures.end(), false}; |
425 | |
426 | // Get the spelling location for performing the fix. This is necessary because |
427 | // macros can map the same spelling location to different source locations, |
428 | // and we only want to fix the token once, before it is expanded by the macro. |
429 | SourceLocation FixLocation = UsageRange.getBegin(); |
430 | FixLocation = SourceMgr.getSpellingLoc(Loc: FixLocation); |
431 | if (FixLocation.isInvalid()) |
432 | return {NamingCheckFailures.end(), false}; |
433 | |
434 | auto EmplaceResult = NamingCheckFailures.try_emplace(Key: FailureId); |
435 | NamingCheckFailure &Failure = EmplaceResult.first->second; |
436 | |
437 | // Try to insert the identifier location in the Usages map, and bail out if it |
438 | // is already in there |
439 | if (!Failure.RawUsageLocs.insert(V: FixLocation).second) |
440 | return EmplaceResult; |
441 | |
442 | if (Failure.FixStatus != RenamerClangTidyCheck::ShouldFixStatus::ShouldFix) |
443 | return EmplaceResult; |
444 | |
445 | if (SourceMgr.isWrittenInScratchSpace(Loc: FixLocation)) |
446 | Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; |
447 | |
448 | if (!utils::rangeCanBeFixed(Range: UsageRange, SM: &SourceMgr)) |
449 | Failure.FixStatus = RenamerClangTidyCheck::ShouldFixStatus::InsideMacro; |
450 | |
451 | return EmplaceResult; |
452 | } |
453 | |
454 | void RenamerClangTidyCheck::addUsage(const NamedDecl *Decl, |
455 | SourceRange UsageRange, |
456 | const SourceManager &SourceMgr) { |
457 | if (hasNoName(Decl)) |
458 | return; |
459 | |
460 | // Ignore ClassTemplateSpecializationDecl which are creating duplicate |
461 | // replacements with CXXRecordDecl. |
462 | if (isa<ClassTemplateSpecializationDecl>(Val: Decl)) |
463 | return; |
464 | |
465 | // We don't want to create a failure for every NamedDecl we find. Ideally |
466 | // there is just one NamedDecl in every group of "related" NamedDecls that |
467 | // becomes the failure. This NamedDecl and all of its related NamedDecls |
468 | // become usages. E.g. Since NamedDecls are Redeclarable, only the canonical |
469 | // NamedDecl becomes the failure and all redeclarations become usages. |
470 | const NamedDecl *FailureDecl = getFailureForNamedDecl(ND: Decl); |
471 | |
472 | std::optional<FailureInfo> MaybeFailure = |
473 | getDeclFailureInfo(Decl: FailureDecl, SM: SourceMgr); |
474 | if (!MaybeFailure) |
475 | return; |
476 | |
477 | NamingCheckId FailureId(FailureDecl->getLocation(), FailureDecl->getName()); |
478 | |
479 | auto [FailureIter, NewFailure] = addUsage(FailureId, UsageRange, SourceMgr); |
480 | |
481 | if (FailureIter == NamingCheckFailures.end()) { |
482 | // Nothing to do if the usage wasn't accepted. |
483 | return; |
484 | } |
485 | if (!NewFailure) { |
486 | // FailureInfo has already been provided. |
487 | return; |
488 | } |
489 | |
490 | // Update the stored failure with info regarding the FailureDecl. |
491 | NamingCheckFailure &Failure = FailureIter->second; |
492 | Failure.Info = std::move(*MaybeFailure); |
493 | |
494 | // Don't overwritte the failure status if it was already set. |
495 | if (!Failure.shouldFix()) { |
496 | return; |
497 | } |
498 | const IdentifierTable &Idents = FailureDecl->getASTContext().Idents; |
499 | auto CheckNewIdentifier = Idents.find(Failure.Info.Fixup); |
500 | if (CheckNewIdentifier != Idents.end()) { |
501 | const IdentifierInfo *Ident = CheckNewIdentifier->second; |
502 | if (Ident->isKeyword(LangOpts: getLangOpts())) |
503 | Failure.FixStatus = ShouldFixStatus::ConflictsWithKeyword; |
504 | else if (Ident->hasMacroDefinition()) |
505 | Failure.FixStatus = ShouldFixStatus::ConflictsWithMacroDefinition; |
506 | } else if (!isValidAsciiIdentifier(S: Failure.Info.Fixup)) { |
507 | Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier; |
508 | } |
509 | } |
510 | |
511 | void RenamerClangTidyCheck::check(const MatchFinder::MatchResult &Result) { |
512 | if (!Result.SourceManager) { |
513 | // In principle SourceManager is not null but going only by the definition |
514 | // of MatchResult it must be handled. Cannot rename anything without a |
515 | // SourceManager. |
516 | return; |
517 | } |
518 | RenamerClangTidyVisitor Visitor(this, *Result.SourceManager, |
519 | AggressiveDependentMemberLookup); |
520 | Visitor.TraverseAST(AST&: *Result.Context); |
521 | } |
522 | |
523 | void RenamerClangTidyCheck::checkMacro(const Token &MacroNameTok, |
524 | const MacroInfo *MI, |
525 | const SourceManager &SourceMgr) { |
526 | std::optional<FailureInfo> MaybeFailure = |
527 | getMacroFailureInfo(MacroNameTok, SM: SourceMgr); |
528 | if (!MaybeFailure) |
529 | return; |
530 | FailureInfo &Info = *MaybeFailure; |
531 | StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); |
532 | NamingCheckId ID(MI->getDefinitionLoc(), Name); |
533 | NamingCheckFailure &Failure = NamingCheckFailures[ID]; |
534 | SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); |
535 | |
536 | if (!isValidAsciiIdentifier(S: Info.Fixup)) |
537 | Failure.FixStatus = ShouldFixStatus::FixInvalidIdentifier; |
538 | |
539 | Failure.Info = std::move(Info); |
540 | addUsage(FailureId: ID, UsageRange: Range, SourceMgr); |
541 | } |
542 | |
543 | void RenamerClangTidyCheck::expandMacro(const Token &MacroNameTok, |
544 | const MacroInfo *MI, |
545 | const SourceManager &SourceMgr) { |
546 | StringRef Name = MacroNameTok.getIdentifierInfo()->getName(); |
547 | NamingCheckId ID(MI->getDefinitionLoc(), Name); |
548 | |
549 | auto Failure = NamingCheckFailures.find(Val: ID); |
550 | if (Failure == NamingCheckFailures.end()) |
551 | return; |
552 | |
553 | SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc()); |
554 | addUsage(FailureId: ID, UsageRange: Range, SourceMgr); |
555 | } |
556 | |
557 | static std::string |
558 | getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus, |
559 | const std::string &Fixup) { |
560 | if (Fixup.empty() || |
561 | FixStatus == RenamerClangTidyCheck::ShouldFixStatus::FixInvalidIdentifier) |
562 | return "; cannot be fixed automatically"; |
563 | if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ShouldFix) |
564 | return {}; |
565 | if (FixStatus >= |
566 | RenamerClangTidyCheck::ShouldFixStatus::IgnoreFailureThreshold) |
567 | return {}; |
568 | if (FixStatus == RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithKeyword) |
569 | return "; cannot be fixed because '"+ Fixup + |
570 | "' would conflict with a keyword"; |
571 | if (FixStatus == |
572 | RenamerClangTidyCheck::ShouldFixStatus::ConflictsWithMacroDefinition) |
573 | return "; cannot be fixed because '"+ Fixup + |
574 | "' would conflict with a macro definition"; |
575 | llvm_unreachable("invalid ShouldFixStatus"); |
576 | } |
577 | |
578 | void RenamerClangTidyCheck::onEndOfTranslationUnit() { |
579 | for (const auto &Pair : NamingCheckFailures) { |
580 | const NamingCheckId &Decl = Pair.first; |
581 | const NamingCheckFailure &Failure = Pair.second; |
582 | |
583 | if (Failure.Info.KindName.empty()) |
584 | continue; |
585 | |
586 | if (Failure.shouldNotify()) { |
587 | auto DiagInfo = getDiagInfo(ID: Decl, Failure); |
588 | auto Diag = diag(Loc: Decl.first, |
589 | Description: DiagInfo.Text + getDiagnosticSuffix(FixStatus: Failure.FixStatus, |
590 | Fixup: Failure.Info.Fixup)); |
591 | DiagInfo.ApplyArgs(Diag); |
592 | |
593 | if (Failure.shouldFix()) { |
594 | for (const auto &Loc : Failure.RawUsageLocs) { |
595 | // We assume that the identifier name is made of one token only. This |
596 | // is always the case as we ignore usages in macros that could build |
597 | // identifier names by combining multiple tokens. |
598 | // |
599 | // For destructors, we already take care of it by remembering the |
600 | // location of the start of the identifier and not the start of the |
601 | // tilde. |
602 | // |
603 | // Other multi-token identifiers, such as operators are not checked at |
604 | // all. |
605 | Diag << FixItHint::CreateReplacement(RemoveRange: SourceRange(Loc), |
606 | Code: Failure.Info.Fixup); |
607 | } |
608 | } |
609 | } |
610 | } |
611 | } |
612 | |
613 | } // namespace clang::tidy |
614 |
Definitions
- DenseMapInfo
- getEmptyKey
- getTombstoneKey
- getHashValue
- isEqual
- NameLookup
- NameLookup
- NameLookup
- NameLookup
- NameLookup
- hasMultipleResolutions
- getDecl
- operator bool
- operator*
- findDecl
- getOverrideMethod
- hasNoName
- getFailureForNamedDecl
- findDeclInBases
- RenamerClangTidyCheckPPCallbacks
- RenamerClangTidyCheckPPCallbacks
- MacroDefined
- MacroExpands
- RenamerClangTidyVisitor
- RenamerClangTidyVisitor
- shouldVisitTemplateInstantiations
- shouldVisitImplicitCode
- VisitCXXConstructorDecl
- VisitCXXDestructorDecl
- VisitUsingDecl
- VisitUsingDirectiveDecl
- VisitNamedDecl
- VisitDeclRefExpr
- TraverseNestedNameSpecifierLoc
- VisitMemberExpr
- VisitCXXDependentScopeMemberExpr
- VisitTypedefTypeLoc
- VisitTagTypeLoc
- VisitInjectedClassNameTypeLoc
- VisitUnresolvedUsingTypeLoc
- VisitTemplateTypeParmTypeLoc
- VisitTemplateSpecializationTypeLoc
- VisitDependentTemplateSpecializationTypeLoc
- VisitDesignatedInitExpr
- RenamerClangTidyCheck
- ~RenamerClangTidyCheck
- storeOptions
- registerMatchers
- registerPPCallbacks
- addUsage
- addUsage
- check
- checkMacro
- expandMacro
- getDiagnosticSuffix
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more