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 @@ -434,6 +434,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,16 @@ const CallEvent &Call, const EvalCallOptions &CallOpts = {}); + /// Update the program state with all the path-sensitive information + /// that's necessary to perform construction of an object with a given + /// syntactic construction context. If the construction context is unavailable + /// or unusable for any reason, a dummy temporary region is returned, and the + /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. + /// Returns the updated program state and the new object's this-region. + std::pair handleConstructionContext( + const Expr *E, ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC, EvalCallOptions &CallOpts); + private: ProgramStateRef finishArgumentConstruction(ProgramStateRef State, const CallEvent &Call); @@ -804,16 +814,6 @@ /// constructing into an existing region. const CXXConstructExpr *findDirectConstructorForCurrentCFGElement(); - /// Update the program state with all the path-sensitive information - /// that's necessary to perform construction of an object with a given - /// syntactic construction context. If the construction context is unavailable - /// or unusable for any reason, a dummy temporary region is returned, and the - /// IsConstructorWithImproperlyModeledTargetRegion flag is set in \p CallOpts. - /// Returns the updated program state and the new object's this-region. - std::pair handleConstructionContext( - const Expr *E, ProgramStateRef State, const LocationContext *LCtx, - const ConstructionContext *CC, EvalCallOptions &CallOpts); - /// Common code that handles either a CXXConstructExpr or a /// CXXInheritedCtorInitExpr. void handleConstructor(const Expr *E, ExplodedNode *Pred, Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -535,6 +535,50 @@ // 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; + + ProgramStateRef State; + SVal RetVal; + ExprEngine::EvalCallOptions CallOpts; + SubEngine &Engine = getState()->getStateManager().getOwningEngine(); + std::tie(State, RetVal) = + static_cast(&Engine)->handleConstructionContext(getOriginExpr(), + getState(), + getLocationContext(), + CC, CallOpts); + 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 @@ -124,6 +124,10 @@ case ConstructionContext::SimpleVariableKind: { const auto *DSCC = cast(CC); const auto *DS = DSCC->getDeclStmt(); + Optional ExistingVal = getObjectUnderConstruction(State, DS, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + const auto *Var = cast(DS->getSingleDecl()); SVal LValue = State->getLValue(Var, LCtx); QualType Ty = Var->getType(); @@ -137,6 +141,11 @@ case ConstructionContext::SimpleConstructorInitializerKind: { const auto *ICC = cast(CC); const auto *Init = ICC->getCXXCtorInitializer(); + Optional ExistingVal = + getObjectUnderConstruction(State, Init, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + assert(Init->isAnyMemberInitializer()); const CXXMethodDecl *CurCtor = cast(LCtx->getDecl()); Loc ThisPtr = @@ -235,6 +244,9 @@ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); const CXXConstructExpr *CE = TCC->getConstructorAfterElision(); + Optional ExistingVal = getObjectUnderConstruction(State, CE, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); // Support pre-C++17 copy elision. We'll have the elidable copy // constructor in the AST and in the CFG, but we'll skip it @@ -281,7 +293,19 @@ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); SVal V = UnknownVal(); + if (BTE) { + Optional ExistingVal = + getObjectUnderConstruction(State, BTE, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + } + if (MTE) { + Optional ExistingVal = + getObjectUnderConstruction(State, MTE, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + if (const ValueDecl *VD = MTE->getExtendingDecl()) { assert(MTE->getStorageDuration() != SD_FullExpression); if (!VD->getType()->isReferenceType()) { @@ -318,7 +342,30 @@ const auto *ACC = cast(CC); const Expr *E = ACC->getCallLikeExpr(); unsigned Idx = ACC->getIndex(); + if (const auto *CE = dyn_cast(E)) { + Optional ExistingVal = + getObjectUnderConstruction(State, {CE, Idx}, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + } else if (const auto *CCE = dyn_cast(E)) { + Optional ExistingVal = + getObjectUnderConstruction(State, {CCE, Idx}, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + } else if (const auto *ME = dyn_cast(E)) { + Optional ExistingVal = + getObjectUnderConstruction(State, {ME, Idx}, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + } + const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr(); + if (BTE) { + Optional ExistingVal = + getObjectUnderConstruction(State, BTE, LCtx); + if (ExistingVal.hasValue()) + return std::make_pair(State, *ExistingVal); + } CallEventManager &CEMgr = getStateManager().getCallEventManager(); SVal V = UnknownVal(); Index: clang/unittests/StaticAnalyzer/CMakeLists.txt =================================================================== --- clang/unittests/StaticAnalyzer/CMakeLists.txt +++ clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -10,6 +10,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