aboutsummaryrefslogtreecommitdiff
path: root/clang-tidy/fuchsia
diff options
context:
space:
mode:
authorJulie Hockett <juliehockett@google.com>2018-01-19 23:59:59 +0000
committerJulie Hockett <juliehockett@google.com>2018-01-19 23:59:59 +0000
commitc573a6e96c91a1e33a2b678694fdfba6c7089d73 (patch)
tree40b0afd063ef72c0c34fc007d5adfc026b4628e6 /clang-tidy/fuchsia
parent5f221d4643031d88c0cf43ec5cec10f3bacf2df4 (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.txt1
-rw-r--r--clang-tidy/fuchsia/FuchsiaTidyModule.cpp3
-rw-r--r--clang-tidy/fuchsia/MultipleInheritanceCheck.cpp125
-rw-r--r--clang-tidy/fuchsia/MultipleInheritanceCheck.h48
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