clang-tools 23.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::misc {
17
18namespace {
19AST_MATCHER(CXXRecordDecl, hasBases) {
20 return Node.hasDefinition() && Node.getNumBases() > 0;
21}
22} // namespace
23
24bool MultipleInheritanceCheck::isInterface(const CXXBaseSpecifier &Base) {
25 const CXXRecordDecl *const Node = Base.getType()->getAsCXXRecordDecl();
26 if (!Node)
27 return true;
28
29 assert(Node->isCompleteDefinition());
30
31 // Short circuit the lookup if we have analyzed this record before.
32 if (const auto CachedValue = InterfaceMap.find(Node);
33 CachedValue != InterfaceMap.end())
34 return CachedValue->second;
35
36 // To be an interface, a class must have...
37 const bool CurrentClassIsInterface =
38 // ...no bases that aren't interfaces...
39 llvm::none_of(Node->bases(),
40 [&](const CXXBaseSpecifier &I) {
41 return !I.isVirtual() && !isInterface(I);
42 }) &&
43 // ...no fields, and...
44 Node->field_empty() &&
45 // ...no methods that aren't pure virtual.
46 llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
47 return M->isUserProvided() && !M->isPureVirtual() && !M->isStatic();
48 });
49
50 InterfaceMap.try_emplace(Node, CurrentClassIsInterface);
51 return CurrentClassIsInterface;
52}
53
55 Finder->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),
56 this);
57}
58
59void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
60 const auto &D = *Result.Nodes.getNodeAs<CXXRecordDecl>("decl");
61 // Collect the direct and virtual concrete bases of the class.
62 SmallVector<const CXXRecordDecl *> DirectConcreteBases;
63 for (const CXXBaseSpecifier &Base : D.bases())
64 if (!Base.isVirtual() && !isInterface(Base))
65 DirectConcreteBases.push_back(Base.getType()->getAsCXXRecordDecl());
66
67 SmallVector<const CXXRecordDecl *> VirtualConcreteBases;
68 for (const CXXBaseSpecifier &VBase : D.vbases())
69 if (!isInterface(VBase))
70 VirtualConcreteBases.push_back(VBase.getType()->getAsCXXRecordDecl());
71
72 unsigned NumConcrete = DirectConcreteBases.size();
73
74 // Count only virtual concrete bases that introduce an additional
75 // implementation base, skipping those already represented by a more derived
76 // concrete base.
77 NumConcrete += llvm::count_if(
78 VirtualConcreteBases, [&](const CXXRecordDecl *VirtualBase) {
79 const bool HiddenByMoreDerivedVirtualBase = llvm::any_of(
80 VirtualConcreteBases, [&](const CXXRecordDecl *OtherVirtualBase) {
81 return VirtualBase != OtherVirtualBase &&
82 OtherVirtualBase->isVirtuallyDerivedFrom(VirtualBase);
83 });
84 const bool HiddenByDirectConcreteBase = llvm::any_of(
85 DirectConcreteBases, [&](const CXXRecordDecl *DirectBase) {
86 return DirectBase->isVirtuallyDerivedFrom(VirtualBase);
87 });
88 return !HiddenByMoreDerivedVirtualBase && !HiddenByDirectConcreteBase;
89 });
90
91 if (NumConcrete > 1)
92 diag(D.getBeginLoc(), "inheriting multiple classes that aren't "
93 "pure virtual is discouraged");
94}
95
96} // namespace clang::tidy::misc
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++ -*-===//