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