baloghadamsoftware created this revision.
baloghadamsoftware added reviewers: NoQ, Szelethus.
baloghadamsoftware added a project: clang.
Herald added subscribers: ASDenysPetrov, martong, steakhal, Charusso, 
gamesh411, dkrupp, donat.nagy, mikhail.ramalho, a.sidorin, rnkovacs, szepet, 
xazax.hun, whisperity, mgorny.

Checkers should be able to get the return value under construction for a 
`CallEvenet`. This patch adds a function to achieve this which retrieves the 
return value from the construction context of the call.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80366

Files:
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
  clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
  clang/lib/StaticAnalyzer/Core/CallEvent.cpp
  clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
  clang/unittests/StaticAnalyzer/CMakeLists.txt
  clang/unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp

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<check::PostCall> {
+public:
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+    if (!Call.getOriginExpr())
+      return;
+
+    Optional<SVal> 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<TestReturnValueUnderConstructionChecker>(
+          "test.TestReturnValueUnderConstruction", "", "");
+    });
+}
+
+TEST(TestReturnValueUnderConstructionChecker,
+     ReturnValueUnderConstructionChecker) {
+  EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(
+                  "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
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/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -109,6 +109,96 @@
   return LValue;
 }
 
+Optional<SVal> 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<VariableConstructionContext>(CC);
+      const auto *DS = DSCC->getDeclStmt();
+      return getObjectUnderConstruction(State, DS, LCtx);
+    }
+    case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+    case ConstructionContext::SimpleConstructorInitializerKind: {
+      const auto *ICC = cast<ConstructorInitializerConstructionContext>(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<CFGCXXRecordTypedCall>();
+        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<BlockInvocationContext>(CallerLCtx)) {
+          // Unwrap block invocation contexts. They're mostly part of
+          // the current stack frame.
+          CallerLCtx = CallerLCtx->getParent();
+          assert(!isa<BlockInvocationContext>(CallerLCtx));
+        }
+        return retrieveFromConstructionContext(
+          State, CallerLCtx, RTC->getConstructionContext());
+      }
+      break;
+    }
+    case ConstructionContext::ElidedTemporaryObjectKind: {
+      assert(State->getAnalysisManager().getAnalyzerOptions()
+             .ShouldElideConstructors);
+      const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+      Optional<SVal> RetVal = retrieveFromConstructionContext(
+          State, LCtx, TCC->getConstructionContextAfterElision());
+      if (RetVal.hasValue())
+        return RetVal;
+      LLVM_FALLTHROUGH;
+    }
+    case ConstructionContext::SimpleTemporaryObjectKind: {
+      const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+      const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+      Optional<SVal> 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<ArgumentConstructionContext>(CC);
+      const Expr *E = ACC->getCallLikeExpr();
+      unsigned Idx = ACC->getIndex();
+      if (const auto *CE = dyn_cast<CallExpr>(E)) {
+        return getObjectUnderConstruction(State, {CE, Idx}, LCtx);
+      } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
+        return getObjectUnderConstruction(State, {CCE, Idx}, LCtx);
+      } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(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<ProgramStateRef, SVal> ExprEngine::handleConstructionContext(
     const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
     const ConstructionContext *CC, EvalCallOptions &CallOpts) {
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<CFGConstructor>()) {
+    return Ctor->getConstructionContext();
+  }
+
+  if (const auto RecCall = (*Block)[Index].getAs<CFGCXXRecordTypedCall>()) {
+    return RecCall->getConstructionContext();
+  }
+
+  return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction(unsigned BlockCount) const {
+  const auto *CC = getConstructionContext(*this, BlockCount);
+  if (!CC)
+    return None;
+
+  Optional<SVal> RetVal =
+    ExprEngine::retrieveFromConstructionContext(getState(),
+                                                getLocationContext(), CC);
+  return RetVal;
+}
+
 ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
   const FunctionDecl *D = getDecl();
   if (!D)
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<SVal> retrieveFromConstructionContext(
+      ProgramStateRef State, const LocationContext *LCtx,
+      const ConstructionContext *CC);
+
 private:
   ProgramStateRef finishArgumentConstruction(ProgramStateRef State,
                                              const CallEvent &Call);
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<SVal> getReturnValueUnderConstruction(unsigned BlockCount) const;
+
   // Iterator access to formal parameters and their types.
 private:
   struct GetTypeFn {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to