107 lines
3.6 KiB
C++
107 lines
3.6 KiB
C++
|
//===--- 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<BinaryOperator>("binary_operator");
|
||
|
const auto *DRE =
|
||
|
Result.Nodes.getNodeAs<DeclRefExpr>("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<SourceLocation, 12U> SameSignatureVirtualMethods{};
|
||
|
const auto *MPT = cast<MemberPointerType>(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
|