1//===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===//
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-exception
6//
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} // namespace
25
26// Adds a node (by name) to the interface map, if it was not present in the map
27// previously.
28void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
29 bool IsInterface) {
30 assert(Node->getIdentifier());
31 StringRef Name = Node->getIdentifier()->getName();
32 InterfaceMap.insert(KV: std::make_pair(x&: Name, y&: IsInterface));
33}
34
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.
38bool 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(Key: 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()) return false;
53
54 // Interfaces should have exclusively pure methods.
55 return llvm::none_of(Range: Node->methods(), P: [](const CXXMethodDecl *M) {
56 return M->isUserProvided() && !M->isPureVirtual() && !M->isStatic();
57 });
58}
59
60bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
61 if (!Node->getIdentifier())
62 return false;
63
64 // Short circuit the lookup if we have analyzed this record before.
65 bool PreviousIsInterfaceResult = false;
66 if (getInterfaceStatus(Node, IsInterface&: PreviousIsInterfaceResult))
67 return PreviousIsInterfaceResult;
68
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>();
73 if (!Ty) continue;
74 const RecordDecl *D = Ty->getDecl()->getDefinition();
75 if (!D) continue;
76 const auto *Base = cast<CXXRecordDecl>(Val: D);
77 if (!isInterface(Node: Base)) {
78 addNodeToInterfaceMap(Node, IsInterface: false);
79 return false;
80 }
81 }
82
83 bool CurrentClassIsInterface = isCurrentClassInterface(Node);
84 addNodeToInterfaceMap(Node, IsInterface: CurrentClassIsInterface);
85 return CurrentClassIsInterface;
86}
87
88void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
89 // Match declarations which have bases.
90 Finder->addMatcher(NodeMatch: cxxRecordDecl(hasBases(), isDefinition()).bind(ID: "decl"),
91 Action: this);
92}
93
94void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
95 if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>(ID: "decl")) {
96 // Check against map to see if the class inherits from multiple
97 // concrete classes
98 unsigned NumConcrete = 0;
99 for (const auto &I : D->bases()) {
100 if (I.isVirtual()) continue;
101 const auto *Ty = I.getType()->getAs<RecordType>();
102 if (!Ty) continue;
103 const auto *Base = cast<CXXRecordDecl>(Val: Ty->getDecl()->getDefinition());
104 if (!isInterface(Node: Base)) NumConcrete++;
105 }
106
107 // Check virtual bases to see if there is more than one concrete
108 // non-virtual base.
109 for (const auto &V : D->vbases()) {
110 const auto *Ty = V.getType()->getAs<RecordType>();
111 if (!Ty) continue;
112 const auto *Base = cast<CXXRecordDecl>(Val: Ty->getDecl()->getDefinition());
113 if (!isInterface(Node: Base)) NumConcrete++;
114 }
115
116 if (NumConcrete > 1) {
117 diag(D->getBeginLoc(), "inheriting multiple classes that aren't "
118 "pure virtual is discouraged");
119 }
120 }
121}
122
123} // namespace clang::tidy::fuchsia
124

source code of clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp