10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
23 void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
30 auto IsInSpecialization = hasAncestor(
35 hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
36 unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
37 unless(isInstantiated()), unless(IsInSpecialization),
38 unless(classTemplateSpecializationDecl()))
45 Finder->addMatcher(friendDecl().bind(
"friend_decl"),
this);
49 const MatchFinder::MatchResult &Result) {
50 if (
const auto *RecordDecl =
51 Result.Nodes.getNodeAs<CXXRecordDecl>(
"record_decl")) {
52 StringRef DeclName = RecordDecl->getName();
53 if (RecordDecl->isThisDeclarationADefinition()) {
54 DeclNameToDefinitions[DeclName].push_back(RecordDecl);
60 DeclNameToDeclarations[DeclName].push_back(RecordDecl);
63 const auto *
Decl = Result.Nodes.getNodeAs<FriendDecl>(
"friend_decl");
64 assert(
Decl &&
"Decl is neither record_decl nor friend decl!");
75 if (
const TypeSourceInfo *Tsi =
Decl->getFriendType()) {
76 QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
77 FriendTypes.insert(Desugared.getTypePtr());
83 const CXXRecordDecl *Decl2) {
84 const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
85 const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
90 if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
91 ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
92 return ParentDecl1 == ParentDecl2;
94 assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
95 "ParentDecl1 declaration must be a namespace");
96 assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
97 "ParentDecl2 declaration must be a namespace");
98 auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
99 auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
100 return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
104 const auto *ParentDecl =
Decl->getLexicalParent();
105 if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
108 const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
110 llvm::raw_string_ostream OStream(Ns);
111 NsDecl->printQualifiedName(OStream);
113 return Ns.empty() ?
"(global)" : Ns;
116 void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
118 for (
const auto &KeyValuePair : DeclNameToDeclarations) {
119 const auto &Declarations = KeyValuePair.second;
122 for (
const auto *CurDecl : Declarations) {
123 if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
126 if (FriendTypes.contains(CurDecl->getTypeForDecl())) {
129 if (CurDecl->getLocation().isMacroID() ||
130 CurDecl->getLocation().isInvalid()) {
134 for (
const auto *
Decl : Declarations) {
135 if (
Decl == CurDecl) {
138 if (!CurDecl->hasDefinition() &&
140 diag(CurDecl->getLocation(),
141 "declaration %0 is never referenced, but a declaration with "
142 "the same name found in another namespace '%1'")
144 diag(
Decl->getLocation(),
"a declaration of %0 is found here",
151 const auto DeclName = CurDecl->getName();
152 if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
157 const auto &Definitions = DeclNameToDefinitions[DeclName];
158 for (
const auto *Def : Definitions) {
159 diag(CurDecl->getLocation(),
160 "no definition found for %0, but a definition with "
161 "the same name %1 found in another namespace '%2'")
163 diag(Def->getLocation(),
"a definition of %0 is found here",