Open-source lifetime inference/verification code.
PiperOrigin-RevId: 450954978
diff --git a/lifetime_analysis/test/inheritance.cc b/lifetime_analysis/test/inheritance.cc
new file mode 100644
index 0000000..cf4a2a4
--- /dev/null
+++ b/lifetime_analysis/test/inheritance.cc
@@ -0,0 +1,281 @@
+// 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