diff options
author | Julie Hockett <juliehockett@google.com> | 2018-01-19 23:59:59 +0000 |
---|---|---|
committer | Julie Hockett <juliehockett@google.com> | 2018-01-19 23:59:59 +0000 |
commit | c573a6e96c91a1e33a2b678694fdfba6c7089d73 (patch) | |
tree | 40b0afd063ef72c0c34fc007d5adfc026b4628e6 /clang-tidy/fuchsia | |
parent | 5f221d4643031d88c0cf43ec5cec10f3bacf2df4 (diff) |
[clang-tidy] Adding Fuchsia checker for multiple inheritance
Adds a check to the Fuchsia module to warn when a class
inherits from multiple classes that are not pure virtual.
See https://fuchsia.googlesource.com/zircon/+/master/docs/cxx.md
for reference.
Differential Revision: https://reviews.llvm.org/D40580
git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@323011 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'clang-tidy/fuchsia')
-rw-r--r-- | clang-tidy/fuchsia/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang-tidy/fuchsia/FuchsiaTidyModule.cpp | 3 | ||||
-rw-r--r-- | clang-tidy/fuchsia/MultipleInheritanceCheck.cpp | 125 | ||||
-rw-r--r-- | clang-tidy/fuchsia/MultipleInheritanceCheck.h | 48 |
4 files changed, 177 insertions, 0 deletions
diff --git a/clang-tidy/fuchsia/CMakeLists.txt b/clang-tidy/fuchsia/CMakeLists.txt index 25da1394..0dfc04ee 100644 --- a/clang-tidy/fuchsia/CMakeLists.txt +++ b/clang-tidy/fuchsia/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangTidyFuchsiaModule DefaultArgumentsCheck.cpp FuchsiaTidyModule.cpp + MultipleInheritanceCheck.cpp OverloadedOperatorCheck.cpp StaticallyConstructedObjectsCheck.cpp TrailingReturnCheck.cpp diff --git a/clang-tidy/fuchsia/FuchsiaTidyModule.cpp b/clang-tidy/fuchsia/FuchsiaTidyModule.cpp index 0eed7407..59c63893 100644 --- a/clang-tidy/fuchsia/FuchsiaTidyModule.cpp +++ b/clang-tidy/fuchsia/FuchsiaTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "DefaultArgumentsCheck.h" +#include "MultipleInheritanceCheck.h" #include "OverloadedOperatorCheck.h" #include "StaticallyConstructedObjectsCheck.h" #include "TrailingReturnCheck.h" @@ -28,6 +29,8 @@ public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck<DefaultArgumentsCheck>( "fuchsia-default-arguments"); + CheckFactories.registerCheck<MultipleInheritanceCheck>( + "fuchsia-multiple-inheritance"); CheckFactories.registerCheck<OverloadedOperatorCheck>( "fuchsia-overloaded-operator"); CheckFactories.registerCheck<StaticallyConstructedObjectsCheck>( diff --git a/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp new file mode 100644 index 00000000..c5632a73 --- /dev/null +++ b/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp @@ -0,0 +1,125 @@ +//===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MultipleInheritanceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang; +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace fuchsia { + +AST_MATCHER(CXXRecordDecl, hasBases) { + if (Node.hasDefinition()) + return Node.getNumBases() > 0; + return false; +} + +// Adds a node (by name) to the interface map, if it was not present in the map +// previously. +void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node, + bool isInterface) { + StringRef Name = Node->getIdentifier()->getName(); + InterfaceMap.insert(std::make_pair(Name, isInterface)); +} + +// Returns "true" if the boolean "isInterface" has been set to the +// interface status of the current Node. Return "false" if the +// interface status for the current node is not yet known. +bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node, + bool &isInterface) const { + StringRef Name = Node->getIdentifier()->getName(); + llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name); + if (Pair == InterfaceMap.end()) + return false; + isInterface = Pair->second; + return true; +} + +bool MultipleInheritanceCheck::isCurrentClassInterface( + const CXXRecordDecl *Node) const { + // Interfaces should have no fields. + if (!Node->field_empty()) return false; + + // Interfaces should have exclusively pure methods. + return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) { + return M->isUserProvided() && !M->isPure() && !M->isStatic(); + }); +} + +bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) { + // Short circuit the lookup if we have analyzed this record before. + bool PreviousIsInterfaceResult; + if (getInterfaceStatus(Node, PreviousIsInterfaceResult)) + return PreviousIsInterfaceResult; + + // To be an interface, all base classes must be interfaces as well. + for (const auto &I : Node->bases()) { + if (I.isVirtual()) continue; + const auto *Ty = I.getType()->getAs<RecordType>(); + assert(Ty && "RecordType of base class is unknown"); + const RecordDecl *D = Ty->getDecl()->getDefinition(); + if (!D) continue; + const auto *Base = cast<CXXRecordDecl>(D); + if (!isInterface(Base)) { + addNodeToInterfaceMap(Node, false); + return false; + } + } + + bool CurrentClassIsInterface = isCurrentClassInterface(Node); + addNodeToInterfaceMap(Node, CurrentClassIsInterface); + return CurrentClassIsInterface; +} + +void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) { + // Requires C++. + if (!getLangOpts().CPlusPlus) + return; + + // Match declarations which have bases. + Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this); +} + +void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) { + // Check against map to see if if the class inherits from multiple + // concrete classes + unsigned NumConcrete = 0; + for (const auto &I : D->bases()) { + if (I.isVirtual()) continue; + const auto *Ty = I.getType()->getAs<RecordType>(); + assert(Ty && "RecordType of base class is unknown"); + const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); + if (!isInterface(Base)) NumConcrete++; + } + + // Check virtual bases to see if there is more than one concrete + // non-virtual base. + for (const auto &V : D->vbases()) { + const auto *Ty = V.getType()->getAs<RecordType>(); + assert(Ty && "RecordType of base class is unknown"); + const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); + if (!isInterface(Base)) NumConcrete++; + } + + if (NumConcrete > 1) { + diag(D->getLocStart(), + "inheriting mulitple classes that aren't " + "pure virtual is discouraged"); + } + } +} + +} // namespace fuchsia +} // namespace tidy +} // namespace clang diff --git a/clang-tidy/fuchsia/MultipleInheritanceCheck.h b/clang-tidy/fuchsia/MultipleInheritanceCheck.h new file mode 100644 index 00000000..6250e3ec --- /dev/null +++ b/clang-tidy/fuchsia/MultipleInheritanceCheck.h @@ -0,0 +1,48 @@ +//===--- MultipleInheritanceCheck.h - clang-tidy-----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_MULTIPLE_INHERITANCE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_MULTIPLE_INHERITANCE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace fuchsia { + +/// Mulitple implementation inheritance is discouraged. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/fuchsia-multiple-inheritance.html +class MultipleInheritanceCheck : public ClangTidyCheck { +public: + MultipleInheritanceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + + void onEndOfTranslationUnit() override { InterfaceMap.clear(); } + +private: + void addNodeToInterfaceMap(const CXXRecordDecl *Node, bool isInterface); + bool getInterfaceStatus(const CXXRecordDecl *Node, bool &isInterface) const; + bool isCurrentClassInterface(const CXXRecordDecl *Node) const; + bool isInterface(const CXXRecordDecl *Node); + + // Contains the identity of each named CXXRecord as an interface. This is + // used to memoize lookup speeds and improve performance from O(N^2) to O(N), + // where N is the number of classes. + llvm::StringMap<bool> InterfaceMap; +}; + +} // namespace fuchsia +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_FUCHSIA_MULTIPLE_INHERITANCE_H |