//===--- ComparePointerToMemberVirtualFunctionCheck.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 "ComparePointerToMemberVirtualFunctionCheck.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/OperationKinds.h" #include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchersMacros.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticIDs.h" #include "llvm/ADT/SmallVector.h" using namespace clang::ast_matchers; namespace clang::tidy::bugprone { namespace { AST_MATCHER(CXXMethodDecl, isVirtual) { return Node.isVirtual(); } static const char *const ErrorMsg = "comparing a pointer to member virtual function with other pointer is " "unspecified behavior, only compare it with a null-pointer constant for " "equality."; } // namespace void ComparePointerToMemberVirtualFunctionCheck::registerMatchers( MatchFinder *Finder) { auto DirectMemberVirtualFunctionPointer = unaryOperator( allOf(hasOperatorName("&"), hasUnaryOperand(declRefExpr(to(cxxMethodDecl(isVirtual())))))); auto IndirectMemberPointer = ignoringImpCasts(declRefExpr().bind("indirect_member_pointer")); Finder->addMatcher( binaryOperator( allOf(hasAnyOperatorName("==", "!="), hasEitherOperand( hasType(memberPointerType(pointee(functionType())))), anyOf(hasEitherOperand(DirectMemberVirtualFunctionPointer), hasEitherOperand(IndirectMemberPointer)), unless(hasEitherOperand( castExpr(hasCastKind(CK_NullToMemberPointer)))))) .bind("binary_operator"), this); } void ComparePointerToMemberVirtualFunctionCheck::check( const MatchFinder::MatchResult &Result) { const auto *BO = Result.Nodes.getNodeAs("binary_operator"); const auto *DRE = Result.Nodes.getNodeAs("indirect_member_pointer"); if (DRE == nullptr) { // compare with pointer to member virtual function. diag(BO->getOperatorLoc(), ErrorMsg); return; } // compare with variable which type is pointer to member function. llvm::SmallVector SameSignatureVirtualMethods{}; const auto *MPT = cast(DRE->getType().getCanonicalType()); const Type *T = MPT->getClass(); if (T == nullptr) return; const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); if (RD == nullptr) return; constexpr bool StopVisit = false; auto VisitSameSignatureVirtualMethods = [&](const CXXRecordDecl *CurrentRecordDecl) -> bool { bool Ret = !StopVisit; for (const auto *MD : CurrentRecordDecl->methods()) { if (MD->isVirtual() && MD->getType() == MPT->getPointeeType()) { SameSignatureVirtualMethods.push_back(MD->getBeginLoc()); Ret = StopVisit; } } return Ret; }; if (StopVisit != VisitSameSignatureVirtualMethods(RD)) { RD->forallBases(VisitSameSignatureVirtualMethods); } if (!SameSignatureVirtualMethods.empty()) { diag(BO->getOperatorLoc(), ErrorMsg); for (const auto Loc : SameSignatureVirtualMethods) diag(Loc, "potential member virtual function is declared here.", DiagnosticIDs::Note); } } } // namespace clang::tidy::bugprone