diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp')
| -rw-r--r-- | clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp new file mode 100644 index 00000000000..c5632a7301e --- /dev/null +++ b/clang-tools-extra/clang-tidy/fuchsia/MultipleInheritanceCheck.cpp @@ -0,0 +1,125 @@ +//===--- MultipleInheritanceCheck.cpp - clang-tidy-------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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 { + +AST_MATCHER(CXXRecordDecl, hasBases) { + if (Node.hasDefinition()) + return Node.getNumBases() > 0; + return false; +} + +// 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) { + 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 { + 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) { + // 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>(); + assert(Ty && "RecordType of base class is unknown"); + 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) { + // Requires C++. + if (!getLangOpts().CPlusPlus) + return; + + // Match declarations which have bases. + Finder->addMatcher(cxxRecordDecl(hasBases()).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>(); + assert(Ty && "RecordType of base class is unknown"); + 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>(); + assert(Ty && "RecordType of base class is unknown"); + const auto *Base = cast<CXXRecordDecl>(Ty->getDecl()->getDefinition()); + if (!isInterface(Base)) NumConcrete++; + } + + if (NumConcrete > 1) { + diag(D->getLocStart(), + "inheriting mulitple classes that aren't " + "pure virtual is discouraged"); + } + } +} + +} // namespace fuchsia +} // namespace tidy +} // namespace clang |

