blob: ac8496bf569d044662e87bd819995b01ccdbb50c [file] [log] [blame]
// 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