124 lines · cpp
1//===- unittests/StaticAnalyzer/TestReturnValueUnderConstruction.cpp ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "CheckerRegistration.h"10#include "clang/StaticAnalyzer/Core/Checker.h"11#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"12#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"13#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"14#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"15#include "clang/Tooling/Tooling.h"16#include "gtest/gtest.h"17#include <optional>18 19namespace clang {20namespace ento {21namespace {22 23class TestReturnValueUnderConstructionChecker24 : public Checker<check::PostCall> {25public:26 void checkPostCall(const CallEvent &Call, CheckerContext &C) const {27 // Only calls with origin expression are checked. These are `returnC()`,28 // `returnD()`, C::C() and D::D().29 if (!Call.getOriginExpr())30 return;31 32 // Since `returnC` returns an object by value, the invocation results33 // in an object of type `C` constructed into variable `c`. Thus the34 // return value of `CallEvent::getReturnValueUnderConstruction()` must35 // be non-empty and has to be a `MemRegion`.36 std::optional<SVal> RetVal = Call.getReturnValueUnderConstruction();37 ASSERT_TRUE(RetVal);38 ASSERT_TRUE(RetVal->getAsRegion());39 40 const auto *RetReg = cast<TypedValueRegion>(RetVal->getAsRegion());41 const Expr *OrigExpr = Call.getOriginExpr();42 ASSERT_EQ(OrigExpr->getType()->getCanonicalTypeInternal(),43 RetReg->getValueType()->getCanonicalTypeInternal());44 }45};46 47void addTestReturnValueUnderConstructionChecker(48 AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) {49 AnOpts.CheckersAndPackages =50 {{"test.TestReturnValueUnderConstruction", true}};51 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {52 Registry.addChecker<TestReturnValueUnderConstructionChecker>(53 "test.TestReturnValueUnderConstruction", "MockDescription");54 });55}56 57TEST(TestReturnValueUnderConstructionChecker,58 ReturnValueUnderConstructionChecker) {59 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(60 R"(class C {61 public:62 C(int nn): n(nn) {}63 virtual ~C() {}64 private:65 int n;66 };67 68 C returnC(int m) {69 C c(m);70 return c;71 }72 73 void foo() {74 C c = returnC(1);75 })"));76 77 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(78 R"(class C {79 public:80 C(int nn): n(nn) {}81 explicit C(): C(0) {}82 virtual ~C() {}83 private:84 int n;85 };86 87 C returnC() {88 C c;89 return c;90 }91 92 void foo() {93 C c = returnC();94 })"));95 96 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(97 R"(class C {98 public:99 C(int nn): n(nn) {}100 virtual ~C() {}101 private:102 int n;103 };104 105 class D: public C {106 public:107 D(int nn): C(nn) {}108 virtual ~D() {}109 };110 111 D returnD(int m) {112 D d(m);113 return d;114 }115 116 void foo() {117 D d = returnD(1); 118 })"));119}120 121} // namespace122} // namespace ento123} // namespace clang124