blob: 8b60e37bd59883fa8eac9c0b187d5fb12ff81f54 [file] [log] [blame]
Googlerb0019ca2021-11-26 14:20:10 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "lifetime_annotations/lifetime_annotations.h"
6
7#include <string>
8#include <utility>
9
10#include "devtools/cymbal/data_flow/testing_support.h"
11#include "lifetime_analysis/test/named_func_lifetimes.h"
12#include "lifetime_analysis/test/run_on_code.h"
13#include "testing/base/public/gunit.h"
14#include "third_party/llvm/llvm-project/clang/include/clang/ASTMatchers/ASTMatchFinder.h"
15#include "third_party/llvm/llvm-project/clang/include/clang/ASTMatchers/ASTMatchers.h"
16
17namespace devtools_rust {
18namespace {
19
20using testing::StartsWith;
21using testing::status::IsOkAndHolds;
22using testing::status::StatusIs;
23
24std::string QualifiedName(const clang::FunctionDecl* func) {
25 std::string str;
26 llvm::raw_string_ostream ostream(str);
27 func->printQualifiedName(ostream);
28 ostream.flush();
29 return str;
30}
31
32class LifetimeAnnotationsTest : public testing::Test {
33 protected:
34 absl::StatusOr<NamedFuncLifetimes> GetNamedLifetimeAnnotations(
35 absl::string_view code,
36 const clang::tooling::FileContentMappings& file_contents =
37 clang::tooling::FileContentMappings()) {
38 absl::StatusOr<NamedFuncLifetimes> result;
39 runOnCodeWithLifetimeHandlers(
40 code,
41 [&result](clang::ASTContext& ast_context,
42 const LifetimeAnnotationContext& lifetime_context) {
43 using clang::ast_matchers::findAll;
44 using clang::ast_matchers::functionDecl;
45 using clang::ast_matchers::match;
46
47 NamedFuncLifetimes named_func_lifetimes;
48 for (const auto& node :
49 match(findAll(functionDecl().bind("func")), ast_context)) {
50 if (const auto* func =
51 node.getNodeAs<clang::FunctionDecl>("func")) {
52 llvm::Expected<FunctionLifetimes> func_lifetimes =
53 GetLifetimeAnnotations(func, lifetime_context);
54
55 if (!func_lifetimes) {
56 result = absl::UnknownError(
57 llvm::toString(func_lifetimes.takeError()));
58 return;
59 }
60 named_func_lifetimes.Add(QualifiedName(func),
61 NameLifetimes(*func_lifetimes));
62 }
63 }
64
65 result = std::move(named_func_lifetimes);
66 },
67 {}, file_contents);
68
69 return result;
70 }
71};
72
73TEST_F(LifetimeAnnotationsTest, NoLifetimes) {
74 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
75 int f(int);
76 )"),
77 IsOkAndHolds(LifetimesAre({{"f", "()"}})));
78}
79
80TEST_F(LifetimeAnnotationsTest, Failure_NoAnnotationsNoLifetimeElision) {
81 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
82 int** f(int*);
83 )"),
84 StatusIs(absl::StatusCode::kUnknown,
85 StartsWith("Lifetime elision not enabled")));
86}
87
88TEST_F(LifetimeAnnotationsTest, Failure_NoAnnotationsElisionPragmaInWrongFile) {
89 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
90 #pragma lifetime_elision
91 #include "header.h"
92 )",
93 {std::make_pair("header.h", R"(
94 int** f(int*);
95 )")}),
96 StatusIs(absl::StatusCode::kUnknown,
97 StartsWith("Lifetime elision not enabled")));
98}
99
100TEST_F(LifetimeAnnotationsTest, LifetimeElision_OneInputLifetime) {
101 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
102 #pragma lifetime_elision
103 int** f(int*);
104 )"),
105 IsOkAndHolds(LifetimesAre({{"f", "a -> (a, a)"}})));
106}
107
108TEST_F(LifetimeAnnotationsTest, LifetimeElision_NoOutputLifetimes) {
109 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
110 #pragma lifetime_elision
111 void f(int**, int *);
112 )"),
113 IsOkAndHolds(LifetimesAre({{"f", "(a, b), c"}})));
114}
115
116TEST_F(LifetimeAnnotationsTest, LifetimeElision_Templates) {
117 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
118 #pragma lifetime_elision
119 template <class T> class vector {};
120 int* f(vector<int *>);
121 vector<int*> g(int *);
122 )"),
123 IsOkAndHolds(LifetimesAre({{"f", "a -> a"}, {"g", "a -> a"}})));
124}
125
126TEST_F(LifetimeAnnotationsTest, LifetimeElision_Method) {
127 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
128 #pragma lifetime_elision
129 struct S {
130 int** method(int *, int *);
131 };
132 )"),
133 IsOkAndHolds(LifetimesAre({{"S::method", "c: a, b -> (c, c)"}})));
134}
135
136TEST_F(LifetimeAnnotationsTest, LifetimeElision_FailureTooFewInputLifetimes) {
137 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
138 #pragma lifetime_elision
139 int* f();
140 )"),
141 StatusIs(absl::StatusCode::kUnknown,
142 StartsWith("Cannot determine output lifetimes")));
143}
144
145TEST_F(LifetimeAnnotationsTest, LifetimeElision_FailureTooManyInputLifetimes) {
146 EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
147 #pragma lifetime_elision
148 int* f(int**);
149 )"),
150 StatusIs(absl::StatusCode::kUnknown,
151 StartsWith("Cannot determine output lifetimes")));
152}
153
154} // namespace
155} // namespace devtools_rust