128 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- 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 {
 | 
						|
namespace tidy {
 | 
						|
namespace 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 fuchsia
 | 
						|
}  // namespace tidy
 | 
						|
}  // namespace clang
 |