aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp
blob: 060f3aebcd81ef537bcf84607eb90a315a154a5d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "MultipleInheritanceCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang;
using namespace clang::ast_matchers;

namespace clang::tidy::fuchsia {

namespace {
AST_MATCHER(CXXRecordDecl, hasBases) {
  if (Node.hasDefinition())
    return Node.getNumBases() > 0;
  return false;
}
} // namespace

// Adds a node (by name) to the interface map, if it was not present in the map
// previously.
void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,
                                                     bool IsInterface) {
  assert(Node->getIdentifier());
  StringRef Name = Node->getIdentifier()->getName();
  InterfaceMap.insert(std::make_pair(Name, IsInterface));
}

// Returns "true" if the boolean "isInterface" has been set to the
// interface status of the current Node. Return "false" if the
// interface status for the current node is not yet known.
bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,
                                                  bool &IsInterface) const {
  assert(Node->getIdentifier());
  StringRef Name = Node->getIdentifier()->getName();
  llvm::StringMapConstIterator<bool> Pair = InterfaceMap.find(Name);
  if (Pair == InterfaceMap.end())
    return false;
  IsInterface = Pair->second;
  return true;
}

bool MultipleInheritanceCheck::isCurrentClassInterface(
    const CXXRecordDecl *Node) const {
  // Interfaces should have no fields.
  if (!Node->field_empty()) return false;

  // Interfaces should have exclusively pure methods.
  return llvm::none_of(Node->methods(), [](const CXXMethodDecl *M) {
    return M->isUserProvided() && !M->isPure() && !M->isStatic();
  });
}

bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {
  if (!Node->getIdentifier())
    return false;

  // Short circuit the lookup if we have analyzed this record before.
  bool PreviousIsInterfaceResult;
  if (getInterfaceStatus(Node, PreviousIsInterfaceResult))
    return PreviousIsInterfaceResult;

  // To be an interface, all base classes must be interfaces as well.
  for (const auto &I : Node->bases()) {
    if (I.isVirtual()) continue;
    const auto *Ty = I.getType()->getAs<RecordType>();
    if (!Ty) continue;
    const RecordDecl *D = Ty->getDecl()->getDefinition();
    if (!D) continue;
    const auto *Base = cast<CXXRecordDecl>(D);
    if (!isInterface(Base)) {
      addNodeToInterfaceMap(Node, false);
      return false;
    }
  }

  bool CurrentClassIsInterface = isCurrentClassInterface(Node);
  addNodeToInterfaceMap(Node, CurrentClassIsInterface);
  return CurrentClassIsInterface;
}

void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {
  // Match declarations which have bases.
  Finder->addMatcher(
      cxxRecordDecl(allOf(hasBases(), isDefinition())).bind("decl"), this);
}

void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {
  if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {
    // Check against map to see if if the class inherits from multiple
    // concrete classes
    unsigned NumConcrete = 0;
    for (const auto &I : D->bases()) {
      if (I.isVirtual()) continue;
      const auto *Ty = I.getType()->getAs<RecordType>();
      if (!Ty) continue;
      const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
      if (!isInterface(Base)) NumConcrete++;
    }
    
    // Check virtual bases to see if there is more than one concrete 
    // non-virtual base.
    for (const auto &V : D->vbases()) {
      const auto *Ty = V.getType()->getAs<RecordType>();
      if (!Ty) continue;
      const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition());
      if (!isInterface(Base)) NumConcrete++;
    }

    if (NumConcrete > 1) {
      diag(D->getBeginLoc(), "inheriting multiple classes that aren't "
                             "pure virtual is discouraged");
    }
  }
}

} // namespace clang::tidy::fuchsia