blob: 2c6290c123a59cfff0ef99a7f40f3b7f0519a821 [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 (non-template) records (structs, classes, unions).
#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, MembersWithSameAnnotationMergeLifetimes) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* i;
[[clang::annotate("member_lifetimes", "a")]]
int* j;
};
void target(S* s, int* p, int* q) {
s->i = p;
s->j = q;
}
)"),
LifetimesAre({{"target", "(a, b), a, a"}}));
}
TEST_F(LifetimeAnalysisTest, StructsWithTemplateFieldsDoesNotMergeLifetimes) {
EXPECT_THAT(GetLifetimes(R"(
template <typename A, typename B>
struct S { A i; B j; };
void target(S<int*, int*>* s, int* p, int* q) {
s->i = p;
s->j = q;
}
)"),
LifetimesAre({{"target", "(<a, b>, c), a, b"}}));
}
TEST_F(LifetimeAnalysisTest, StructWithArrayMergesLifetimes) {
EXPECT_THAT(GetLifetimes(R"(
template <typename A>
struct S { A array; };
void target(S<int**>* s, int* p, int* q) {
s->array[0] = p;
s->array[1] = q;
}
)"),
LifetimesAre({{"target", "(a, b, c), a, a"}}));
}
TEST_F(LifetimeAnalysisTest, DeclRecordWithConditionalOperator) {
EXPECT_THAT(GetLifetimes(R"(
template <typename P>
struct S { P p; };
int* target(int* a, int* b, bool cond) {
S<int*> s = cond ? S<int*>{a} : S<int*>{b};
return s.p;
}
)"),
LifetimesAre({{"target", "a, a, () -> a"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnRecordWithConditionalOperator) {
EXPECT_THAT(GetLifetimes(R"(
template <typename P>
struct S { P p; };
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, MaterializeRecordWithConditionalOperator) {
EXPECT_THAT(GetLifetimes(R"(
template <typename P>
struct S { P p; };
int* target(int* a, int* b, bool cond) {
return (cond ? S<int*>{a} : S<int*>{b}).p;
}
)"),
LifetimesAre({{"target", "a, a, () -> a"}}));
}
TEST_F(LifetimeAnalysisTest, ConstructorInitRecordWithConditionalOperator) {
EXPECT_THAT(GetLifetimes(R"(
template <typename P>
struct S { P p; };
template <typename P>
struct T {
T(int* a, int* b, bool cond) : s(cond ? S<int*>{a} : S<int*>{b}) {}
S<P> s;
};
int* target(int* a, int* b, bool cond) {
T<int*> t(a, b, cond);
return t.s.p;
}
)"),
LifetimesAre({{"T<int *>::T", "(a, b): a, a, ()"},
{"target", "a, a, () -> a"}}));
}
TEST_F(LifetimeAnalysisTest, MemberInitRecordWithConditionalOperator) {
EXPECT_THAT(
GetLifetimes(R"(
template <typename P>
struct S { P p; };
template <typename A, typename B, typename P>
struct T {
T(int* a, int* b, bool cond) : a(a), b(b), cond(cond) {}
A a;
B b;
bool cond;
S<P> s{cond ? S<int*>{a} : S<int*>{b}};
};
int* target(int* a, int* b, bool cond) {
T<int*, int*, int*> t(a, b, cond);
return t.s.p;
}
)"),
LifetimesAre({{"T<int *, int *, int *>::T", "(<a, a, a>, b): a, a, ()"},
{"target", "a, a, () -> a"}}));
}
TEST_F(LifetimeAnalysisTest, SimpleStruct) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
[[clang::annotate("member_lifetimes", "a")]]
int* b;
};
void target(S* s, int* a, int* b) {
s->a = a;
s->b = b;
}
)"),
LifetimesAre({{"target", "(a, b), a, a"}}));
}
TEST_F(LifetimeAnalysisTest, SimpleUnion) {
EXPECT_THAT(GetLifetimes(R"(
union [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
[[clang::annotate("member_lifetimes", "a")]]
int* b;
};
void target(S* s, int* a, int* b) {
s->a = a;
s->b = b;
}
)"),
LifetimesAre({{"target", "(a, b), a, a"}}));
}
TEST_F(LifetimeAnalysisTest, StructReference) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
[[clang::annotate("member_lifetimes", "a")]]
int* b;
};
void target(S& s, int* a, int* b) {
s.a = a;
s.b = b;
}
)"),
LifetimesAre({{"target", "(a, b), a, a"}}));
}
TEST_F(LifetimeAnalysisTest, StructValue) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
int* target(S s) {
return s.a;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMultiplePtrsSameLifetime) {
// TODO(veluca): here, we correctly deduce *once f gets called* that something
// fishy is going on, namely, that `s.a` could be pointing to a local
// variable. However, we should already know this from the initialization of
// `s`.
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a", "a")]]
int** a;
[[clang::annotate("member_lifetimes", "a")]]
int* b;
};
void f(S& s) {
*s.a = s.b;
}
void target(int* a, int b) {
S s{&a, &b};
f(s);
}
)"),
LifetimesContain({{"target",
"ERROR: function returns reference to a local "
"through parameter 'a'"}}));
}
TEST_F(LifetimeAnalysisTest, StructNonLocalPtr) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
int* target(S* s) {
return s->a;
}
)"),
LifetimesAre({{"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberStructInitializedWithInitializerList) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
struct [[clang::annotate("lifetime_params", "a")]] S {
S(int* a): t{a} { }
[[clang::annotate("member_lifetimes", "a")]]
T t;
};
int* target(int* a) {
return S{a}.t.a;
}
)"),
LifetimesAre({{"S::S", "(a, b): a"}, {"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberPtr) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
int a;
};
int* target(S* s) {
return &s->a;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructReferenceMember) {
// This is a regression test for a bug where we were not treating accesses to
// member variables of reference type correctly.
EXPECT_THAT(GetLifetimes(R"(
struct S1 {
int a;
};
struct [[clang::annotate("lifetime_params", "a")]] S2 {
[[clang::annotate("member_lifetimes", "a")]]
S1 &s1;
};
int& target(S2* s2) {
// Make sure we can find the field S1::a. This is to ensure that our
// member access for s2->s1 is in fact returning an object of type S1
// (not S1&).
s2->s1.a = 5;
return s2->s1.a;
}
)"),
LifetimesAre({{"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructStaticMemberFunction) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
static int* f(int* x) { return x; }
};
int* target(int* a) {
return S::f(a);
}
)"),
LifetimesAre({{"S::f", "a -> a"}, {"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFunction) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return a; }
};
)"),
LifetimesAre({{"S::f", "(a, b): -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFunctionExplicitThis) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return this->a; }
};
)"),
LifetimesAre({{"S::f", "(a, b): -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFunctionCall) {
EXPECT_THAT(
GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return a; }
};
int* target(S* s) {
return s->f();
}
)"),
LifetimesAre({{"S::f", "(a, b): -> a"}, {"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFunctionCallDot) {
EXPECT_THAT(
GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return a; }
};
int* target(S* s) {
return (*s).f();
}
)"),
LifetimesAre({{"S::f", "(a, b): -> a"}, {"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFunctionComplexCall) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
void set(int* x) { a = x; }
int* f() { return a; }
};
int* target(S* s, int* b) {
s->set(b);
return (*s).f();
}
)"),
LifetimesAre({{"S::set", "(a, b): a"},
{"S::f", "(a, b): -> a"},
{"target", "(a, b), a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructReturnAddressOfMemberFunction) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
static void f();
};
typedef void (*funtype)();
funtype target() {
S s;
return s.f;
}
)"),
LifetimesContain({{"target", "-> ()"}}));
}
TEST_F(LifetimeAnalysisTest, StructDefaultConstructor) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target() {
S s;
}
)"),
LifetimesAre({{"target", ""}}));
}
TEST_F(LifetimeAnalysisTest, StructDefaultConstructor_ExplicitCall) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target() {
S();
}
)"),
LifetimesAre({{"target", ""}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorStatic) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
S(int* a) { this->a = a; }
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(int* a) {
static S s{a};
}
)"),
LifetimesAre({{"S::S", "(a, b): a"}, {"target", "static"}}));
}
TEST_F(LifetimeAnalysisTest, StructCopyConstructorStatic) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(S* x) {
static S s = *x;
}
)"),
LifetimesAre({{"target", "(static, a)"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorOutputsFieldPointer) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
S(int** field_out) {
*field_out = &i;
}
int i;
};
int* target() {
int* i_out;
S s(&i_out);
return i_out;
}
)"),
LifetimesAre({{"S::S", "a: (a, b)"},
{"target",
"ERROR: function returns reference to a local"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorOutputsThisPointer) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
S(S** this_out) {
*this_out = this;
}
};
S* target() {
S* s_out;
S s(&s_out);
return s_out;
}
)"),
LifetimesAre({{"S::S", "a: (a, b)"},
{"target",
"ERROR: function returns reference to a local"}}));
}
TEST_F(LifetimeAnalysisTest,
StructConstructorOutputsFieldPointerConstructorInitializer) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
S(int** field_out) {
*field_out = &i;
}
int i;
};
struct T {
T(int** int_out): s(int_out) {}
S s;
};
)"),
LifetimesAre({{"S::S", "a: (a, b)"}, {"T::T", "a: (a, b)"}}));
}
TEST_F(LifetimeAnalysisTest,
StructConstructorOutputsThisPointerConstructorInitializer) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
S(S** this_out) {
*this_out = this;
}
};
struct T {
T(S** this_out): s(this_out) {}
S s;
};
)"),
LifetimesAre({{"S::S", "a: (a, b)"}, {"T::T", "a: (a, b)"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorOutputsThisPointerInitMember) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
S(S** this_out) {
*this_out = this;
}
};
static S* static_s_ptr;
struct T {
T() {}
S s{&static_s_ptr};
};
)"),
LifetimesAre({{"S::S", "a: (a, b)"}, {"T::T", "static:"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorInitializers) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
S(int* a): a(a) { }
[[clang::annotate("member_lifetimes", "a")]]
int* a = nullptr;
// The following members don't affect lifetimes, but we keep them
// around to make sure that the related code is exercised.
int b = 0;
// This member points into the struct itself, forcing the lifetime
// parameter in the constructor to be the same as the lifetime of the
// object itself.
[[clang::annotate("member_lifetimes", "a")]]
int* c = &b;
int d;
};
)"),
LifetimesAre({{"S::S", "(a, a): a"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorStaticPtr) {
EXPECT_THAT(GetLifetimes(R"(
static int x;
struct [[clang::annotate("lifetime_params", "a")]] S {
S() { a = &x; }
[[clang::annotate("member_lifetimes", "a")]]
int* a = nullptr;
};
)"),
LifetimesAre({{"S::S", "(a, b):"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorStaticPtrInitializer) {
EXPECT_THAT(GetLifetimes(R"(
static int x;
struct [[clang::annotate("lifetime_params", "a")]] S {
S(): a(&x) { }
[[clang::annotate("member_lifetimes", "a")]]
int* a = nullptr;
};
)"),
LifetimesAre({{"S::S", "(a, b):"}}));
}
TEST_F(LifetimeAnalysisTest, StructConstructorStaticPtrMemberInitializer) {
EXPECT_THAT(GetLifetimes(R"(
static int x;
struct [[clang::annotate("lifetime_params", "a")]] S {
S() { }
[[clang::annotate("member_lifetimes", "a")]]
int* a = &x;
};
)"),
LifetimesAre({{"S::S", "(a, b):"}}));
}
TEST_F(LifetimeAnalysisTest, StructMemberFreeFunction) {
// Check that calling a method behaves in the same way as a free function.
EXPECT_THAT(GetLifetimes(R"(
static int x;
struct [[clang::annotate("lifetime_params", "a")]] S {
void f() { a = &x; }
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void f(S& a) {
a.a = &x;
}
)"),
LifetimesAre({{"S::f", "(a, b):"}, {"f", "(a, b)"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnFieldFromTemporaryStructConstructor) {
// S(i) with a single argument produces a clang::CXXFunctionalCastExpr around
// a clang::CXXConstructExpr.
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
S(int* i) : f(i) {}
[[clang::annotate("member_lifetimes", "a")]]
int* f;
};
int* ConstructorSyntax(int* i) {
return S{i}.f;
}
int* CastSyntax(int* i) {
return S(i).f;
}
)"),
LifetimesAre({{"S::S", "(a, b): a"},
{"ConstructorSyntax", "a -> a"},
{"CastSyntax", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest,
ReturnFieldFromTemporaryStructConstructorInitList) {
// S has no constructors so S{i} produces a clang::InitListExpr.
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* f;
};
int* target(int* i) {
return S{i}.f;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnFieldFromTemporaryUnion) {
// S has no constructors so S{i} produces a clang::InitListExpr.
EXPECT_THAT(GetLifetimes(R"(
union [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* f;
};
int* target(int* i) {
return S{i}.f;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructInitList) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(int* a) {
S s{a};
}
)"),
LifetimesAre({{"target", "a"}}));
}
TEST_F(LifetimeAnalysisTest, UnionInitList) {
EXPECT_THAT(GetLifetimes(R"(
union [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(int* a) {
S s{a};
}
)"),
LifetimesAre({{"target", "a"}}));
}
TEST_F(LifetimeAnalysisTest, StructCopy) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(S* a, S* b) {
*a = *b;
}
)"),
LifetimesAre({{"target", "(a, b), (a, c)"}}));
}
TEST_F(LifetimeAnalysisTest, StructCopyStatic) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(S* x) {
static S s;
s = *x;
}
)"),
LifetimesAre({{"target", "(static, a)"}}));
}
// We fail to initialize the temporary object in a CXXOperatorCallExpr argument,
// which causes us to assert when we visit the MaterializeTemporaryExpr later.
TEST_F(LifetimeAnalysisTest, DISABLED_CallExprWithRecordInitializedArguments) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* i;
};
S callee(const S& s1, const S& s2) {
return S{s2.i};
}
S target(int* a, int* b) {
return callee(S{a}, S{b});
}
)"),
LifetimesAre({{"target", "a, b -> b"}}));
}
TEST_F(LifetimeAnalysisTest, StructAssignMemberStatic) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
void target(int* x) {
static S s;
s.a = x;
}
)"),
LifetimesAre({{"target", "static"}}));
}
TEST_F(LifetimeAnalysisTest, StructCopyExplicit) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
S& operator=(const S& other) {
a = other.a;
return *this;
}
};
void target(S* a, S* b) {
*a = *b;
}
)"),
LifetimesAre({{"S::operator=", "(a, c): (a, b) -> (a, c)"},
{"target", "(a, b), (a, c)"}}));
}
TEST_F(LifetimeAnalysisTest, StructCopyExplicitNoop) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
S& operator=(const S& other) {
return *this;
}
};
void target(S* a, S* b) {
*a = *b;
}
)"),
LifetimesAre({{"S::operator=", "(c, d): (a, b) -> (c, d)"},
{"target", "(a, b), (c, d)"}}));
}
TEST_F(LifetimeAnalysisTest, StructWithStruct) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
T t;
};
int* target(S* s) {
return s->t.a;
}
)"),
LifetimesAre({{"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, NonReferenceLikeStruct) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
int a;
};
void target(S* a, S* b) {
a->a = b->a;
}
)"),
LifetimesAre({{"target", "a, b"}}));
}
TEST_F(LifetimeAnalysisTest, StructNonReferenceLikeField) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
int a;
[[clang::annotate("member_lifetimes", "a")]]
int* b;
};
void target(S* a, S* b) {
a->a = b->a;
}
)"),
LifetimesAre({{"target", "(a, b), (c, d)"}}));
}
TEST_F(LifetimeAnalysisTest, StructAssignToReference) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
int a;
[[clang::annotate("member_lifetimes", "a")]]
int& b;
};
void target(S* a, S* b) {
a->a = b->a;
a->b = b->b;
}
)"),
LifetimesAre({{"target", "(a, b), (c, d)"}}));
}
TEST_F(LifetimeAnalysisTest, NonReferenceLikeStructCopyAssignment) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
int a;
};
void target(S* a, S* b) {
*a = *b;
}
)"),
LifetimesAre({{"target", "a, b"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnReferenceLikeStruct) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* p;
};
S target() {
int i = 42;
S s = { &i };
return s;
}
)"),
LifetimesAre({{"target",
"ERROR: function returns reference to a local"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnNonReferenceLikeStruct) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
int a;
};
S target() {
int i = 42;
S s = { i };
return s;
}
)"),
LifetimesAre({{"target", ""}}));
}
TEST_F(LifetimeAnalysisTest, ReturnNonReferenceLikeStructCopy) {
EXPECT_THAT(GetLifetimes(R"(
struct S {
int a;
};
S target(S& s) {
return s;
}
)"),
LifetimesAre({{"target", "a"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnNonReferenceLikeStructFromTemporary) {
// This is a repro for a crash observed on b/228325046.
EXPECT_THAT(GetLifetimes(R"(
struct S {};
S target() {
return S();
}
)"),
LifetimesAre({{"target", ""}}));
}
TEST_F(LifetimeAnalysisTest, StructInnerDoublePtrInitList) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a", "a")]]
int** x;
};
void f(int** b) {
S s{b};
int i = 0;
*s.x = &i;
}
)"),
LifetimesAre({{"f",
"ERROR: function returns reference to a local "
"through parameter 'b'"}}));
}
TEST_F(LifetimeAnalysisTest, StructInnerDoublePtr) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a", "a")]]
int** x;
};
void g(S* s, int* a) {
*s->x = a;
}
void f(int* a, int** b) {
S s{b};
g(&s, a);
}
)"),
LifetimesAre({{"f", "a, (a, a)"}, {"g", "(a, b), a"}}));
}
TEST_F(LifetimeAnalysisTest, StructInnerDoublePtrAssign) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a", "a")]]
int** x;
};
void g(S* s, int* a) {
*s->x = a;
}
int* f(int* a, int** b) {
S s{b};
g(&s, a);
return *b;
}
)"),
LifetimesAre({{"f", "a, (a, a) -> a"}, {"g", "(a, b), a"}}));
}
TEST_F(LifetimeAnalysisTest, StructInnerDoublePtrParam) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a", "a")]]
int** x;
};
void g(S* s, int* a) {
*s->x = a;
}
void f(S& s, int* a, int** b) {
s.x = b;
g(&s, a);
}
)"),
LifetimesAre({{"f", "(a, b), a, (a, a)"}, {"g", "(a, b), a"}}));
}
TEST_F(LifetimeAnalysisTest, List) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] List {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
[[clang::annotate("member_lifetimes", "a", "a")]]
List* next;
void Append(List& oth) {
next = &oth;
}
int* Get() const {
return a;
}
};
int* target(List* l, int* a) {
if (l->next) {
l->next->a = a;
}
return l->Get();
}
)"),
LifetimesAre({{"List::Append", "(a, b): (a, a)"},
{"List::Get", "(a, b): -> a"},
{"target", "(a, b), a -> a"}}));
}
// TODO(danakj): Crashes trying to find the initializer expression under
// MaterializeTemporaryExpr. Should be improved by cl/414032764.
TEST_F(LifetimeAnalysisTest, DISABLED_VarDeclReferenceToRecordTemporary) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
int* target(int* a) {
const S<int*>& s = S<int*>{a};
return s.a;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, VarDeclReferenceToRecordTemplate) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*>* target(S<int*>* a) {
S<int*>& b = *a;
return &b;
}
)"),
LifetimesAre({{"target", "(a, b) -> (a, b)"}}));
}
TEST_F(LifetimeAnalysisTest, VarDeclReferenceToRecordNoTemplate) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
S* target(S* a) {
S& b = *a;
return &b;
}
)"),
LifetimesAre({{"target", "(a, b) -> (a, b)"}}));
}
TEST_F(LifetimeAnalysisTest, ConstructorInitReferenceToRecord) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] S {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
template <class Ref>
struct R {
R(S& s): s(s) {}
Ref s;
};
int* target(S* a) {
R<S&> r(*a);
return r.s.a;
}
)"),
LifetimesAre({{"R<S &>::R", "(a, b, c): (a, b)"},
{"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, MemberInitReferenceToRecord) {
EXPECT_THAT(
GetLifetimes(R"(
template <typename P>
struct S {
P p;
};
template<typename P>
struct [[clang::annotate("lifetime_params", "a")]] R {
R(P p): ss{p} {}
S<P> ss;
[[clang::annotate("member_lifetimes", "a")]]
S<P>& s{ss};
};
int* target(int* a) {
R<int*> r(a);
return r.s.p;
}
)"),
LifetimesAre({{"R<int *>::R", "(<a> [b], b): a"}, {"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructTemplateReturn) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*> target(S<int*>& s) {
return s;
}
)"),
LifetimesAre({{"target", "(a, b) -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructTemplateReturnXvalue) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*> target(S<int*> s) {
return s;
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructTemplateReturnCall) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*> take_by_ref(S<int*>& s) {
return s;
}
S<int*> take_by_value(S<int*> s) {
return s;
}
)"),
LifetimesAre({{"take_by_ref", "(a, b) -> a"},
{"take_by_value", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructTemplateReturnLocal) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*> target(int* a) {
int i = 42;
return { &i };
}
)"),
LifetimesAre({{"target",
"ERROR: function returns reference to a local"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnStructTemporaryConstructor) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
S(T a) : a(a) {}
T a;
};
S<int*> ConstructorCastSyntax(int* a) {
return S(a);
}
S<int*> ConstructTemporarySyntax(int* a) {
return S{a};
}
)"),
LifetimesAre({{"S<int *>::S", "(a, b): a"},
{"ConstructorCastSyntax", "a -> a"},
{"ConstructTemporarySyntax", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnStructTemporaryInitializerList) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct S {
T a;
};
S<int*> InitListExpr(int* a) {
return {a};
}
S<int*> CastWithInitListExpr(int* a) {
return S<int*>{a};
}
)"),
LifetimesAre({{"InitListExpr", "a -> a"},
{"CastWithInitListExpr", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, ReturnUnionTemporaryInitializerList) {
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
union S {
T a;
};
S<int*> target(int* a) {
return {a};
}
)"),
LifetimesAre({{"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructWithUnionMember) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
union [[clang::annotate("lifetime_params", "a")]] U {
[[clang::annotate("member_lifetimes", "a")]]
int* i;
} u;
};
int* target(T* t) {
return t->u.i;
}
)"),
LifetimesAre({{"target", "(a, b) -> a"}}));
}
} // namespace
} // namespace lifetimes
} // namespace tidy
} // namespace clang