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
;
16 namespace clang::tidy::fuchsia
{
19 AST_MATCHER(CXXRecordDecl
, hasBases
) {
20 if (Node
.hasDefinition())
21 return Node
.getNumBases() > 0;
26 // Adds a node (by name) to the interface map, if it was not present in the map
28 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl
*Node
,
30 assert(Node
->getIdentifier());
31 StringRef Name
= Node
->getIdentifier()->getName();
32 InterfaceMap
.insert(std::make_pair(Name
, IsInterface
));
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())
45 IsInterface
= Pair
->second
;
49 bool MultipleInheritanceCheck::isCurrentClassInterface(
50 const CXXRecordDecl
*Node
) const {
51 // Interfaces should have no fields.
52 if (!Node
->field_empty()) return false;
54 // Interfaces should have exclusively pure methods.
55 return llvm::none_of(Node
->methods(), [](const CXXMethodDecl
*M
) {
56 return M
->isUserProvided() && !M
->isPureVirtual() && !M
->isStatic();
60 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl
*Node
) {
61 if (!Node
->getIdentifier())
64 // Short circuit the lookup if we have analyzed this record before.
65 bool PreviousIsInterfaceResult
= false;
66 if (getInterfaceStatus(Node
, PreviousIsInterfaceResult
))
67 return PreviousIsInterfaceResult
;
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
>();
74 const RecordDecl
*D
= Ty
->getDecl()->getDefinition();
76 const auto *Base
= cast
<CXXRecordDecl
>(D
);
77 if (!isInterface(Base
)) {
78 addNodeToInterfaceMap(Node
, false);
83 bool CurrentClassIsInterface
= isCurrentClassInterface(Node
);
84 addNodeToInterfaceMap(Node
, CurrentClassIsInterface
);
85 return CurrentClassIsInterface
;
88 void MultipleInheritanceCheck::registerMatchers(MatchFinder
*Finder
) {
89 // Match declarations which have bases.
90 Finder
->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),
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
98 unsigned NumConcrete
= 0;
99 for (const auto &I
: D
->bases()) {
100 if (I
.isVirtual()) continue;
101 const auto *Ty
= I
.getType()->getAs
<RecordType
>();
103 const auto *Base
= cast
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition());
104 if (!isInterface(Base
)) NumConcrete
++;
107 // Check virtual bases to see if there is more than one concrete
109 for (const auto &V
: D
->vbases()) {
110 const auto *Ty
= V
.getType()->getAs
<RecordType
>();
112 const auto *Base
= cast
<CXXRecordDecl
>(Ty
->getDecl()->getDefinition());
113 if (!isInterface(Base
)) NumConcrete
++;
116 if (NumConcrete
> 1) {
117 diag(D
->getBeginLoc(), "inheriting multiple classes that aren't "
118 "pure virtual is discouraged");
123 } // namespace clang::tidy::fuchsia