diff options
author | Alexander Kornienko <alexfh@google.com> | 2017-11-24 14:16:29 +0000 |
---|---|---|
committer | Alexander Kornienko <alexfh@google.com> | 2017-11-24 14:16:29 +0000 |
commit | d4ac4afda75c9ef174e85eea7f40a6ae6cc50ab4 (patch) | |
tree | c43c0be034454ea297f0ccdc84302e9636abed63 /clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp | |
parent | 70cdb5b3914803ca89a96a867a47936d049a4b32 (diff) | |
download | bcm5719-llvm-d4ac4afda75c9ef174e85eea7f40a6ae6cc50ab4.tar.gz bcm5719-llvm-d4ac4afda75c9ef174e85eea7f40a6ae6cc50ab4.zip |
[clang-tidy] Move a few more checks from misc to bugprone.
Summary:
clang_tidy/rename_check.py misc-assert-side-effect bugprone-assert-side-effect
clang_tidy/rename_check.py misc-bool-pointer-implicit-conversion bugprone-bool-pointer-implicit-conversion
clang_tidy/rename_check.py misc-fold-init-type bugprone-fold-init-type
clang_tidy/rename_check.py misc-forward-declaration-namespace bugprone-forward-declaration-namespace
clang_tidy/rename_check.py misc-inaccurate-erase bugprone-inaccurate-erase
clang_tidy/rename_check.py misc-move-forwarding-reference bugprone-move-forwarding-reference
clang_tidy/rename_check.py misc-multiple-statement-macro bugprone-multiple-statement-macro
clang_tidy/rename_check.py misc-use-after-move bugprone-use-after-move
clang_tidy/rename_check.py misc-virtual-near-miss bugprone-virtual-near-miss
Manually fixed a reference to UseAfterMoveCheck in the hicpp module.
Manually fixed header guards.
Reviewers: hokein
Reviewed By: hokein
Subscribers: nemanjai, mgorny, javed.absar, xazax.hun, kbarton, cfe-commits
Differential Revision: https://reviews.llvm.org/D40426
llvm-svn: 318950
Diffstat (limited to 'clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp new file mode 100644 index 00000000000..9ea5b553da2 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/ForwardDeclarationNamespaceCheck.cpp @@ -0,0 +1,174 @@ +//===--- 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 |