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: 25 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()->getCanonicalTypeInternal(), 42 RetReg->getValueType()->getCanonicalTypeInternal()); 43 } 44 }; 45 46 void addTestReturnValueUnderConstructionChecker( 47 AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) { 48 AnOpts.CheckersAndPackages = 49 {{"test.TestReturnValueUnderConstruction", true}}; 50 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) { 51 Registry.addChecker<TestReturnValueUnderConstructionChecker>( 52 "test.TestReturnValueUnderConstruction", "", ""); 53 }); 54 } 55 56 TEST(TestReturnValueUnderConstructionChecker, 57 ReturnValueUnderConstructionChecker) { 58 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>( 59 R"(class C { 60 public: 61 C(int nn): n(nn) {} 62 virtual ~C() {} 63 private: 64 int n; 65 }; 66 67 C returnC(int m) { 68 C c(m); 69 return c; 70 } 71 72 void foo() { 73 C c = returnC(1); 74 })")); 75 76 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>( 77 R"(class C { 78 public: 79 C(int nn): n(nn) {} 80 explicit C(): C(0) {} 81 virtual ~C() {} 82 private: 83 int n; 84 }; 85 86 C returnC() { 87 C c; 88 return c; 89 } 90 91 void foo() { 92 C c = returnC(); 93 })")); 94 95 EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>( 96 R"(class C { 97 public: 98 C(int nn): n(nn) {} 99 virtual ~C() {} 100 private: 101 int n; 102 }; 103 104 class D: public C { 105 public: 106 D(int nn): C(nn) {} 107 virtual ~D() {} 108 }; 109 110 D returnD(int m) { 111 D d(m); 112 return d; 113 } 114 115 void foo() { 116 D d = returnD(1); 117 })")); 118 } 119 120 } // namespace 121 } // namespace ento 122 } // namespace clang 123