|  | //===--- 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 { | 
|  |  | 
|  | namespace { | 
|  | AST_MATCHER(CXXRecordDecl, hasBases) { | 
|  | if (Node.hasDefinition()) | 
|  | return Node.getNumBases() > 0; | 
|  | return false; | 
|  | } | 
|  | } // namespace | 
|  |  | 
|  | // 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) { | 
|  | assert(Node->getIdentifier()); | 
|  | 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 { | 
|  | assert(Node->getIdentifier()); | 
|  | 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) { | 
|  | if (!Node->getIdentifier()) | 
|  | return false; | 
|  |  | 
|  | // 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>(); | 
|  | if (!Ty) continue; | 
|  | 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>(); | 
|  | if (!Ty) continue; | 
|  | 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>(); | 
|  | if (!Ty) continue; | 
|  | 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 |