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