| // 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 lifetime propagation between virtual functions. |
| |
| #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, WithPureVirtual) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a) = 0; |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a) override { return a; } |
| }; |
| )"), |
| LifetimesContain( |
| {{"Base::f", "b: a -> a"}, {"Derived::f", "b: a -> a"}})); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithStatic) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a) = 0; |
| }; |
| |
| struct Derived1 : public Base { |
| int* f(int* a) override { return a; } |
| }; |
| |
| struct Derived2 : public Base { |
| int* f(int* a) override { |
| static int i = 42; |
| return &i; |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a -> a"}, |
| {"Derived1::f", "b: a -> a"}, |
| {"Derived2::f", "b: a -> c"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithTwoDeriveds) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a, int* b) = 0; |
| }; |
| |
| struct Derived1 : public Base { |
| int* f(int* a, int* b) override { return a; } |
| }; |
| |
| struct Derived2 : public Base { |
| int* f(int* a, int* b) override { return b; } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a, a -> a"}, |
| {"Derived1::f", "c: a, b -> a"}, |
| {"Derived2::f", "c: a, b -> b"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithBaseReturnStatic) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a) { return a; } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a) override { |
| static int i = 42; |
| return &i; |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a -> a"}, |
| {"Derived::f", "b: a -> c"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceChained) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a) = 0; |
| }; |
| |
| struct Derived1 : public Base { |
| int* f(int* a) override { |
| static int i = 42; |
| return &i; |
| } |
| }; |
| |
| struct Derived2 : public Derived1 { |
| int* f(int* a) override { return a; } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a -> a"}, |
| {"Derived1::f", "b: a -> a"}, |
| {"Derived2::f", "b: a -> a"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithControlFlow) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a, int* b) { return a; } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a, int* b) override { |
| if (*a < *b) |
| return a; |
| return b; |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a, a -> a"}, |
| {"Derived::f", "b: a, a -> a"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithLocal) { |
| EXPECT_THAT( |
| GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a) { return a; } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a) override { |
| int i = 42; |
| return &i; |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a -> a"}, |
| {"Derived::f", "ERROR: function returns reference to a local"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithStaticPtr) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual void f(int** a) {} |
| }; |
| |
| struct Derived : public Base { |
| void f(int** a) override { |
| static int i = 42; |
| *a = &i; |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "c: (a, b)"}, |
| {"Derived::f", "c: (a, b)"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithRecursion) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a, int* b) { return a; } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a, int* b) override { |
| if (*a > *b) |
| return b; |
| *a -= 1; |
| return f(a, b); |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a, a -> a"}, |
| {"Derived::f", "c: a, b -> b"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, FunctionVirtualInheritanceWithExplicitBaseCall) { |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a, int* b) { return a; } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a, int* b) override { |
| if (*a > *b) |
| return b; |
| return Base::f(a, b); |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a, a -> a"}, |
| {"Derived::f", "b: a, a -> a"}, |
| })); |
| } |
| |
| TEST_F(LifetimeAnalysisTest, |
| DISABLED_FunctionVirtualInheritanceWithComplexRecursion) { |
| // TODO(kinuko): Fix this. Currently this doesn't work because in |
| // AnalyzeFunctionRecursive() the recursion cycle check |
| // (FindAndMarkCycleWithFunc) happens before the code expands the possible |
| // overrides, and let it return early when it finds f() in Base::f() even if |
| // it has overrides. Later in AnalyzeRecursiveFunctions Base::f() is analyzed |
| // but it doesn't expand the overrides there. See the TODO in |
| // AnalyzeFunctionRecursive. |
| EXPECT_THAT(GetLifetimes(R"( |
| struct Base { |
| virtual ~Base() {} |
| virtual int* f(int* a, int* b) { |
| if (*a > *b) |
| return b; |
| *a -= 1; |
| return f(a, b); |
| } |
| }; |
| |
| struct Derived : public Base { |
| int* f(int* a, int* b) override { |
| if (*a == *b) |
| return a; |
| return Base::f(a, b); |
| } |
| }; |
| )"), |
| LifetimesContain({ |
| {"Base::f", "b: a, a -> a"}, |
| {"Derived::f", "b: a, a -> a"}, |
| })); |
| } |
| |
| } // namespace |
| } // namespace lifetimes |
| } // namespace tidy |
| } // namespace clang |