blob: fea359f0744066763471bf2e5e6940820343ebf0 [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 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