130 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "MultipleInheritanceCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang;14using namespace clang::ast_matchers;15 16namespace clang::tidy::fuchsia {17 18namespace {19AST_MATCHER(CXXRecordDecl, hasBases) {20 if (Node.hasDefinition())21 return Node.getNumBases() > 0;22 return false;23}24} // namespace25 26// Adds a node (by name) to the interface map, if it was not present in the map27// previously.28void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,29 bool IsInterface) {30 assert(Node->getIdentifier());31 const 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 the36// interface status of the current Node. Return "false" if the37// interface status for the current node is not yet known.38bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,39 bool &IsInterface) const {40 assert(Node->getIdentifier());41 const StringRef Name = Node->getIdentifier()->getName();42 auto Pair = InterfaceMap.find(Name);43 if (Pair == InterfaceMap.end())44 return false;45 IsInterface = Pair->second;46 return true;47}48 49bool MultipleInheritanceCheck::isCurrentClassInterface(50 const CXXRecordDecl *Node) const {51 // Interfaces should have no fields.52 if (!Node->field_empty())53 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->isPureVirtual() && !M->isStatic();58 });59}60 61bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {62 if (!Node->getIdentifier())63 return false;64 65 // Short circuit the lookup if we have analyzed this record before.66 bool PreviousIsInterfaceResult = false;67 if (getInterfaceStatus(Node, PreviousIsInterfaceResult))68 return PreviousIsInterfaceResult;69 70 // To be an interface, all base classes must be interfaces as well.71 for (const auto &I : Node->bases()) {72 if (I.isVirtual())73 continue;74 const auto *Base = I.getType()->getAsCXXRecordDecl();75 if (!Base)76 continue;77 assert(Base->isCompleteDefinition());78 if (!isInterface(Base)) {79 addNodeToInterfaceMap(Node, false);80 return false;81 }82 }83 84 const bool CurrentClassIsInterface = isCurrentClassInterface(Node);85 addNodeToInterfaceMap(Node, CurrentClassIsInterface);86 return CurrentClassIsInterface;87}88 89void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {90 // Match declarations which have bases.91 Finder->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),92 this);93}94 95void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {96 if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {97 // Check against map to see if the class inherits from multiple98 // concrete classes99 unsigned NumConcrete = 0;100 for (const auto &I : D->bases()) {101 if (I.isVirtual())102 continue;103 const auto *Base = I.getType()->getAsCXXRecordDecl();104 if (!Base)105 continue;106 assert(Base->isCompleteDefinition());107 if (!isInterface(Base))108 NumConcrete++;109 }110 111 // Check virtual bases to see if there is more than one concrete112 // non-virtual base.113 for (const auto &V : D->vbases()) {114 const auto *Base = V.getType()->getAsCXXRecordDecl();115 if (!Base)116 continue;117 assert(Base->isCompleteDefinition());118 if (!isInterface(Base))119 NumConcrete++;120 }121 122 if (NumConcrete > 1) {123 diag(D->getBeginLoc(), "inheriting multiple classes that aren't "124 "pure virtual is discouraged");125 }126 }127}128 129} // namespace clang::tidy::fuchsia130