You've already forked linux-packaging-mono
acceptance-tests
data
debian
docs
external
Newtonsoft.Json
api-doc-tools
api-snapshot
aspnetwebstack
bdwgc
binary-reference-assemblies
bockbuild
boringssl
cecil
cecil-legacy
corefx
corert
helix-binaries
ikdasm
ikvm
illinker-test-assets
linker
llvm-project
clang
clang-tools-extra
change-namespace
clang-apply-replacements
clang-move
clang-query
clang-reorder-fields
clang-tidy
android
boost
bugprone
ArgumentCommentCheck.cpp
ArgumentCommentCheck.h
AssertSideEffectCheck.cpp
AssertSideEffectCheck.h
BoolPointerImplicitConversionCheck.cpp
BoolPointerImplicitConversionCheck.h
BugproneTidyModule.cpp
CMakeLists.txt
CopyConstructorInitCheck.cpp
CopyConstructorInitCheck.h
DanglingHandleCheck.cpp
DanglingHandleCheck.h
FoldInitTypeCheck.cpp
FoldInitTypeCheck.h
ForwardDeclarationNamespaceCheck.cpp
ForwardDeclarationNamespaceCheck.h
InaccurateEraseCheck.cpp
InaccurateEraseCheck.h
IntegerDivisionCheck.cpp
IntegerDivisionCheck.h
MisplacedOperatorInStrlenInAllocCheck.cpp
MisplacedOperatorInStrlenInAllocCheck.h
MoveForwardingReferenceCheck.cpp
MoveForwardingReferenceCheck.h
MultipleStatementMacroCheck.cpp
MultipleStatementMacroCheck.h
StringConstructorCheck.cpp
StringConstructorCheck.h
SuspiciousMemsetUsageCheck.cpp
SuspiciousMemsetUsageCheck.h
UndefinedMemoryManipulationCheck.cpp
UndefinedMemoryManipulationCheck.h
UseAfterMoveCheck.cpp
UseAfterMoveCheck.h
VirtualNearMissCheck.cpp
VirtualNearMissCheck.h
cert
cppcoreguidelines
fuchsia
google
hicpp
llvm
misc
modernize
mpi
objc
performance
plugin
readability
tool
utils
CMakeLists.txt
ClangTidy.cpp
ClangTidy.h
ClangTidyDiagnosticConsumer.cpp
ClangTidyDiagnosticConsumer.h
ClangTidyModule.cpp
ClangTidyModule.h
ClangTidyModuleRegistry.h
ClangTidyOptions.cpp
ClangTidyOptions.h
add_new_check.py
rename_check.py
clang-tidy-vs
clangd
docs
include-fixer
modularize
pp-trace
test
tool-template
unittests
.arcconfig
.gitignore
CMakeLists.txt
CODE_OWNERS.TXT
LICENSE.TXT
README.txt
compiler-rt
libcxx
libcxxabi
libunwind
lld
lldb
llvm
openmp
polly
nuget-buildtasks
nunit-lite
roslyn-binaries
rx
xunit-binaries
how-to-bump-roslyn-binaries.md
ikvm-native
llvm
m4
man
mcs
mk
mono
msvc
netcore
po
runtime
samples
scripts
support
tools
COPYING.LIB
LICENSE
Makefile.am
Makefile.in
NEWS
README.md
acinclude.m4
aclocal.m4
autogen.sh
code_of_conduct.md
compile
config.guess
config.h.in
config.rpath
config.sub
configure.REMOVED.git-id
configure.ac.REMOVED.git-id
depcomp
install-sh
ltmain.sh.REMOVED.git-id
missing
mkinstalldirs
mono-uninstalled.pc.in
test-driver
winconfig.h
175 lines
7.0 KiB
C++
175 lines
7.0 KiB
C++
![]() |
//===--- ForwardDeclarationNamespaceCheck.cpp - clang-tidy ------*- C++ -*-===//
|
||
|
//
|
||
|
// The LLVM Compiler Infrastructure
|
||
|
//
|
||
|
// This file is distributed under the University of Illinois Open Source
|
||
|
// License. See LICENSE.TXT for details.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "ForwardDeclarationNamespaceCheck.h"
|
||
|
#include "clang/AST/ASTContext.h"
|
||
|
#include "clang/AST/Decl.h"
|
||
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
||
|
#include <stack>
|
||
|
#include <string>
|
||
|
|
||
|
using namespace clang::ast_matchers;
|
||
|
|
||
|
namespace clang {
|
||
|
namespace tidy {
|
||
|
namespace bugprone {
|
||
|
|
||
|
void ForwardDeclarationNamespaceCheck::registerMatchers(MatchFinder *Finder) {
|
||
|
// Match all class declarations/definitions *EXCEPT*
|
||
|
// 1. implicit classes, e.g. `class A {};` has implicit `class A` inside `A`.
|
||
|
// 2. nested classes declared/defined inside another class.
|
||
|
// 3. template class declaration, template instantiation or
|
||
|
// specialization (NOTE: extern specialization is filtered out by
|
||
|
// `unless(hasAncestor(cxxRecordDecl()))`).
|
||
|
auto IsInSpecialization = hasAncestor(
|
||
|
decl(anyOf(cxxRecordDecl(isExplicitTemplateSpecialization()),
|
||
|
functionDecl(isExplicitTemplateSpecialization()))));
|
||
|
Finder->addMatcher(
|
||
|
cxxRecordDecl(
|
||
|
hasParent(decl(anyOf(namespaceDecl(), translationUnitDecl()))),
|
||
|
unless(isImplicit()), unless(hasAncestor(cxxRecordDecl())),
|
||
|
unless(isInstantiated()), unless(IsInSpecialization),
|
||
|
unless(classTemplateSpecializationDecl()))
|
||
|
.bind("record_decl"),
|
||
|
this);
|
||
|
|
||
|
// Match all friend declarations. Classes used in friend declarations are not
|
||
|
// marked as referenced in AST. We need to record all record classes used in
|
||
|
// friend declarations.
|
||
|
Finder->addMatcher(friendDecl().bind("friend_decl"), this);
|
||
|
}
|
||
|
|
||
|
void ForwardDeclarationNamespaceCheck::check(
|
||
|
const MatchFinder::MatchResult &Result) {
|
||
|
if (const auto *RecordDecl =
|
||
|
Result.Nodes.getNodeAs<CXXRecordDecl>("record_decl")) {
|
||
|
StringRef DeclName = RecordDecl->getName();
|
||
|
if (RecordDecl->isThisDeclarationADefinition()) {
|
||
|
DeclNameToDefinitions[DeclName].push_back(RecordDecl);
|
||
|
} else {
|
||
|
// If a declaration has no definition, the definition could be in another
|
||
|
// namespace (a wrong namespace).
|
||
|
// NOTE: even a declaration does have definition, we still need it to
|
||
|
// compare with other declarations.
|
||
|
DeclNameToDeclarations[DeclName].push_back(RecordDecl);
|
||
|
}
|
||
|
} else {
|
||
|
const auto *Decl = Result.Nodes.getNodeAs<FriendDecl>("friend_decl");
|
||
|
assert(Decl && "Decl is neither record_decl nor friend decl!");
|
||
|
|
||
|
// Classes used in friend delarations are not marked referenced in AST,
|
||
|
// so we need to check classes used in friend declarations manually to
|
||
|
// reduce the rate of false positive.
|
||
|
// For example, in
|
||
|
// \code
|
||
|
// struct A;
|
||
|
// struct B { friend A; };
|
||
|
// \endcode
|
||
|
// `A` will not be marked as "referenced" in the AST.
|
||
|
if (const TypeSourceInfo *Tsi = Decl->getFriendType()) {
|
||
|
QualType Desugared = Tsi->getType().getDesugaredType(*Result.Context);
|
||
|
FriendTypes.insert(Desugared.getTypePtr());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool haveSameNamespaceOrTranslationUnit(const CXXRecordDecl *Decl1,
|
||
|
const CXXRecordDecl *Decl2) {
|
||
|
const DeclContext *ParentDecl1 = Decl1->getLexicalParent();
|
||
|
const DeclContext *ParentDecl2 = Decl2->getLexicalParent();
|
||
|
|
||
|
// Since we only matched declarations whose parent is Namespace or
|
||
|
// TranslationUnit declaration, the parent should be either a translation unit
|
||
|
// or namespace.
|
||
|
if (ParentDecl1->getDeclKind() == Decl::TranslationUnit ||
|
||
|
ParentDecl2->getDeclKind() == Decl::TranslationUnit) {
|
||
|
return ParentDecl1 == ParentDecl2;
|
||
|
}
|
||
|
assert(ParentDecl1->getDeclKind() == Decl::Namespace &&
|
||
|
"ParentDecl1 declaration must be a namespace");
|
||
|
assert(ParentDecl2->getDeclKind() == Decl::Namespace &&
|
||
|
"ParentDecl2 declaration must be a namespace");
|
||
|
auto *Ns1 = NamespaceDecl::castFromDeclContext(ParentDecl1);
|
||
|
auto *Ns2 = NamespaceDecl::castFromDeclContext(ParentDecl2);
|
||
|
return Ns1->getOriginalNamespace() == Ns2->getOriginalNamespace();
|
||
|
}
|
||
|
|
||
|
static std::string getNameOfNamespace(const CXXRecordDecl *Decl) {
|
||
|
const auto *ParentDecl = Decl->getLexicalParent();
|
||
|
if (ParentDecl->getDeclKind() == Decl::TranslationUnit) {
|
||
|
return "(global)";
|
||
|
}
|
||
|
const auto *NsDecl = cast<NamespaceDecl>(ParentDecl);
|
||
|
std::string Ns;
|
||
|
llvm::raw_string_ostream OStream(Ns);
|
||
|
NsDecl->printQualifiedName(OStream);
|
||
|
OStream.flush();
|
||
|
return Ns.empty() ? "(global)" : Ns;
|
||
|
}
|
||
|
|
||
|
void ForwardDeclarationNamespaceCheck::onEndOfTranslationUnit() {
|
||
|
// Iterate each group of declarations by name.
|
||
|
for (const auto &KeyValuePair : DeclNameToDeclarations) {
|
||
|
const auto &Declarations = KeyValuePair.second;
|
||
|
// If more than 1 declaration exists, we check if all are in the same
|
||
|
// namespace.
|
||
|
for (const auto *CurDecl : Declarations) {
|
||
|
if (CurDecl->hasDefinition() || CurDecl->isReferenced()) {
|
||
|
continue; // Skip forward declarations that are used/referenced.
|
||
|
}
|
||
|
if (FriendTypes.count(CurDecl->getTypeForDecl()) != 0) {
|
||
|
continue; // Skip forward declarations referenced as friend.
|
||
|
}
|
||
|
if (CurDecl->getLocation().isMacroID() ||
|
||
|
CurDecl->getLocation().isInvalid()) {
|
||
|
continue;
|
||
|
}
|
||
|
// Compare with all other declarations with the same name.
|
||
|
for (const auto *Decl : Declarations) {
|
||
|
if (Decl == CurDecl) {
|
||
|
continue; // Don't compare with self.
|
||
|
}
|
||
|
if (!CurDecl->hasDefinition() &&
|
||
|
!haveSameNamespaceOrTranslationUnit(CurDecl, Decl)) {
|
||
|
diag(CurDecl->getLocation(),
|
||
|
"declaration %0 is never referenced, but a declaration with "
|
||
|
"the same name found in another namespace '%1'")
|
||
|
<< CurDecl << getNameOfNamespace(Decl);
|
||
|
diag(Decl->getLocation(), "a declaration of %0 is found here",
|
||
|
DiagnosticIDs::Note)
|
||
|
<< Decl;
|
||
|
break; // FIXME: We only generate one warning for each declaration.
|
||
|
}
|
||
|
}
|
||
|
// Check if a definition in another namespace exists.
|
||
|
const auto DeclName = CurDecl->getName();
|
||
|
if (DeclNameToDefinitions.find(DeclName) == DeclNameToDefinitions.end()) {
|
||
|
continue; // No definition in this translation unit, we can skip it.
|
||
|
}
|
||
|
// Make a warning for each definition with the same name (in other
|
||
|
// namespaces).
|
||
|
const auto &Definitions = DeclNameToDefinitions[DeclName];
|
||
|
for (const auto *Def : Definitions) {
|
||
|
diag(CurDecl->getLocation(),
|
||
|
"no definition found for %0, but a definition with "
|
||
|
"the same name %1 found in another namespace '%2'")
|
||
|
<< CurDecl << Def << getNameOfNamespace(Def);
|
||
|
diag(Def->getLocation(), "a definition of %0 is found here",
|
||
|
DiagnosticIDs::Note)
|
||
|
<< Def;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace bugprone
|
||
|
} // namespace tidy
|
||
|
} // namespace clang
|