brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.2 KiB · 652dec9 Raw
130 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "MultipleInheritanceCheck.h"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::fuchsia {17 18namespace {19AST_MATCHER(CXXRecordDecl, hasBases) {20  if (Node.hasDefinition())21    return Node.getNumBases() > 0;22  return false;23}24} // namespace25 26// Adds a node (by name) to the interface map, if it was not present in the map27// previously.28void MultipleInheritanceCheck::addNodeToInterfaceMap(const CXXRecordDecl *Node,29                                                     bool IsInterface) {30  assert(Node->getIdentifier());31  const 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 the36// interface status of the current Node. Return "false" if the37// interface status for the current node is not yet known.38bool MultipleInheritanceCheck::getInterfaceStatus(const CXXRecordDecl *Node,39                                                  bool &IsInterface) const {40  assert(Node->getIdentifier());41  const StringRef Name = Node->getIdentifier()->getName();42  auto Pair = InterfaceMap.find(Name);43  if (Pair == InterfaceMap.end())44    return false;45  IsInterface = Pair->second;46  return true;47}48 49bool MultipleInheritanceCheck::isCurrentClassInterface(50    const CXXRecordDecl *Node) const {51  // Interfaces should have no fields.52  if (!Node->field_empty())53    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->isPureVirtual() && !M->isStatic();58  });59}60 61bool MultipleInheritanceCheck::isInterface(const CXXRecordDecl *Node) {62  if (!Node->getIdentifier())63    return false;64 65  // Short circuit the lookup if we have analyzed this record before.66  bool PreviousIsInterfaceResult = false;67  if (getInterfaceStatus(Node, PreviousIsInterfaceResult))68    return PreviousIsInterfaceResult;69 70  // To be an interface, all base classes must be interfaces as well.71  for (const auto &I : Node->bases()) {72    if (I.isVirtual())73      continue;74    const auto *Base = I.getType()->getAsCXXRecordDecl();75    if (!Base)76      continue;77    assert(Base->isCompleteDefinition());78    if (!isInterface(Base)) {79      addNodeToInterfaceMap(Node, false);80      return false;81    }82  }83 84  const bool CurrentClassIsInterface = isCurrentClassInterface(Node);85  addNodeToInterfaceMap(Node, CurrentClassIsInterface);86  return CurrentClassIsInterface;87}88 89void MultipleInheritanceCheck::registerMatchers(MatchFinder *Finder) {90  // Match declarations which have bases.91  Finder->addMatcher(cxxRecordDecl(hasBases(), isDefinition()).bind("decl"),92                     this);93}94 95void MultipleInheritanceCheck::check(const MatchFinder::MatchResult &Result) {96  if (const auto *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl")) {97    // Check against map to see if the class inherits from multiple98    // concrete classes99    unsigned NumConcrete = 0;100    for (const auto &I : D->bases()) {101      if (I.isVirtual())102        continue;103      const auto *Base = I.getType()->getAsCXXRecordDecl();104      if (!Base)105        continue;106      assert(Base->isCompleteDefinition());107      if (!isInterface(Base))108        NumConcrete++;109    }110 111    // Check virtual bases to see if there is more than one concrete112    // non-virtual base.113    for (const auto &V : D->vbases()) {114      const auto *Base = V.getType()->getAsCXXRecordDecl();115      if (!Base)116        continue;117      assert(Base->isCompleteDefinition());118      if (!isInterface(Base))119        NumConcrete++;120    }121 122    if (NumConcrete > 1) {123      diag(D->getBeginLoc(), "inheriting multiple classes that aren't "124                             "pure virtual is discouraged");125    }126  }127}128 129} // namespace clang::tidy::fuchsia130