xref: /llvm-project/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp (revision 587deb45577750344251d2595300c2b68a431b5b)
1 //===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "MultipleInheritanceCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang;
15 using namespace clang::ast_matchers;
16 
17 namespace clang {
18 namespace tidy {
19 namespace fuchsia {
20 
21 AST_MATCHER(CXXRecordDecl, hasBases) {
22   if (Node.hasDefinition())
23     return Node.getNumBases() > 0;
24   return false;
25 }
26 
27 // Adds a node (by name) to the interface map, if it was not present in the map
28 // previously.
29 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
30                                                      bool isInterface) {
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.
38 bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
39                                                   bool &isInterface) const {
40   StringRef Name = Node->getIdentifier()->getName();
41   llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
42   if (Pair == InterfaceMap.end())
43     return false;
44   isInterface = Pair->second;
45   return true;
46 }
47 
48 bool MultipleInheritanceCheck::isCurrentClassInterface(
49     const CXXRecordDecl *Node) const {
50   // Interfaces should have no fields.
51   if (!Node->field_empty()) return false;
52 
53   // Interfaces should have exclusively pure methods.
54   return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
55     return M->isUserProvided() && !M->isPure() && !M->isStatic();
56   });
57 }
58 
59 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
60   // Short circuit the lookup if we have analyzed this record before.
61   bool PreviousIsInterfaceResult;
62   if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
63     return PreviousIsInterfaceResult;
64 
65   // To be an interface, all base classes must be interfaces as well.
66   for (const auto &I : Node->bases()) {
67     if (I.isVirtual()) continue;
68     const auto *Ty = I.getType()->getAs<RecordType>();
69     assert(Ty && "RecordType of base class is unknown");
70     const RecordDecl *D = Ty->getDecl()->getDefinition();
71     if (!D) continue;
72     const auto *Base = cast<CXXRecordDecl>(D);
73     if (!isInterface(Base)) {
74       addNodeToInterfaceMap(Node, false);
75       return false;
76     }
77   }
78 
79   bool CurrentClassIsInterface = isCurrentClassInterface(Node);
80   addNodeToInterfaceMap(Node, CurrentClassIsInterface);
81   return CurrentClassIsInterface;
82 }
83 
84 void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
85   // Requires C++.
86   if (!getLangOpts().CPlusPlus)
87     return;
88 
89   // Match declarations which have bases.
90   Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this);
91 }
92 
93 void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
94   if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
95     // Check against map to see if if the class inherits from multiple
96     // concrete classes
97     unsigned NumConcrete = 0;
98     for (const auto &I : D->bases()) {
99       if (I.isVirtual()) continue;
100       const auto *Ty = I.getType()->getAs<RecordType>();
101       assert(Ty && "RecordType of base class is unknown");
102       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
103       if (!isInterface(Base)) NumConcrete++;
104     }
105 
106     // Check virtual bases to see if there is more than one concrete
107     // non-virtual base.
108     for (const auto &V : D->vbases()) {
109       const auto *Ty = V.getType()->getAs<RecordType>();
110       assert(Ty && "RecordType of base class is unknown");
111       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
112       if (!isInterface(Base)) NumConcrete++;
113     }
114 
115     if (NumConcrete > 1) {
116       diag(D->getLocStart(),
117            "inheriting mulitple classes that aren't "
118            "pure virtual is discouraged");
119     }
120   }
121 }
122 
123 }  // namespace fuchsia
124 }  // namespace tidy
125 }  // namespace clang
126