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 | #include <optional> |
18 | |
19 | namespace clang { |
20 | namespace ento { |
21 | namespace { |
22 | |
23 | class TestReturnValueUnderConstructionChecker |
24 | : public Checker<check::PostCall> { |
25 | public: |
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 results |
33 | // in an object of type `C` constructed into variable `c`. Thus the |
34 | // return value of `CallEvent::getReturnValueUnderConstruction()` must |
35 | // 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>(Val: RetVal->getAsRegion()); |
41 | const Expr *OrigExpr = Call.getOriginExpr(); |
42 | ASSERT_EQ(OrigExpr->getType()->getCanonicalTypeInternal(), |
43 | RetReg->getValueType()->getCanonicalTypeInternal()); |
44 | } |
45 | }; |
46 | |
47 | void addTestReturnValueUnderConstructionChecker( |
48 | AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { |
49 | AnOpts.CheckersAndPackages = |
50 | {{"test.TestReturnValueUnderConstruction" , true}}; |
51 | AnalysisConsumer.AddCheckerRegistrationFn(Fn: [](CheckerRegistry &Registry) { |
52 | Registry.addChecker<TestReturnValueUnderConstructionChecker>( |
53 | FullName: "test.TestReturnValueUnderConstruction" , Desc: "" , DocsUri: "" ); |
54 | }); |
55 | } |
56 | |
57 | TEST(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 | } // namespace |
122 | } // namespace ento |
123 | } // namespace clang |
124 | |