1 //===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "MultipleInheritanceCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang
;
14 using namespace clang::ast_matchers
;
21 AST_MATCHER(CXXRecordDecl
, hasBases
) {
22 if (Node
.hasDefinition())
23 return Node
.getNumBases() > 0;
28 // Adds a node (by name) to the interface map, if it was not present in the map
30 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl
*Node
,
32 assert(Node
->getIdentifier());
33 StringRef Name
= Node
->getIdentifier()->getName();
34 InterfaceMap
.insert(std::make_pair(Name
, IsInterface
));
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())
47 IsInterface
= Pair
->second
;
51 bool MultipleInheritanceCheck::isCurrentClassInterface(
52 const CXXRecordDecl
*Node
) const {
53 // Interfaces should have no fields.
54 if (!Node
->field_empty()) return false;
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();
62 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl
*Node
) {
63 if (!Node
->getIdentifier())
66 // Short circuit the lookup if we have analyzed this record before.
67 bool PreviousIsInterfaceResult
;
68 if (getInterfaceStatus(Node
, PreviousIsInterfaceResult
))
69 return PreviousIsInterfaceResult
;
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
>();
76 const RecordDecl
*D
= Ty
->getDecl()->getDefinition();
78 const auto *Base
= cast
<CXXRecordDecl
>(D
);
79 if (!isInterface(Base
)) {
80 addNodeToInterfaceMap(Node
, false);
85 bool CurrentClassIsInterface
= isCurrentClassInterface(Node
);
86 addNodeToInterfaceMap(Node
, CurrentClassIsInterface
);
87 return CurrentClassIsInterface
;
90 void MultipleInheritanceCheck::registerMatchers(MatchFinder
*Finder
) {
91 // Match declarations which have bases.
93 cxxRecordDecl(allOf(hasBases(), isDefinition())).bind("decl"), this);
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
100 unsigned NumConcrete
= 0;
101 for (const auto &I
: D
->bases()) {
102 if (I
.isVirtual()) continue;
103 const auto *Ty
= I
.getType()->getAs
<RecordType
>();
105 const auto *Base
= cast
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition());
106 if (!isInterface(Base
)) NumConcrete
++;
109 // Check virtual bases to see if there is more than one concrete
111 for (const auto &V
: D
->vbases()) {
112 const auto *Ty
= V
.getType()->getAs
<RecordType
>();
114 const auto *Base
= cast
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition());
115 if (!isInterface(Base
)) NumConcrete
++;
118 if (NumConcrete
> 1) {
119 diag(D
->getBeginLoc(), "inheriting multiple classes that aren't "
120 "pure virtual is discouraged");
125 } // namespace fuchsia