10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
28 auto IsInSpecialization = hasAncestor(
29 decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
30 functionDecl(isExplicitTemplateSpecialization()))));
33 hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
34 unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
35 unless(isInstantiated()), unless(IsInSpecialization),
36 unless(classTemplateSpecializationDecl()))
43 Finder->addMatcher(friendDecl().bind(
"friend_decl"),
this);
47 const MatchFinder::MatchResult &Result) {
48 if (
const auto *RecordDecl =
49 Result.Nodes.getNodeAs<CXXRecordDecl>(
"record_decl")) {
50 StringRef DeclName = RecordDecl->getName();
51 if (RecordDecl->isThisDeclarationADefinition()) {
52 DeclNameToDefinitions[DeclName].push_back(RecordDecl);
58 DeclNameToDeclarations[DeclName].push_back(RecordDecl);
61 const auto *
Decl = Result.Nodes.getNodeAs<FriendDecl>(
"friend_decl");
62 assert(
Decl &&
"Decl is neither record_decl nor friend decl!");
73 if (
const TypeSourceInfo *Tsi =
Decl->getFriendType()) {
74 QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
75 FriendTypes.insert(Desugared.getTypePtr());
81 const CXXRecordDecl *Decl2) {
82 const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
83 const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
88 if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
89 ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
90 return ParentDecl1 == ParentDecl2;
92 assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
93 "ParentDecl1 declaration must be a namespace");
94 assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
95 "ParentDecl2 declaration must be a namespace");
96 auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
97 auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
98 return Ns1->getFirstDecl() == Ns2->getFirstDecl();
102 const auto *ParentDecl =
Decl->getLexicalParent();
103 if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
106 const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
108 llvm::raw_string_ostream OStream(Ns);
109 NsDecl->printQualifiedName(OStream);
111 return Ns.empty() ?
"(global)" : Ns;
116 for (
const auto &KeyValuePair : DeclNameToDeclarations) {
117 const auto &Declarations = KeyValuePair.second;
120 for (
const auto *CurDecl : Declarations) {
121 if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
124 if (FriendTypes.contains(CurDecl->getTypeForDecl())) {
127 if (CurDecl->getLocation().isMacroID() ||
128 CurDecl->getLocation().isInvalid()) {
132 for (
const auto *
Decl : Declarations) {
133 if (
Decl == CurDecl) {
136 if (!CurDecl->hasDefinition() &&
138 diag(CurDecl->getLocation(),
139 "declaration %0 is never referenced, but a declaration with "
140 "the same name found in another namespace '%1'")
142 diag(
Decl->getLocation(),
"a declaration of %0 is found here",
149 const auto DeclName = CurDecl->getName();
150 if (!DeclNameToDefinitions.contains(DeclName)) {
155 const auto &Definitions = DeclNameToDefinitions[DeclName];
156 for (
const auto *Def : Definitions) {
157 diag(CurDecl->getLocation(),
158 "no definition found for %0, but a definition with "
159 "the same name %1 found in another namespace '%2'")
161 diag(Def->getLocation(),
"a definition of %0 is found here",
const FunctionDecl * Decl
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void onEndOfTranslationUnit() override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static std::string getNameOfNamespace(const CXXRecordDecl *Decl)
static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1, const CXXRecordDecl *Decl2)