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-exception
6 //
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
18 namespace clang {
19 namespace ento {
20 namespace {
21
22 class TestReturnValueUnderConstructionChecker
23 : public Checker<check::PostCall> {
24 public:
checkPostCall(const CallEvent & Call,CheckerContext & C) const25 void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
26 // Only calls with origin expression are checked. These are `returnC()`,
27 // `returnD()`, C::C() and D::D().
28 if (!Call.getOriginExpr())
29 return;
30
31 // Since `returnC` returns an object by value, the invocation results
32 // in an object of type `C` constructed into variable `c`. Thus the
33 // return value of `CallEvent::getReturnValueUnderConstruction()` must
34 // be non-empty and has to be a `MemRegion`.
35 Optional<SVal> RetVal = Call.getReturnValueUnderConstruction();
36 ASSERT_TRUE(RetVal);
37 ASSERT_TRUE(RetVal->getAsRegion());
38
39 const auto *RetReg = cast<TypedValueRegion>(RetVal->getAsRegion());
40 const Expr *OrigExpr = Call.getOriginExpr();
41 ASSERT_EQ(OrigExpr->getType(), RetReg->getValueType());
42 }
43 };
44
addTestReturnValueUnderConstructionChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)45 void addTestReturnValueUnderConstructionChecker(
46 AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) {
47 AnOpts.CheckersAndPackages =
48 {{"test.TestReturnValueUnderConstruction", true}};
49 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
50 Registry.addChecker<TestReturnValueUnderConstructionChecker>(
51 "test.TestReturnValueUnderConstruction", "", "");
52 });
53 }
54
TEST(TestReturnValueUnderConstructionChecker,ReturnValueUnderConstructionChecker)55 TEST(TestReturnValueUnderConstructionChecker,
56 ReturnValueUnderConstructionChecker) {
57 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(
58 R"(class C {
59 public:
60 C(int nn): n(nn) {}
61 virtual ~C() {}
62 private:
63 int n;
64 };
65
66 C returnC(int m) {
67 C c(m);
68 return c;
69 }
70
71 void foo() {
72 C c = returnC(1);
73 })"));
74
75 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(
76 R"(class C {
77 public:
78 C(int nn): n(nn) {}
79 explicit C(): C(0) {}
80 virtual ~C() {}
81 private:
82 int n;
83 };
84
85 C returnC() {
86 C c;
87 return c;
88 }
89
90 void foo() {
91 C c = returnC();
92 })"));
93
94 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(
95 R"(class C {
96 public:
97 C(int nn): n(nn) {}
98 virtual ~C() {}
99 private:
100 int n;
101 };
102
103 class D: public C {
104 public:
105 D(int nn): C(nn) {}
106 virtual ~D() {}
107 };
108
109 D returnD(int m) {
110 D d(m);
111 return d;
112 }
113
114 void foo() {
115 D d = returnD(1);
116 })"));
117 }
118
119 } // namespace
120 } // namespace ento
121 } // namespace clang
122