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"
22#define DEBUG_TYPE "clang-tidy"
34 return {DenseMapInfo<clang::SourceLocation>::getEmptyKey(),
"EMPTY"};
38 return {DenseMapInfo<clang::SourceLocation>::getTombstoneKey(),
43 assert(Val !=
getEmptyKey() &&
"Cannot hash the empty key!");
46 return DenseMapInfo<clang::SourceLocation>::getHashValue(Val.first) +
47 DenseMapInfo<StringRef>::getHashValue(Val.second);
66 llvm::PointerIntPair<const NamedDecl *, 1, bool> Data;
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) {}
74 bool hasMultipleResolutions()
const {
return Data.getInt(); }
75 const NamedDecl *getDecl()
const {
76 assert(!hasMultipleResolutions() &&
"Found multiple decls");
77 return Data.getPointer();
79 operator bool()
const {
return !hasMultipleResolutions(); }
80 const NamedDecl *operator*()
const {
return getDecl(); }
85static const NamedDecl *
findDecl(
const RecordDecl &RecDecl,
87 for (
const Decl *D : RecDecl.decls()) {
88 if (
const auto *ND = dyn_cast<NamedDecl>(D)) {
89 if (ND->getDeclName().isIdentifier() && ND->getName() == DeclName)
100 if (Method->size_overridden_methods() != 1)
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)
109 if (NumOverrides > 1)
115 return !Decl->getIdentifier() || Decl->getName().empty();
119 const auto *Canonical = cast<NamedDecl>(ND->getCanonicalDecl());
123 if (
const auto *Method = dyn_cast<CXXMethodDecl>(ND)) {
125 Canonical = cast<NamedDecl>(Overridden->getCanonicalDecl());
126 else if (
const FunctionTemplateDecl *Primary = Method->getPrimaryTemplate())
127 if (
const FunctionDecl *TemplatedDecl = Primary->getTemplatedDecl())
128 Canonical = cast<NamedDecl>(TemplatedDecl->getCanonicalDecl());
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;
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 TST->getTemplateName().getAsTemplateDecl()))
158 Record = TD->getTemplatedDecl();
173 return NameLookup(std::nullopt);
175 return NameLookup(Found);
181class RenamerClangTidyCheckPPCallbacks :
public PPCallbacks {
183 RenamerClangTidyCheckPPCallbacks(
const SourceManager &SM,
184 RenamerClangTidyCheck *Check)
185 : SM(SM), Check(Check) {}
188 void MacroDefined(
const Token &MacroNameTok,
189 const MacroDirective *MD)
override {
190 const MacroInfo *Info = MD->getMacroInfo();
191 if (Info->isBuiltinMacro())
193 if (SM.isWrittenInBuiltinFile(MacroNameTok.getLocation()))
195 if (SM.isWrittenInCommandLineFile(MacroNameTok.getLocation()))
197 if (SM.isInSystemHeader(MacroNameTok.getLocation()))
199 Check->checkMacro(MacroNameTok, Info, SM);
203 void MacroExpands(
const Token &MacroNameTok,
const MacroDefinition &MD,
205 const MacroArgs * )
override {
206 Check->expandMacro(MacroNameTok, MD.getMacroInfo(), SM);
210 const SourceManager &SM;
211 RenamerClangTidyCheck *Check;
214class RenamerClangTidyVisitor
215 :
public RecursiveASTVisitor<RenamerClangTidyVisitor> {
217 RenamerClangTidyVisitor(RenamerClangTidyCheck *Check,
const SourceManager &SM,
218 bool AggressiveDependentMemberLookup)
219 : Check(Check), SM(SM),
220 AggressiveDependentMemberLookup(AggressiveDependentMemberLookup) {}
222 bool shouldVisitTemplateInstantiations()
const {
return true; }
224 bool shouldVisitImplicitCode()
const {
return false; }
226 bool VisitCXXConstructorDecl(CXXConstructorDecl *Decl) {
227 if (Decl->isImplicit())
229 Check->addUsage(Decl->getParent(), Decl->getNameInfo().getSourceRange(),
232 for (
const auto *Init : Decl->inits()) {
233 if (!Init->isWritten() || Init->isInClassMemberInitializer())
235 if (
const FieldDecl *FD = Init->getAnyMember())
236 Check->addUsage(FD, SourceRange(Init->getMemberLocation()), SM);
244 bool VisitCXXDestructorDecl(CXXDestructorDecl *Decl) {
245 if (Decl->isImplicit())
247 SourceRange Range = Decl->getNameInfo().getSourceRange();
248 if (Range.getBegin().isInvalid())
253 Range.setBegin(CharSourceRange::getTokenRange(Range).getEnd());
254 Check->addUsage(Decl->getParent(), Range, SM);
258 bool VisitUsingDecl(UsingDecl *Decl) {
259 for (
const auto *Shadow : Decl->shadows())
260 Check->addUsage(Shadow->getTargetDecl(),
261 Decl->getNameInfo().getSourceRange(), SM);
265 bool VisitUsingDirectiveDecl(UsingDirectiveDecl *Decl) {
266 Check->addUsage(Decl->getNominatedNamespaceAsWritten(),
267 Decl->getIdentLocation(), SM);
271 bool VisitNamedDecl(NamedDecl *Decl) {
272 SourceRange UsageRange =
273 DeclarationNameInfo(Decl->getDeclName(), Decl->getLocation())
275 Check->addUsage(Decl, UsageRange, SM);
279 bool VisitDeclRefExpr(DeclRefExpr *DeclRef) {
280 SourceRange Range = DeclRef->getNameInfo().getSourceRange();
281 Check->addUsage(DeclRef->getDecl(), Range, SM);
285 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Loc) {
286 if (NestedNameSpecifier Spec = Loc.getNestedNameSpecifier();
287 Spec.getKind() == NestedNameSpecifier::Kind::Namespace) {
288 if (
const auto *Decl =
289 dyn_cast<NamespaceDecl>(Spec.getAsNamespaceAndPrefix().Namespace))
290 Check->addUsage(Decl, Loc.getLocalSourceRange(), SM);
293 using Base = RecursiveASTVisitor<RenamerClangTidyVisitor>;
294 return Base::TraverseNestedNameSpecifierLoc(Loc);
297 bool VisitMemberExpr(MemberExpr *MemberRef) {
298 SourceRange Range = MemberRef->getMemberNameInfo().getSourceRange();
299 Check->addUsage(MemberRef->getMemberDecl(), Range, SM);
304 VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *DepMemberRef) {
305 QualType BaseType = DepMemberRef->isArrow()
306 ? DepMemberRef->getBaseType()->getPointeeType()
307 : DepMemberRef->getBaseType();
308 if (BaseType.isNull())
310 const CXXRecordDecl *Base = BaseType.getTypePtr()->getAsCXXRecordDecl();
313 DeclarationName DeclName = DepMemberRef->getMemberNameInfo().getName();
314 if (!DeclName.isIdentifier())
316 StringRef
DependentName = DeclName.getAsIdentifierInfo()->getName();
319 *Base, DependentName, AggressiveDependentMemberLookup)) {
321 Check->addUsage(*Resolved,
322 DepMemberRef->getMemberNameInfo().getSourceRange(), SM);
328 bool VisitTypedefTypeLoc(
const TypedefTypeLoc &Loc) {
329 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
333 bool VisitTagTypeLoc(
const TagTypeLoc &Loc) {
334 Check->addUsage(Loc.getOriginalDecl(), Loc.getNameLoc(), SM);
338 bool VisitUnresolvedUsingTypeLoc(
const UnresolvedUsingTypeLoc &Loc) {
339 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
343 bool VisitTemplateTypeParmTypeLoc(
const TemplateTypeParmTypeLoc &Loc) {
344 Check->addUsage(Loc.getDecl(), Loc.getNameLoc(), SM);
349 VisitTemplateSpecializationTypeLoc(
const TemplateSpecializationTypeLoc &Loc) {
350 const TemplateDecl *Decl =
351 Loc.getTypePtr()->getTemplateName().getAsTemplateDecl(
356 if (
const auto *ClassDecl = dyn_cast<TemplateDecl>(Decl))
357 if (
const NamedDecl *TemplDecl = ClassDecl->getTemplatedDecl())
358 Check->addUsage(TemplDecl, Loc.getTemplateNameLoc(), SM);
363 bool VisitDesignatedInitExpr(DesignatedInitExpr *Expr) {
364 for (
const DesignatedInitExpr::Designator &D : Expr->designators()) {
365 if (!
D.isFieldDesignator())
367 const FieldDecl *FD =
D.getFieldDecl();
370 const IdentifierInfo *II = FD->getIdentifier();
373 SourceRange FixLocation{
D.getFieldLoc(),
D.getFieldLoc()};
374 Check->addUsage(FD, FixLocation, SM);
381 RenamerClangTidyCheck *Check;
382 const SourceManager &SM;
383 const bool AggressiveDependentMemberLookup;
391 AggressiveDependentMemberLookup(
392 Options.get(
"AggressiveDependentMemberLookup", false)) {}
396 Options.store(Opts,
"AggressiveDependentMemberLookup",
397 AggressiveDependentMemberLookup);
401 Finder->addMatcher(translationUnitDecl(),
this);
405 const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
406 ModuleExpanderPP->addPPCallbacks(
407 std::make_unique<RenamerClangTidyCheckPPCallbacks>(SM,
this));
410std::pair<RenamerClangTidyCheck::NamingCheckFailureMap::iterator, bool>
413 SourceRange UsageRange,
const SourceManager &SourceMgr) {
415 if (UsageRange.isInvalid())
416 return {NamingCheckFailures.end(),
false};
421 SourceLocation FixLocation = UsageRange.getBegin();
422 FixLocation = SourceMgr.getSpellingLoc(FixLocation);
423 if (FixLocation.isInvalid())
424 return {NamingCheckFailures.end(),
false};
427 if (SourceMgr.isInSystemHeader(FixLocation))
428 return {NamingCheckFailures.end(),
false};
430 auto EmplaceResult = NamingCheckFailures.try_emplace(FailureId);
435 if (!Failure.RawUsageLocs.insert(FixLocation).second)
436 return EmplaceResult;
439 return EmplaceResult;
441 if (SourceMgr.isWrittenInScratchSpace(FixLocation))
447 return EmplaceResult;
451 SourceRange UsageRange,
452 const SourceManager &SourceMgr) {
453 if (SourceMgr.isInSystemHeader(Decl->getLocation()))
461 if (isa<ClassTemplateSpecializationDecl>(Decl))
471 std::optional<FailureInfo> MaybeFailure =
476 NamingCheckId FailureId(FailureDecl->getLocation(), FailureDecl->getName());
478 auto [FailureIter, NewFailure] =
addUsage(FailureId, UsageRange, SourceMgr);
480 if (FailureIter == NamingCheckFailures.end()) {
491 Failure.
Info = std::move(*MaybeFailure);
497 const IdentifierTable &Idents = FailureDecl->getASTContext().Idents;
498 auto CheckNewIdentifier = Idents.find(Failure.
Info.
Fixup);
499 if (CheckNewIdentifier != Idents.end()) {
500 const IdentifierInfo *Ident = CheckNewIdentifier->second;
501 if (Ident->isKeyword(getLangOpts()))
503 else if (Ident->hasMacroDefinition())
505 }
else if (!isValidAsciiIdentifier(Failure.
Info.
Fixup)) {
511 if (!Result.SourceManager) {
517 RenamerClangTidyVisitor Visitor(
this, *Result.SourceManager,
518 AggressiveDependentMemberLookup);
519 Visitor.TraverseAST(*Result.Context);
524 const SourceManager &SourceMgr) {
525 std::optional<FailureInfo> MaybeFailure =
530 StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
533 SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
535 if (!isValidAsciiIdentifier(Info.Fixup))
538 Failure.
Info = std::move(Info);
544 const SourceManager &SourceMgr) {
545 StringRef Name = MacroNameTok.getIdentifierInfo()->getName();
548 auto Failure = NamingCheckFailures.find(ID);
549 if (Failure == NamingCheckFailures.end())
552 SourceRange Range(MacroNameTok.getLocation(), MacroNameTok.getEndLoc());
558 const std::string &Fixup) {
561 return "; cannot be fixed automatically";
568 return "; cannot be fixed because '" + Fixup +
569 "' would conflict with a keyword";
572 return "; cannot be fixed because '" + Fixup +
573 "' would conflict with a macro definition";
574 llvm_unreachable(
"invalid ShouldFixStatus");
578 for (
const auto &Pair : NamingCheckFailures) {
587 auto Diag = diag(Decl.first,
604 Diag << FixItHint::CreateReplacement(SourceRange(Loc),
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) final
std::pair< SourceLocation, StringRef > NamingCheckId
void onEndOfTranslationUnit() final
virtual DiagInfo getDiagInfo(const NamingCheckId &ID, const NamingCheckFailure &Failure) const =0
Overridden by derived classes, returns a description of the diagnostic that should be emitted for the...
void expandMacro(const Token &MacroNameTok, const MacroInfo *MI, const SourceManager &SourceMgr)
Add a usage of a macro if it already has a violation.
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Derived classes should not implement any matching logic themselves; this class will do the matching a...
RenamerClangTidyCheck(StringRef CheckName, ClangTidyContext *Context)
ShouldFixStatus
This enum will be used in select of the diagnostic message.
@ IgnoreFailureThreshold
Values pass this threshold will be ignored completely i.e no message, no fixup.
@ ConflictsWithMacroDefinition
The fixup will conflict with a macro definition, so we can't fix it automatically.
@ ConflictsWithKeyword
The fixup will conflict with a language keyword, so we can't fix it automatically.
@ InsideMacro
If the identifier was used or declared within a macro we won't offer a fixup for safety reasons.
@ FixInvalidIdentifier
The fixup results in an identifier that is not a valid c/c++ identifier.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Derived classes that override this function should call this method from the overridden method.
virtual std::optional< FailureInfo > getMacroFailureInfo(const Token &MacroNameTok, const SourceManager &SM) const =0
Overridden by derived classes, returns information about if and how a macro failed the check.
void addUsage(const NamedDecl *Decl, SourceRange Range, const SourceManager &SourceMgr)
virtual std::optional< FailureInfo > getDeclFailureInfo(const NamedDecl *Decl, const SourceManager &SM) const =0
Overridden by derived classes, returns information about if and how a Decl failed the check.
void checkMacro(const Token &MacroNameTok, const MacroInfo *MI, const SourceManager &SourceMgr)
Check Macros for style violations.
bool rangeCanBeFixed(SourceRange Range, const SourceManager *SM)
static NameLookup findDeclInBases(const CXXRecordDecl &Parent, StringRef DeclName, bool AggressiveTemplateLookup)
Returns a decl matching the DeclName in Parent or one of its base classes.
static std::string getDiagnosticSuffix(const RenamerClangTidyCheck::ShouldFixStatus FixStatus, const std::string &Fixup)
static const CXXMethodDecl * getOverrideMethod(const CXXMethodDecl *Method)
Returns the function that Method is overridding.
static const NamedDecl * getFailureForNamedDecl(const NamedDecl *ND)
static const NamedDecl * findDecl(const RecordDecl &RecDecl, StringRef DeclName)
static bool hasNoName(const NamedDecl *Decl)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
llvm::StringMap< ClangTidyValue > OptionMap
Represents customized diagnostic text and how arguments should be applied.
llvm::unique_function< void(DiagnosticBuilder &)> ApplyArgs
Information describing a failed check.
Holds an identifier name check failure, tracking the kind of the identifier, its possible fixup and t...
ShouldFixStatus FixStatus
llvm::DenseSet< SourceLocation > RawUsageLocs
A set of all the identifier usages starting SourceLocation.
bool shouldNotify() const
bool shouldFix() const
Whether the failure should be fixed or not.
static NamingCheckId getTombstoneKey()
static NamingCheckId getEmptyKey()
static bool isEqual(const NamingCheckId &LHS, const NamingCheckId &RHS)
static unsigned getHashValue(NamingCheckId Val)
clang::tidy::RenamerClangTidyCheck::NamingCheckId NamingCheckId