forked from OSchip/llvm-project
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
|