• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 using namespace clang;
14 using namespace clang::ast_matchers;
15 
16 namespace clang {
17 namespace tidy {
18 namespace fuchsia {
19 
20 namespace {
AST_MATCHER(CXXRecordDecl,hasBases)21 AST_MATCHER(CXXRecordDecl, hasBases) {
22   if (Node.hasDefinition())
23     return Node.getNumBases() > 0;
24   return false;
25 }
26 } // namespace
27 
28 // Adds a node (by name) to the interface map, if it was not present in the map
29 // previously.
addNodeToInterfaceMap(const CXXRecordDecl * Node,bool isInterface)30 void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
31                                                      bool isInterface) {
32   assert(Node->getIdentifier());
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.
getInterfaceStatus(const CXXRecordDecl * Node,bool & isInterface) const40 bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
41                                                   bool &isInterface) const {
42   assert(Node->getIdentifier());
43   StringRef Name = Node->getIdentifier()->getName();
44   llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
45   if (Pair == InterfaceMap.end())
46     return false;
47   isInterface = Pair->second;
48   return true;
49 }
50 
isCurrentClassInterface(const CXXRecordDecl * Node) const51 bool MultipleInheritanceCheck::isCurrentClassInterface(
52     const CXXRecordDecl *Node) const {
53   // Interfaces should have no fields.
54   if (!Node->field_empty()) return false;
55 
56   // Interfaces should have exclusively pure methods.
57   return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
58     return M->isUserProvided() && !M->isPure() && !M->isStatic();
59   });
60 }
61 
isInterface(const CXXRecordDecl * Node)62 bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
63   if (!Node->getIdentifier())
64     return false;
65 
66   // Short circuit the lookup if we have analyzed this record before.
67   bool PreviousIsInterfaceResult;
68   if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
69     return PreviousIsInterfaceResult;
70 
71   // To be an interface, all base classes must be interfaces as well.
72   for (const auto &I : Node->bases()) {
73     if (I.isVirtual()) continue;
74     const auto *Ty = I.getType()->getAs<RecordType>();
75     if (!Ty) continue;
76     const RecordDecl *D = Ty->getDecl()->getDefinition();
77     if (!D) continue;
78     const auto *Base = cast<CXXRecordDecl>(D);
79     if (!isInterface(Base)) {
80       addNodeToInterfaceMap(Node, false);
81       return false;
82     }
83   }
84 
85   bool CurrentClassIsInterface = isCurrentClassInterface(Node);
86   addNodeToInterfaceMap(Node, CurrentClassIsInterface);
87   return CurrentClassIsInterface;
88 }
89 
registerMatchers(MatchFinder * Finder)90 void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
91   // Match declarations which have bases.
92   Finder->addMatcher(
93       cxxRecordDecl(allOf(hasBases(), isDefinition())).bind("decl"), this);
94 }
95 
check(const MatchFinder::MatchResult & Result)96 void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
97   if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
98     // Check against map to see if if the class inherits from multiple
99     // concrete classes
100     unsigned NumConcrete = 0;
101     for (const auto &I : D->bases()) {
102       if (I.isVirtual()) continue;
103       const auto *Ty = I.getType()->getAs<RecordType>();
104       if (!Ty) continue;
105       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
106       if (!isInterface(Base)) NumConcrete++;
107     }
108 
109     // Check virtual bases to see if there is more than one concrete
110     // non-virtual base.
111     for (const auto &V : D->vbases()) {
112       const auto *Ty = V.getType()->getAs<RecordType>();
113       if (!Ty) continue;
114       const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
115       if (!isInterface(Base)) NumConcrete++;
116     }
117 
118     if (NumConcrete > 1) {
119       diag(D->getBeginLoc(), "inheriting multiple classes that aren't "
120                              "pure virtual is discouraged");
121     }
122   }
123 }
124 
125 }  // namespace fuchsia
126 }  // namespace tidy
127 }  // namespace clang
128