clang-tools 22.0.0git
MultipleInheritanceCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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(std::make_pair(Name, 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 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 bool CurrentClassIsInterface = isCurrentClassInterface(Node);
85 addNodeToInterfaceMap(Node, CurrentClassIsInterface);
86 return CurrentClassIsInterface;
87}
88
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 multiple
98 // concrete classes
99 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 concrete
112 // 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::fuchsia
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//