diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp')
-rw-r--r-- | contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp new file mode 100644 index 0000000000..1c589e3468 --- /dev/null +++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -0,0 +1,240 @@ +//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines a checker that checks virtual method calls during +// construction or destruction of C++ objects. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" + +using namespace clang; +using namespace ento; + +namespace { +enum class ObjectState : bool { CtorCalled, DtorCalled }; +} // end namespace + // FIXME: Ascending over StackFrameContext maybe another method. + +namespace llvm { +template <> struct FoldingSetTrait<ObjectState> { + static inline void Profile(ObjectState X, FoldingSetNodeID &ID) { + ID.AddInteger(static_cast<int>(X)); + } +}; +} // end namespace llvm + +namespace { +class VirtualCallChecker + : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { +public: + // These are going to be null if the respective check is disabled. + mutable std::unique_ptr<BugType> BT_Pure, BT_Impure; + bool ShowFixIts = false; + + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + +private: + void registerCtorDtorCallInState(bool IsBeginFunction, + CheckerContext &C) const; +}; +} // end namespace + +// GDM (generic data map) to the memregion of this for the ctor and dtor. +REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) + +// The function to check if a callexpr is a virtual method call. +static bool isVirtualCall(const CallExpr *CE) { + bool CallIsNonVirtual = false; + + if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { + // The member access is fully qualified (i.e., X::F). + // Treat this as a non-virtual call and do not warn. + if (CME->getQualifier()) + CallIsNonVirtual = true; + + if (const Expr *Base = CME->getBase()) { + // The most derived class is marked final. + if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>()) + CallIsNonVirtual = true; + } + } + + const CXXMethodDecl *MD = + dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); + if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() && + !MD->getParent()->hasAttr<FinalAttr>()) + return true; + return false; +} + +// The BeginFunction callback when enter a constructor or a destructor. +void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { + registerCtorDtorCallInState(true, C); +} + +// The EndFunction callback when leave a constructor or a destructor. +void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + registerCtorDtorCallInState(false, C); +} + +void VirtualCallChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto MC = dyn_cast<CXXMemberCall>(&Call); + if (!MC) + return; + + const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MD) + return; + + ProgramStateRef State = C.getState(); + // Member calls are always represented by a call-expression. + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + if (!isVirtualCall(CE)) + return; + + const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); + const ObjectState *ObState = State->get<CtorDtorMap>(Reg); + if (!ObState) + return; + + bool IsPure = MD->isPure(); + + // At this point we're sure that we're calling a virtual method + // during construction or destruction, so we'll emit a report. + SmallString<128> Msg; + llvm::raw_svector_ostream OS(Msg); + OS << "Call to "; + if (IsPure) + OS << "pure "; + OS << "virtual method '" << MD->getParent()->getDeclName() + << "::" << MD->getDeclName() << "' during "; + if (*ObState == ObjectState::CtorCalled) + OS << "construction "; + else + OS << "destruction "; + if (IsPure) + OS << "has undefined behavior"; + else + OS << "bypasses virtual dispatch"; + + ExplodedNode *N = + IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode(); + if (!N) + return; + + const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure; + if (!BT) { + // The respective check is disabled. + return; + } + + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); + + if (ShowFixIts && !IsPure) { + // FIXME: These hints are valid only when the virtual call is made + // directly from the constructor/destructor. Otherwise the dispatch + // will work just fine from other callees, and the fix may break + // the otherwise correct program. + FixItHint Fixit = FixItHint::CreateInsertion( + CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::"); + Report->addFixItHint(Fixit); + } + + C.emitReport(std::move(Report)); +} + +void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, + CheckerContext &C) const { + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); + if (!MD) + return; + + ProgramStateRef State = C.getState(); + auto &SVB = C.getSValBuilder(); + + // Enter a constructor, set the corresponding memregion be true. + if (isa<CXXConstructorDecl>(MD)) { + auto ThiSVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + const MemRegion *Reg = ThiSVal.getAsRegion(); + if (IsBeginFunction) + State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); + else + State = State->remove<CtorDtorMap>(Reg); + + C.addTransition(State); + return; + } + + // Enter a Destructor, set the corresponding memregion be true. + if (isa<CXXDestructorDecl>(MD)) { + auto ThiSVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + const MemRegion *Reg = ThiSVal.getAsRegion(); + if (IsBeginFunction) + State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); + else + State = State->remove<CtorDtorMap>(Reg); + + C.addTransition(State); + return; + } +} + +void ento::registerVirtualCallModeling(CheckerManager &Mgr) { + Mgr.registerChecker<VirtualCallChecker>(); +} + +void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.getChecker<VirtualCallChecker>(); + Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(), + "Pure virtual method call", + categories::CXXObjectLifecycle); +} + +void ento::registerVirtualCallChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.getChecker<VirtualCallChecker>(); + if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "PureOnly")) { + Chk->BT_Impure = std::make_unique<BugType>( + Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch", + categories::CXXObjectLifecycle); + Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "ShowFixIts"); + } +} + +bool ento::shouldRegisterVirtualCallModeling(const CheckerManager &mgr) { + const LangOptions &LO = mgr.getLangOpts(); + return LO.CPlusPlus; +} + +bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &mgr) { + const LangOptions &LO = mgr.getLangOpts(); + return LO.CPlusPlus; +} + +bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &mgr) { + const LangOptions &LO = mgr.getLangOpts(); + return LO.CPlusPlus; +} |