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