1 //===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "MultipleInheritanceCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang; 15 using namespace clang::ast_matchers; 16 17 namespace clang { 18 namespace tidy { 19 namespace fuchsia { 20 21 namespace { 22 AST_MATCHER(CXXRecordDecl, hasBases) { 23 if (Node.hasDefinition()) 24 return Node.getNumBases() > 0; 25 return false; 26 } 27 } // namespace 28 29 // Adds a node (by name) to the interface map, if it was not present in the map 30 // previously. 31 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node, 32 bool isInterface) { 33 assert(Node->getIdentifier()); 34 StringRef Name = Node->getIdentifier()->getName(); 35 InterfaceMap.insert(std::make_pair(Name, isInterface)); 36 } 37 38 // Returns "true" if the boolean "isInterface" has been set to the 39 // interface status of the current Node. Return "false" if the 40 // interface status for the current node is not yet known. 41 bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node, 42 bool &isInterface) const { 43 assert(Node->getIdentifier()); 44 StringRef Name = Node->getIdentifier()->getName(); 45 llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name); 46 if (Pair == InterfaceMap.end()) 47 return false; 48 isInterface = Pair->second; 49 return true; 50 } 51 52 bool MultipleInheritanceCheck::isCurrentClassInterface( 53 const CXXRecordDecl *Node) const { 54 // Interfaces should have no fields. 55 if (!Node->field_empty()) return false; 56 57 // Interfaces should have exclusively pure methods. 58 return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) { 59 return M->isUserProvided() && !M->isPure() && !M->isStatic(); 60 }); 61 } 62 63 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) { 64 if (!Node->getIdentifier()) 65 return false; 66 67 // Short circuit the lookup if we have analyzed this record before. 68 bool PreviousIsInterfaceResult; 69 if (getInterfaceStatus(Node, PreviousIsInterfaceResult)) 70 return PreviousIsInterfaceResult; 71 72 // To be an interface, all base classes must be interfaces as well. 73 for (const auto &I : Node->bases()) { 74 if (I.isVirtual()) continue; 75 const auto *Ty = I.getType()->getAs<RecordType>(); 76 if (!Ty) continue; 77 const RecordDecl *D = Ty->getDecl()->getDefinition(); 78 if (!D) continue; 79 const auto *Base = cast<CXXRecordDecl>(D); 80 if (!isInterface(Base)) { 81 addNodeToInterfaceMap(Node, false); 82 return false; 83 } 84 } 85 86 bool CurrentClassIsInterface = isCurrentClassInterface(Node); 87 addNodeToInterfaceMap(Node, CurrentClassIsInterface); 88 return CurrentClassIsInterface; 89 } 90 91 void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) { 92 // Requires C++. 93 if (!getLangOpts().CPlusPlus) 94 return; 95 96 // Match declarations which have bases. 97 Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this); 98 } 99 100 void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { 101 if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) { 102 // Check against map to see if if the class inherits from multiple 103 // concrete classes 104 unsigned NumConcrete = 0; 105 for (const auto &I : D->bases()) { 106 if (I.isVirtual()) continue; 107 const auto *Ty = I.getType()->getAs<RecordType>(); 108 if (!Ty) continue; 109 const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); 110 if (!isInterface(Base)) NumConcrete++; 111 } 112 113 // Check virtual bases to see if there is more than one concrete 114 // non-virtual base. 115 for (const auto &V : D->vbases()) { 116 const auto *Ty = V.getType()->getAs<RecordType>(); 117 if (!Ty) continue; 118 const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); 119 if (!isInterface(Base)) NumConcrete++; 120 } 121 122 if (NumConcrete > 1) { 123 diag(D->getBeginLoc(), "inheriting mulitple classes that aren't " 124 "pure virtual is discouraged"); 125 } 126 } 127 } 128 129 } // namespace fuchsia 130 } // namespace tidy 131 } // namespace clang 132