1587deb45SJulie Hockett //===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===//
2587deb45SJulie Hockett //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6587deb45SJulie Hockett //
7587deb45SJulie Hockett //===----------------------------------------------------------------------===//
8587deb45SJulie Hockett
9587deb45SJulie Hockett #include "MultipleInheritanceCheck.h"
10587deb45SJulie Hockett #include "clang/AST/ASTContext.h"
11587deb45SJulie Hockett #include "clang/ASTMatchers/ASTMatchFinder.h"
12587deb45SJulie Hockett
13587deb45SJulie Hockett using namespace clang;
14587deb45SJulie Hockett using namespace clang::ast_matchers;
15587deb45SJulie Hockett
167d2ea6c4SCarlos Galvez namespace clang::tidy::fuchsia {
17587deb45SJulie Hockett
18f8c99297SBenjamin Kramer namespace {
AST_MATCHER(CXXRecordDecl,hasBases)19587deb45SJulie Hockett AST_MATCHER(CXXRecordDecl, hasBases) {
20587deb45SJulie Hockett if (Node.hasDefinition())
21587deb45SJulie Hockett return Node.getNumBases() > 0;
22587deb45SJulie Hockett return false;
23587deb45SJulie Hockett }
24f8c99297SBenjamin Kramer } // namespace
25587deb45SJulie Hockett
26587deb45SJulie Hockett // Adds a node (by name) to the interface map, if it was not present in the map
27587deb45SJulie Hockett // previously.
addNodeToInterfaceMap(const CXXRecordDecl * Node,bool IsInterface)28587deb45SJulie Hockett void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
29ab2d3ce4SAlexander Kornienko bool IsInterface) {
30778a33a0SIlya Biryukov assert(Node->getIdentifier());
31587deb45SJulie Hockett StringRef Name = Node->getIdentifier()->getName();
32ab2d3ce4SAlexander Kornienko InterfaceMap.insert(std::make_pair(Name, IsInterface));
33587deb45SJulie Hockett }
34587deb45SJulie Hockett
35587deb45SJulie Hockett // Returns "true" if the boolean "isInterface" has been set to the
36587deb45SJulie Hockett // interface status of the current Node. Return "false" if the
37587deb45SJulie Hockett // interface status for the current node is not yet known.
getInterfaceStatus(const CXXRecordDecl * Node,bool & IsInterface) const38587deb45SJulie Hockett bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
39ab2d3ce4SAlexander Kornienko bool &IsInterface) const {
40778a33a0SIlya Biryukov assert(Node->getIdentifier());
41587deb45SJulie Hockett StringRef Name = Node->getIdentifier()->getName();
42587deb45SJulie Hockett llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
43587deb45SJulie Hockett if (Pair == InterfaceMap.end())
44587deb45SJulie Hockett return false;
45ab2d3ce4SAlexander Kornienko IsInterface = Pair->second;
46587deb45SJulie Hockett return true;
47587deb45SJulie Hockett }
48587deb45SJulie Hockett
isCurrentClassInterface(const CXXRecordDecl * Node) const49587deb45SJulie Hockett bool MultipleInheritanceCheck::isCurrentClassInterface(
50587deb45SJulie Hockett const CXXRecordDecl *Node) const {
51587deb45SJulie Hockett // Interfaces should have no fields.
52587deb45SJulie Hockett if (!Node->field_empty()) return false;
53587deb45SJulie Hockett
54587deb45SJulie Hockett // Interfaces should have exclusively pure methods.
55587deb45SJulie Hockett return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
56*e90e43fbScor3ntin return M->isUserProvided() && !M->isPureVirtual() && !M->isStatic();
57587deb45SJulie Hockett });
58587deb45SJulie Hockett }
59587deb45SJulie Hockett
isInterface(const CXXRecordDecl * Node)60587deb45SJulie Hockett bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
61778a33a0SIlya Biryukov if (!Node->getIdentifier())
62778a33a0SIlya Biryukov return false;
63778a33a0SIlya Biryukov
64587deb45SJulie Hockett // Short circuit the lookup if we have analyzed this record before.
65cbdc3e1bSPiotr Zegar bool PreviousIsInterfaceResult = false;
66587deb45SJulie Hockett if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
67587deb45SJulie Hockett return PreviousIsInterfaceResult;
68587deb45SJulie Hockett
69587deb45SJulie Hockett // To be an interface, all base classes must be interfaces as well.
70587deb45SJulie Hockett for (const auto &I : Node->bases()) {
71587deb45SJulie Hockett if (I.isVirtual()) continue;
72587deb45SJulie Hockett const auto *Ty = I.getType()->getAs<RecordType>();
739a26bf93SJulie Hockett if (!Ty) continue;
74587deb45SJulie Hockett const RecordDecl *D = Ty->getDecl()->getDefinition();
75587deb45SJulie Hockett if (!D) continue;
76587deb45SJulie Hockett const auto *Base = cast<CXXRecordDecl>(D);
77587deb45SJulie Hockett if (!isInterface(Base)) {
78587deb45SJulie Hockett addNodeToInterfaceMap(Node, false);
79587deb45SJulie Hockett return false;
80587deb45SJulie Hockett }
81587deb45SJulie Hockett }
82587deb45SJulie Hockett
83587deb45SJulie Hockett bool CurrentClassIsInterface = isCurrentClassInterface(Node);
84587deb45SJulie Hockett addNodeToInterfaceMap(Node, CurrentClassIsInterface);
85587deb45SJulie Hockett return CurrentClassIsInterface;
86587deb45SJulie Hockett }
87587deb45SJulie Hockett
registerMatchers(MatchFinder * Finder)88587deb45SJulie Hockett void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
89587deb45SJulie Hockett // Match declarations which have bases.
90df0c8f25SNathan James Finder->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),
91df0c8f25SNathan James this);
92587deb45SJulie Hockett }
93587deb45SJulie Hockett
check(const MatchFinder::MatchResult & Result)94587deb45SJulie Hockett void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
95587deb45SJulie Hockett if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
96678e3ee1SFangrui Song // Check against map to see if the class inherits from multiple
97587deb45SJulie Hockett // concrete classes
98587deb45SJulie Hockett unsigned NumConcrete = 0;
99587deb45SJulie Hockett for (const auto &I : D->bases()) {
1009a26bf93SJulie Hockett if (I.isVirtual()) continue;
101587deb45SJulie Hockett const auto *Ty = I.getType()->getAs<RecordType>();
1029a26bf93SJulie Hockett if (!Ty) continue;
103587deb45SJulie Hockett const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
104587deb45SJulie Hockett if (!isInterface(Base)) NumConcrete++;
105587deb45SJulie Hockett }
106587deb45SJulie Hockett
107587deb45SJulie Hockett // Check virtual bases to see if there is more than one concrete
108587deb45SJulie Hockett // non-virtual base.
109587deb45SJulie Hockett for (const auto &V : D->vbases()) {
110587deb45SJulie Hockett const auto *Ty = V.getType()->getAs<RecordType>();
1119a26bf93SJulie Hockett if (!Ty) continue;
112587deb45SJulie Hockett const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
113587deb45SJulie Hockett if (!isInterface(Base)) NumConcrete++;
114587deb45SJulie Hockett }
115587deb45SJulie Hockett
116587deb45SJulie Hockett if (NumConcrete > 1) {
117abdd042bSKazuaki Ishizaki diag(D->getBeginLoc(), "inheriting multiple classes that aren't "
118587deb45SJulie Hockett "pure virtual is discouraged");
119587deb45SJulie Hockett }
120587deb45SJulie Hockett }
121587deb45SJulie Hockett }
122587deb45SJulie Hockett
1237d2ea6c4SCarlos Galvez } // namespace clang::tidy::fuchsia
124