summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp125
1 files changed, 125 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
new file mode 100644
index 00000000000..c5632a7301e
--- /dev/null
+++ b/clang-tools-extra/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
OpenPOWER on IntegriCloud