| //===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MultipleInheritanceCheck.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/ASTMatchers/ASTMatchFinder.h" |
| |
| using namespace clang; |
| using namespace clang::ast_matchers; |
| |
| namespace clang { |
| namespace tidy { |
| namespace fuchsia { |
| |
| namespace { |
| AST_MATCHER(CXXRecordDecl, hasBases) { |
| if (Node.hasDefinition()) |
| return Node.getNumBases() > 0; |
| return false; |
| } |
| } // namespace |
| |
| // Adds a node (by name) to the interface map, if it was not present in the map |
| // previously. |
| void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node, |
| bool isInterface) { |
| assert(Node->getIdentifier()); |
| StringRef Name = Node->getIdentifier()->getName(); |
| InterfaceMap.insert(std::make_pair(Name, isInterface)); |
| } |
| |
| // Returns "true" if the boolean "isInterface" has been set to the |
| // interface status of the current Node. Return "false" if the |
| // interface status for the current node is not yet known. |
| bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node, |
| bool &isInterface) const { |
| assert(Node->getIdentifier()); |
| StringRef Name = Node->getIdentifier()->getName(); |
| llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name); |
| if (Pair == InterfaceMap.end()) |
| return false; |
| isInterface = Pair->second; |
| return true; |
| } |
| |
| bool MultipleInheritanceCheck::isCurrentClassInterface( |
| const CXXRecordDecl *Node) const { |
| // Interfaces should have no fields. |
| if (!Node->field_empty()) return false; |
| |
| // Interfaces should have exclusively pure methods. |
| return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) { |
| return M->isUserProvided() && !M->isPure() && !M->isStatic(); |
| }); |
| } |
| |
| bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) { |
| if (!Node->getIdentifier()) |
| return false; |
| |
| // Short circuit the lookup if we have analyzed this record before. |
| bool PreviousIsInterfaceResult; |
| if (getInterfaceStatus(Node, PreviousIsInterfaceResult)) |
| return PreviousIsInterfaceResult; |
| |
| // To be an interface, all base classes must be interfaces as well. |
| for (const auto &I : Node->bases()) { |
| if (I.isVirtual()) continue; |
| const auto *Ty = I.getType()->getAs<RecordType>(); |
| if (!Ty) continue; |
| const RecordDecl *D = Ty->getDecl()->getDefinition(); |
| if (!D) continue; |
| const auto *Base = cast<CXXRecordDecl>(D); |
| if (!isInterface(Base)) { |
| addNodeToInterfaceMap(Node, false); |
| return false; |
| } |
| } |
| |
| bool CurrentClassIsInterface = isCurrentClassInterface(Node); |
| addNodeToInterfaceMap(Node, CurrentClassIsInterface); |
| return CurrentClassIsInterface; |
| } |
| |
| void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) { |
| // Requires C++. |
| if (!getLangOpts().CPlusPlus) |
| return; |
| |
| // Match declarations which have bases. |
| Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this); |
| } |
| |
| void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) { |
| if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) { |
| // Check against map to see if if the class inherits from multiple |
| // concrete classes |
| unsigned NumConcrete = 0; |
| for (const auto &I : D->bases()) { |
| if (I.isVirtual()) continue; |
| const auto *Ty = I.getType()->getAs<RecordType>(); |
| if (!Ty) continue; |
| const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); |
| if (!isInterface(Base)) NumConcrete++; |
| } |
| |
| // Check virtual bases to see if there is more than one concrete |
| // non-virtual base. |
| for (const auto &V : D->vbases()) { |
| const auto *Ty = V.getType()->getAs<RecordType>(); |
| if (!Ty) continue; |
| const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); |
| if (!isInterface(Base)) NumConcrete++; |
| } |
| |
| if (NumConcrete > 1) { |
| diag(D->getLocStart(), |
| "inheriting mulitple classes that aren't " |
| "pure virtual is discouraged"); |
| } |
| } |
| } |
| |
| } // namespace fuchsia |
| } // namespace tidy |
| } // namespace clang |