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 namespace {
22 AST_MATCHER(CXXRecordDecl, hasBases) {
23   if (Node.hasDefinition())
24     return Node.getNumBases() > 0;
25   return false;
26 }
27 } // namespace
28 
29 // Adds a node (by name) to the interface map, if it was not present in the map
30 // previously.
31 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
32                                                      bool isInterface) {
33   StringRef Name = Node->getIdentifier()->getName();
34   InterfaceMap.insert(std::make_pair(Name, isInterface));
35 }
36 
37 // Returns "true" if the boolean "isInterface" has been set to the
38 // interface status of the current Node. Return "false" if the
39 // interface status for the current node is not yet known.
40 bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
41                                                   bool &isInterface) const {
42   StringRef Name = Node->getIdentifier()->getName();
43   llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
44   if (Pair == InterfaceMap.end())
45     return false;
46   isInterface = Pair->second;
47   return true;
48 }
49 
50 bool MultipleInheritanceCheck::isCurrentClassInterface(
51     const CXXRecordDecl *Node) const {
52   // Interfaces should have no fields.
53   if (!Node->field_empty()) 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->isPure() && !M->isStatic();
58   });
59 }
60 
61 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
62   // Short circuit the lookup if we have analyzed this record before.
63   bool PreviousIsInterfaceResult;
64   if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
65     return PreviousIsInterfaceResult;
66 
67   // To be an interface, all base classes must be interfaces as well.
68   for (const auto &I : Node->bases()) {
69     if (I.isVirtual()) continue;
70     const auto *Ty = I.getType()->getAs<RecordType>();
71     if (!Ty) continue;
72     const RecordDecl *D = Ty->getDecl()->getDefinition();
73     if (!D) continue;
74     const auto *Base = cast<CXXRecordDecl>(D);
75     if (!isInterface(Base)) {
76       addNodeToInterfaceMap(Node, false);
77       return false;
78     }
79   }
80 
81   bool CurrentClassIsInterface = isCurrentClassInterface(Node);
82   addNodeToInterfaceMap(Node, CurrentClassIsInterface);
83   return CurrentClassIsInterface;
84 }
85 
86 void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
87   // Requires C++.
88   if (!getLangOpts().CPlusPlus)
89     return;
90 
91   // Match declarations which have bases.
92   Finder->addMatcher(cxxRecordDecl(hasBases()).bind("decl"), this);
93 }
94 
95 void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
96   if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
97     // Check against map to see if if the class inherits from multiple
98     // concrete classes
99     unsigned NumConcrete = 0;
100     for (const auto &I : D->bases()) {
101       if (I.isVirtual()) continue;
102       const auto *Ty = I.getType()->getAs<RecordType>();
103       if (!Ty) continue;
104       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
105       if (!isInterface(Base)) NumConcrete++;
106     }
107 
108     // Check virtual bases to see if there is more than one concrete
109     // non-virtual base.
110     for (const auto &V : D->vbases()) {
111       const auto *Ty = V.getType()->getAs<RecordType>();
112       if (!Ty) continue;
113       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
114       if (!isInterface(Base)) NumConcrete++;
115     }
116 
117     if (NumConcrete > 1) {
118       diag(D->getLocStart(),
119            "inheriting mulitple classes that aren't "
120            "pure virtual is discouraged");
121     }
122   }
123 }
124 
125 }  // namespace fuchsia
126 }  // namespace tidy
127 }  // namespace clang
128