Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -431,6 +431,10 @@ return CallArgumentIndex; } + /// If the call returns a C++ record type then the region of its return value + // can be retrieved from its construction context. + Optional getReturnValueUnderConstruction(unsigned BlockCount) const; + // Iterator access to formal parameters and their types. private: struct GetTypeFn { Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -686,6 +686,11 @@ const CallEvent &Call, const EvalCallOptions &CallOpts = {}); + /// Retrieve the value under construction from the construction context + static Optional retrieveFromConstructionContext( + ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC); + private: ProgramStateRef finishArgumentConstruction(ProgramStateRef State, const CallEvent &Call); Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -536,6 +536,44 @@ // FIXME: Variadic arguments are not handled at all right now. } +static const ConstructionContext +*getConstructionContext(const CallEvent &Call, unsigned BlockCount) { + const CFGBlock *Block; + unsigned Index; + + const StackFrameContext *StackFrame = Call.getCalleeStackFrame(BlockCount); + if (!StackFrame) + return nullptr; + + Block = StackFrame->getCallSiteBlock(); + if (!Block) + return nullptr; + + Index = StackFrame->getIndex(); + + if(const auto Ctor = (*Block)[Index].getAs()) { + return Ctor->getConstructionContext(); + } + + if (const auto RecCall = (*Block)[Index].getAs()) { + return RecCall->getConstructionContext(); + } + + return nullptr; +} + +Optional +CallEvent::getReturnValueUnderConstruction(unsigned BlockCount) const { + const auto *CC = getConstructionContext(*this, BlockCount); + if (!CC) + return None; + + Optional RetVal = + ExprEngine::retrieveFromConstructionContext(getState(), + getLocationContext(), CC); + return RetVal; +} + ArrayRef AnyFunctionCall::parameters() const { const FunctionDecl *D = getDecl(); if (!D) Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -109,6 +109,96 @@ return LValue; } +Optional ExprEngine::retrieveFromConstructionContext( + ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC) { + if (CC) { + switch (CC->getKind()) { + case ConstructionContext::CXX17ElidedCopyVariableKind: + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast(CC); + const auto *DS = DSCC->getDeclStmt(); + return getObjectUnderConstruction(State, DS, LCtx); + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast(CC); + const auto *Init = ICC->getCXXCtorInitializer(); + return getObjectUnderConstruction(State, Init, LCtx); + } + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + const StackFrameContext *SFC = LCtx->getStackFrame(); + if (const LocationContext *CallerLCtx = SFC->getParent()) { + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs(); + if (!RTC) { + // We were unable to find the correct construction context for the + // call in the parent stack frame. This is equivalent to not being + // able to find construction context at all. + break; + } + if (isa(CallerLCtx)) { + // Unwrap block invocation contexts. They're mostly part of + // the current stack frame. + CallerLCtx = CallerLCtx->getParent(); + assert(!isa(CallerLCtx)); + } + return retrieveFromConstructionContext( + State, CallerLCtx, RTC->getConstructionContext()); + } + break; + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(State->getAnalysisManager().getAnalyzerOptions() + .ShouldElideConstructors); + const auto *TCC = cast(CC); + Optional RetVal = retrieveFromConstructionContext( + State, LCtx, TCC->getConstructionContextAfterElision()); + if (RetVal.hasValue()) + return RetVal; + LLVM_FALLTHROUGH; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + Optional RetVal; + if (BTE) { + RetVal = getObjectUnderConstruction(State, BTE, LCtx); + if (RetVal.hasValue()) + return RetVal; + } + + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + if (MTE) + RetVal = getObjectUnderConstruction(State, MTE, LCtx); + + return RetVal; + } + case ConstructionContext::ArgumentKind: { + const auto *ACC = cast(CC); + const Expr *E = ACC->getCallLikeExpr(); + unsigned Idx = ACC->getIndex(); + if (const auto *CE = dyn_cast(E)) { + return getObjectUnderConstruction(State, {CE, Idx}, LCtx); + } else if (const auto *CCE = dyn_cast(E)) { + return getObjectUnderConstruction(State, {CCE, Idx}, LCtx); + } else if (const auto *ME = dyn_cast(E)) { + return getObjectUnderConstruction(State, {ME, Idx}, LCtx); + } else if (const auto *BTE = ACC->getCXXBindTemporaryExpr()) { + return getObjectUnderConstruction(State, BTE, LCtx); + } + + LLVM_FALLTHROUGH; + } + default: + return None; + } + } + + return None; +} + std::pair ExprEngine::handleConstructionContext( const Expr *E, ProgramStateRef State, const LocationContext *LCtx, const ConstructionContext *CC, EvalCallOptions &CallOpts) { Index: clang/unittests/StaticAnalyzer/CMakeLists.txt =================================================================== --- clang/unittests/StaticAnalyzer/CMakeLists.txt +++ clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -8,6 +8,7 @@ StoreTest.cpp RegisterCustomCheckersTest.cpp SymbolReaperTest.cpp + TestReturnValueUnderConstruction.cpp ) clang_target_link_libraries(StaticAnalysisTests Index: clang/unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp =================================================================== --- /dev/null +++ clang/unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp @@ -0,0 +1,55 @@ +//===- unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp ------===// +// +// 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 "CheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +class TestReturnValueUnderConstructionChecker + : public Checker { +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + if (!Call.getOriginExpr()) + return; + + Optional RetVal = Call.getReturnValueUnderConstruction(0); + assert(RetVal); + assert(RetVal->getAsRegion()); + } +}; + +void addTestReturnValueUnderConstructionChecker( + AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { + AnOpts.CheckersAndPackages = + {{"test.TestReturnValueUnderConstruction", true}}; + AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { + Registry.addChecker( + "test.TestReturnValueUnderConstruction", "", ""); + }); +} + +TEST(TestReturnValueUnderConstructionChecker, + ReturnValueUnderConstructionChecker) { + EXPECT_TRUE(runCheckerOnCode( + "class C { public: C(int nn): n(nn) {} virtual ~C() {} " + " private: int n; };" + "C returnC(int m) { C c(m); return c; }" + "void foo() { C c = returnC(1); }")); +} + +} // namespace +} // namespace ento +} // namespace clang