blob: 5998a6ab600f2d0022f1cb41af1cd52ede6ddde7 [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 for initialization.
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "lifetime_analysis/test/lifetime_analysis_test.h"
namespace clang {
namespace tidy {
namespace lifetimes {
namespace {
// 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"}}));
}
// TODO(danakj): Fails because a nested TransferMemberExpr() ends up looking for
// the field from the outer expr on the object of the inner expr.
//
// The code:
// ObjectSet struct_points_to =
// points_to_map.GetExprObjectSet(member->getBase());
//
// The AST:
// MemberExpr 0x4027d3f2628 'int *':'int *' lvalue .p 0x4027d3f7338
// `-MemberExpr 0x4027d3f25f8 'S<int *>':'struct S<int *>' lvalue .s
// 0x4027d3f74c0
// `-DeclRefExpr 0x4027d3f25d8 'R<int *>':'struct R<int *>' lvalue Var
// 0x4027d3f6cd0 'r' 'R<int *>':'struct R<int *>'
//
// The p field is on struct S, but the code tries to find it on an object
// of type R<int *>.
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"}}));
}
} // namespace
} // namespace lifetimes
} // namespace tidy
} // namespace clang