blob: cf4a2a49e670cd5eb9fc37ce94c6dca67b50566c [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 inheritance.
#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, StructSimpleInheritance) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] B {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
struct S : public B {
};
int* target(S* s, int* a) {
s->a = a;
return s->a;
}
)"),
LifetimesAre({{"target", "(a, b), a -> a"}}));
}
TEST_F(LifetimeAnalysisTest,
DISABLED_StructInheritanceCallTrivialDefaultConstructor) {
EXPECT_THAT(GetLifetimes(R"(
struct T {};
struct S: public T {
S(): T() {}
int* a;
};
void target() {
S s;
}
)"),
LifetimesAre({{"target", ""}}));
}
TEST_F(LifetimeAnalysisTest, StructInheritanceCallBaseConstructor) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
int* b;
T(int* b): b(b) {}
};
struct S: public T {
S(int* a, int* b): a(a), T(b) {}
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
int* target(int* a, int* b) {
S s(a, b);
return s.b;
}
)"),
LifetimesContain({{"target", "a, a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructInheritanceCallBaseConstructorTypedef) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
int* b;
T(int* b): b(b) {}
};
using U = T;
struct S: public U {
S(int* a, int* b): a(a), T(b) {}
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
int* target(int* a, int* b) {
S s(a, b);
return s.b;
}
)"),
LifetimesContain({{"target", "a, a -> a"}}));
}
TEST_F(LifetimeAnalysisTest,
StructInheritanceCallBaseConstructorTypedefBaseInit) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] T {
[[clang::annotate("member_lifetimes", "a")]]
int* b;
T(int* b): b(b) {}
};
using U = T;
struct S: public T {
S(int* a, int* b): a(a), U(b) {}
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
int* target(int* a, int* b) {
S s(a, b);
return s.b;
}
)"),
LifetimesContain({{"target", "a, a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructSimpleInheritanceWithMethod) {
EXPECT_THAT(
GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] B {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return a; }
};
struct S : public B {
};
int* target(S* s, int* a) {
s->a = a;
return s->f();
}
)"),
LifetimesAre({{"B::f", "(a, b): -> a"}, {"target", "(a, b), a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructSimpleInheritanceWithMethodInDerived) {
EXPECT_THAT(
GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] B {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
struct S : public B {
int* f() { return a; }
};
int* target(S* s, int* a) {
s->a = a;
return s->f();
}
)"),
LifetimesAre({{"S::f", "(a, b): -> a"}, {"target", "(a, b), a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructSimpleInheritanceChained) {
EXPECT_THAT(
GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] A {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
};
struct B : public A {
int* f() { return a; }
};
struct S : public B {
};
int* target(S* s, int* a) {
s->a = a;
return s->f();
}
)"),
LifetimesAre({{"B::f", "(a, b): -> a"}, {"target", "(a, b), a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructSimpleInheritanceWithSwappedTemplateArgs) {
// Base and Derived have template arguments where the order is swapped, so
// if the code reuse the same vector representation for the lifetimes
// Derived (T, U) for the base class where Base has (U, T) this code fails.
EXPECT_THAT(GetLifetimes(R"(
template <typename U, typename T>
struct Base {
T base_t;
U base_u;
};
template <typename T, typename U>
struct Derived : public Base<U, T> {
T derived_t;
U derived_u;
};
int* target(Derived<int*, float*>* d, int* t1, int* t2) {
d->derived_t = t1;
d->base_t = t2;
return d->derived_t;
}
)"),
// The lifetime for Derived::derived_t should also be
// Base::base_t. See discussions at cl/411724984.
LifetimesAre({{"target", "(<a, b>, c), a, a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, StructSimpleInheritanceWithDoubledTemplateArgs) {
// Base and Derived have different number of template arguments.
// Similar test case as StructSimpleInheritanceWithSwappedTemplateArgs.
EXPECT_THAT(GetLifetimes(R"(
template <typename T, typename U>
struct Base {
T base_t;
U base_u;
};
template <typename T>
struct Derived : public Base<T, T> {
T derived_t;
};
int* target(Derived<int*>* d, int* t1, int* t2, int* t3) {
d->derived_t = t1;
d->base_t = t2;
d->base_u = t3;
return d->derived_t;
}
)"),
LifetimesAre({{"target", "(a, b), a, a, a -> a"}}));
}
TEST_F(LifetimeAnalysisTest,
StructSimpleInheritanceWithTemplateSubstitutedAndArgs) {
// Base is a template type and has different number of template arguments from
// Derived. Similar test case as
// StructSimpleInheritanceWithSwappedTemplateArgs.
EXPECT_THAT(GetLifetimes(R"(
template <typename T>
struct Base {
T base_t;
};
template <typename B, typename T>
struct Derived : public B {
T derived_t;
};
int* target(Derived<Base<int*>, int*>* d, int* t1, int* t2) {
d->derived_t = t1;
d->base_t = t2;
return d->derived_t;
}
)"),
LifetimesAre({{"target", "(<a, b>, c), b, a -> b"}}));
}
TEST_F(LifetimeAnalysisTest, PassDerivedByValue) {
EXPECT_THAT(GetLifetimes(R"(
struct [[clang::annotate("lifetime_params", "a")]] B {
[[clang::annotate("member_lifetimes", "a")]]
int* a;
int* f() { return a; }
};
struct S : public B {
};
int* target(S s) {
return s.f();
}
)"),
LifetimesAre({{"B::f", "(a, b): -> a"}, {"target", "a -> a"}}));
}
TEST_F(LifetimeAnalysisTest, PassDerivedByValue_BaseIsTemplate) {
EXPECT_THAT(
GetLifetimes(R"(
template <class T>
struct B {
T a;
T f() { return a; }
};
template <class T>
struct S : public B<T> {
};
int* target(S<int *> s) {
return s.f();
}
)"),
LifetimesAre({{"B<int *>::f", "(a, b): -> a"}, {"target", "a -> a"}}));
}
} // namespace
} // namespace lifetimes
} // namespace tidy
} // namespace clang