| // Part of the Crubit project, under the Apache License v2.0 with LLVM | |
| // Exceptions. See /LICENSE for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // Tests involving initializers. | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "lifetime_analysis/test/lifetime_analysis_test.h" | |
| namespace clang { | |
| namespace tidy { | |
| namespace lifetimes { | |
| namespace { | |
| TEST_F(LifetimeAnalysisTest, | |
| ReturnStructFieldFromMultipleInitializersConstructor) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T i) : i(i) {} | |
| T i; | |
| }; | |
| int* ConstructorSyntax(int* a, int* b, bool cond) { | |
| return (cond ? S<int*>{a} : S<int*>{b}).i; | |
| } | |
| int* CastSyntax(int* a, int* b, bool cond) { | |
| return (cond ? S<int*>(a) : S<int*>(b)).i; | |
| } | |
| )"), | |
| LifetimesAre({ | |
| {"S<int *>::S", "(a, b): a"}, | |
| {"ConstructorSyntax", "a, a, () -> a"}, | |
| {"CastSyntax", "a, a, () -> a"}, | |
| })); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ReturnStructFieldFromMultipleInitializersInitList) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| T i; | |
| }; | |
| int* target(int* a, int* b, bool cond) { | |
| return (cond ? S<int*>{a} : S<int*>{b}).i; | |
| } | |
| )"), | |
| LifetimesAre({{"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ReturnStructFromMultipleInitializersConstructSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T i) : i(i) {} | |
| T i; | |
| }; | |
| S<int*> target(int* a, int* b, bool cond) { | |
| return cond ? S<int*>{a} : S<int*>{b}; | |
| } | |
| )"), | |
| LifetimesAre( | |
| {{"S<int *>::S", "(a, b): a"}, {"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, ReturnStructFromMultipleInitializersCastSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T i) : i(i) {} | |
| T i; | |
| }; | |
| S<int*> target(int* a, int* b, bool cond) { | |
| return cond ? S<int*>(a) : S<int*>(b); | |
| } | |
| )"), | |
| LifetimesAre( | |
| {{"S<int *>::S", "(a, b): a"}, {"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ReturnStructFromMultipleInitializersInitListSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| T i; | |
| }; | |
| S<int*> target(int* a, int* b, bool cond) { | |
| return cond ? S<int*>{a} : S<int*>{b}; | |
| } | |
| )"), | |
| LifetimesAre({{"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, StructWithMultipleInitializersConstructorSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T i) : i(i) {} | |
| T i; | |
| }; | |
| int* target(int* a, int* b, bool cond) { | |
| S<int*> s = cond ? S{a} : S{b}; | |
| return s.i; | |
| } | |
| )"), | |
| LifetimesAre( | |
| {{"S<int *>::S", "(a, b): a"}, {"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, StructWithMultipleInitializersCastSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T i) : i(i) {} | |
| T i; | |
| }; | |
| int* target(int* a, int* b, bool cond) { | |
| S<int*> s = cond ? S(a) : S(b); | |
| return s.i; | |
| } | |
| )"), | |
| LifetimesAre( | |
| {{"S<int *>::S", "(a, b): a"}, {"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, StructWithMultipleInitializersInitListSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| T i; | |
| }; | |
| int* target(int* a, int* b, bool cond) { | |
| S<int*> s = cond ? S<int*>{a} : S<int*>{b}; | |
| return s.i; | |
| } | |
| )"), | |
| LifetimesAre({{"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ConstructorInitWithMultipleInitializersConstructorSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : r(cond() ? R{a} : R{b}) {} | |
| R<T> r; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"cond", ""}, | |
| {"S<int *>::S", "(a, b): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ConstructorInitWithMultipleInitializersCastSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : r(cond() ? R(a) : R(b)) {} | |
| R<T> r; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"cond", ""}, | |
| {"S<int *>::S", "(a, b): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ConstructorInitWithMultipleInitializersInitListSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : r(cond() ? R<T>{a} : R<T>{b}) {} | |
| R<T> r; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"S<int *>::S", "(a, b): a, a"}, | |
| {"cond", ""}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| MemberInitWithMultipleInitializersConstructorSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : a(a), b(b) {} | |
| T a; | |
| T b; | |
| R<T> r{true ? R{a} : R{b}}; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"S<int *>::S", "(a, b): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, MemberInitWithMultipleInitializersCastSyntax) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : a(a), b(b) {} | |
| T a; | |
| T b; | |
| R<T> r{true ? R(a) : R(b)}; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"S<int *>::S", "(a, b): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, ConstructorInitWithMultiplePointers) { | |
| EXPECT_THAT( | |
| GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T, typename U, typename V> | |
| struct S { | |
| S(T a, U b) : r(cond() ? a : b) {} | |
| R<V> r; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*, int*, int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"cond", ""}, | |
| {"S<int *, int *, int *>::S", "(<b, c, a>, d): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, | |
| ConstructorInitWithMultiplePointersAndStoresFields) { | |
| EXPECT_THAT( | |
| GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T, typename U, typename V> | |
| struct S { | |
| S(T a, U b) : a_(a), b_(b), r(cond() ? a : b) {} | |
| T a_; | |
| U b_; | |
| R<V> r; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*, int*, int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"cond", ""}, | |
| {"S<int *, int *, int *>::S", "(<a, a, a>, b): a, a"}, | |
| {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, MemberInitWithMultiplePointers) { | |
| EXPECT_THAT( | |
| GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| R(T i) : i(i) {} | |
| T i; | |
| }; | |
| bool cond(); | |
| template <typename T, typename U, typename V> | |
| struct S { | |
| S(T a, U b, bool cond) : a(a), b(b), cond(cond) {} | |
| T a; | |
| U b; | |
| bool cond; | |
| R<V> r{cond ? a : b}; | |
| }; | |
| int* target(int* a, int* b, bool cond) { | |
| S<int*, int*, int*> s(a, b, cond); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"R<int *>::R", "(a, b): a"}, | |
| {"S<int *, int *, int *>::S", "(<a, a, a>, b): a, a, ()"}, | |
| {"target", "a, a, () -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, MemberInitWithMultipleInitializersInitListSyntax) { | |
| EXPECT_THAT( | |
| GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| T i; | |
| }; | |
| template <typename T> | |
| struct S { | |
| S(T a, T b) : a(a), b(b) {} | |
| T a; | |
| T b; | |
| R<T> r{true ? R<T>{a} : R<T>{b}}; | |
| }; | |
| int* target(int* a, int* b) { | |
| S<int*> s(a, b); | |
| return s.r.i; | |
| } | |
| )"), | |
| LifetimesAre({{"S<int *>::S", "(a, b): a, a"}, {"target", "a, a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, DeclStructInitializerWithConversionOperator) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| T a; | |
| }; | |
| template <typename T> | |
| struct S { | |
| T a; | |
| operator R<T>() { return {a}; } | |
| }; | |
| int* target(int* a) { | |
| R<int*> r = S<int*>{a}; | |
| return r.a; | |
| } | |
| )"), | |
| LifetimesAre({{"S<int *>::operator R", "(a, b): -> a"}, | |
| {"target", "a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, DeclStructInitializerFromCall) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| T a; | |
| }; | |
| template <typename T> | |
| struct R<T> f(T a) { | |
| return R<T>{a}; | |
| } | |
| int* target(int* a) { | |
| R<int*> r = f<int*>(a); | |
| return r.a; | |
| } | |
| )"), | |
| LifetimesAre({{"f", "a -> a"}, {"target", "a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, ReturnStructInitializerWithConversionOperator) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct R { | |
| T a; | |
| }; | |
| template <typename T> | |
| struct S { | |
| T a; | |
| operator R<T>() { return {a}; } | |
| }; | |
| R<int*> target(int* a) { | |
| return S<int*>{a}; | |
| } | |
| )"), | |
| LifetimesAre({{"S<int *>::operator R", "(a, b): -> a"}, | |
| {"target", "a -> a"}})); | |
| } | |
| // TODO(danakj): Crashes due to operator() not being a CXXConstructExpr, but | |
| // SetExprObjectSetRespectingType only handles CXXConstructExpr for record | |
| // types. | |
| TEST_F(LifetimeAnalysisTest, | |
| DISABLED_ConstructorInitializerWithConversionOperator) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| T a; | |
| }; | |
| template <typename T> | |
| struct R { | |
| T a; | |
| operator S<T>() { return {a}; } | |
| }; | |
| // This initializes the `s` field from a constructor initializer. | |
| template <typename T> | |
| struct QConstructor { | |
| QQConstructor(T a) : s(R<T>{a}) {} | |
| S<T> s; | |
| }; | |
| int* constructor(int* a) { | |
| return QQConstructor<int*>{a}.s.a; | |
| } | |
| // This initializes the `s` field from a transparent InitListExpr on a | |
| // member initializer. | |
| template <typename T> | |
| struct QMember { | |
| QMember(T a) : a(a) {} | |
| T a; | |
| S<T> s{S<T>(R<T>{a})}; | |
| }; | |
| int* member(int* a) { | |
| return QMember<int*>{a}.s.a; | |
| } | |
| )"), | |
| LifetimesAre({{"QConstructor<int *>::QConstructor", "(a, b): a"}, | |
| {"QMember<int *>::QMember", "(a, b): a"}, | |
| {"constructor", "a -> a"}, | |
| {"member", "a -> a"}})); | |
| } | |
| TEST_F(LifetimeAnalysisTest, StructInitializerWithCtorCall) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| S(T a) : a(a) {} | |
| T a; | |
| }; | |
| int* TransparentInitListExpr(int* a) { | |
| S<int*> s{S<int*>(a)}; | |
| return s.a; | |
| } | |
| int* CastSyntax(int* a) { | |
| S<int*> s((S<int*>(a))); | |
| return s.a; | |
| } | |
| )"), | |
| LifetimesAre({{"S<int *>::S", "(a, b): a"}, | |
| {"TransparentInitListExpr", "a -> a"}, | |
| {"CastSyntax", "a -> a"}})); | |
| } | |
| // TODO(danakj): Crashes because the initializer expression is a | |
| // CXXStaticCastExpr, and operator() is not a CXXConstructExpr, but | |
| // SetExprObjectSetRespectingType only handles CXXConstructExpr for record | |
| // types. | |
| TEST_F(LifetimeAnalysisTest, DISABLED_StaticCastInitializer) { | |
| EXPECT_THAT(GetLifetimes(R"( | |
| template <typename T> | |
| struct S { | |
| T a; | |
| }; | |
| template <typename T> | |
| struct R { | |
| T a; | |
| operator S<T>() { return {a}; } | |
| }; | |
| int* target(int* a) { | |
| return static_cast<S<int*>>(R<int*>{a}).a; | |
| } | |
| )"), | |
| LifetimesAre({{"target", "a -> a"}})); | |
| } | |
| } // namespace | |
| } // namespace lifetimes | |
| } // namespace tidy | |
| } // namespace clang |