1 //===--- ComparePointerToMemberVirtualFunctionCheck.cpp - clang-tidy ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "ComparePointerToMemberVirtualFunctionCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/ASTTypeTraits.h"
12 #include "clang/AST/DeclCXX.h"
13 #include "clang/AST/OperationKinds.h"
14 #include "clang/AST/Type.h"
15 #include "clang/ASTMatchers/ASTMatchFinder.h"
16 #include "clang/ASTMatchers/ASTMatchers.h"
17 #include "clang/ASTMatchers/ASTMatchersMacros.h"
18 #include "clang/Basic/Diagnostic.h"
19 #include "clang/Basic/DiagnosticIDs.h"
20 #include "llvm/ADT/SmallVector.h"
22 using namespace clang::ast_matchers
;
24 namespace clang::tidy::bugprone
{
28 AST_MATCHER(CXXMethodDecl
, isVirtual
) { return Node
.isVirtual(); }
30 static const char *const ErrorMsg
=
31 "comparing a pointer to member virtual function with other pointer is "
32 "unspecified behavior, only compare it with a null-pointer constant for "
37 void ComparePointerToMemberVirtualFunctionCheck::registerMatchers(
38 MatchFinder
*Finder
) {
40 auto DirectMemberVirtualFunctionPointer
= unaryOperator(
41 allOf(hasOperatorName("&"),
42 hasUnaryOperand(declRefExpr(to(cxxMethodDecl(isVirtual()))))));
43 auto IndirectMemberPointer
=
44 ignoringImpCasts(declRefExpr().bind("indirect_member_pointer"));
48 allOf(hasAnyOperatorName("==", "!="),
50 hasType(memberPointerType(pointee(functionType())))),
51 anyOf(hasEitherOperand(DirectMemberVirtualFunctionPointer
),
52 hasEitherOperand(IndirectMemberPointer
)),
53 unless(hasEitherOperand(
54 castExpr(hasCastKind(CK_NullToMemberPointer
))))))
55 .bind("binary_operator"),
59 void ComparePointerToMemberVirtualFunctionCheck::check(
60 const MatchFinder::MatchResult
&Result
) {
61 const auto *BO
= Result
.Nodes
.getNodeAs
<BinaryOperator
>("binary_operator");
63 Result
.Nodes
.getNodeAs
<DeclRefExpr
>("indirect_member_pointer");
66 // compare with pointer to member virtual function.
67 diag(BO
->getOperatorLoc(), ErrorMsg
);
70 // compare with variable which type is pointer to member function.
71 llvm::SmallVector
<SourceLocation
, 12U> SameSignatureVirtualMethods
{};
72 const auto *MPT
= cast
<MemberPointerType
>(DRE
->getType().getCanonicalType());
73 const Type
*T
= MPT
->getClass();
76 const CXXRecordDecl
*RD
= T
->getAsCXXRecordDecl();
80 constexpr bool StopVisit
= false;
82 auto VisitSameSignatureVirtualMethods
=
83 [&](const CXXRecordDecl
*CurrentRecordDecl
) -> bool {
84 bool Ret
= !StopVisit
;
85 for (const auto *MD
: CurrentRecordDecl
->methods()) {
86 if (MD
->isVirtual() && MD
->getType() == MPT
->getPointeeType()) {
87 SameSignatureVirtualMethods
.push_back(MD
->getBeginLoc());
94 if (StopVisit
!= VisitSameSignatureVirtualMethods(RD
)) {
95 RD
->forallBases(VisitSameSignatureVirtualMethods
);
98 if (!SameSignatureVirtualMethods
.empty()) {
99 diag(BO
->getOperatorLoc(), ErrorMsg
);
100 for (const auto Loc
: SameSignatureVirtualMethods
)
101 diag(Loc
, "potential member virtual function is declared here.",
102 DiagnosticIDs::Note
);
106 } // namespace clang::tidy::bugprone