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