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
 |