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