blob: 0036833bdc372c0473ae2258f3697897cbcea27a [file] [log] [blame]
Luca Versari99fddff2022-05-25 10:22:32 -07001// 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// Tests for basic functionality.
6
7#include "gmock/gmock.h"
8#include "gtest/gtest.h"
9#include "lifetime_analysis/test/lifetime_analysis_test.h"
10
11namespace clang {
12namespace tidy {
13namespace lifetimes {
14namespace {
15
16TEST_F(LifetimeAnalysisTest, CompilationError) {
17 // Check that we don't analyze code that doesn't compile.
18 // This is a regression test -- we actually used to produce the lifetimes
19 // "a -> a" for this test.
20 EXPECT_THAT(GetLifetimes(R"(
21 int* target(int* a) {
22 undefined(&a);
23 return a;
24 }
25 )"),
26 LifetimesAre({{"", "Compilation error -- see log for details"}}));
27}
28
29TEST_F(LifetimeAnalysisTest, CompilationErrorFallback) {
30 // Allow analysis of broken code to check that our fallback for detecting
31 // expressions containing errors works.
32 AnalyzeBrokenCode();
33
34 EXPECT_THAT(
35 GetLifetimes(R"(
36 int* target(int* a) {
37 undefined(&a);
38 return a;
39 }
40 )"),
41 LifetimesAre(
42 {{"target", "ERROR: encountered an expression containing errors"}}));
43}
44
45TEST_F(LifetimeAnalysisTest, CompilationErrorFromWerrorDoesNotPreventAnalysis) {
46 // Warnings upgraded through -Werror should not prevent analysis.
47 EXPECT_THAT(GetLifetimes(R"(
48#pragma clang diagnostic push
49#pragma clang diagnostic error "-Wunused-variable"
50 int* target(int* a) {
51 int i = 0;
52 return a;
53 }
54#pragma clang diagnostic pop
55 )"),
56 LifetimesAre({{"target", "a -> a"}}));
57}
58
59TEST_F(LifetimeAnalysisTest, NoLifetimes) {
60 EXPECT_THAT(GetLifetimes(R"(
61 void target() {
62 }
63 )"),
64 LifetimesAre({{"target", ""}}));
65}
66
67TEST_F(LifetimeAnalysisTest, NoLifetimesArithmetic) {
68 EXPECT_THAT(GetLifetimes(R"(
69 int target(int a, int b) {
70 return (a + b) - (-b) * a;
71 }
72 )"),
73 LifetimesAre({{"target", "(), ()"}}));
74}
75
76TEST_F(LifetimeAnalysisTest, PointerToMemberDoesNotGetLifetime) {
77 EXPECT_THAT(GetLifetimes(R"(
78 struct S {};
79 void target(S* s, int S::*ptr_to_member) {}
80 )"),
81 LifetimesAre({{"target", "a, ()"}}));
82}
83
84TEST_F(LifetimeAnalysisTest, UnconstrainedParameter) {
85 EXPECT_THAT(GetLifetimes(R"(
86 void target(int* a) {
87 }
88 )"),
89 LifetimesAre({{"target", "a"}}));
90}
91
92TEST_F(LifetimeAnalysisTest, ReturnArgumentPtr) {
93 EXPECT_THAT(GetLifetimes(R"(
94 int* target(int* a) {
95 return a;
96 }
97 )"),
98 LifetimesAre({{"target", "a -> a"}}));
99}
100
101TEST_F(LifetimeAnalysisTest, ReturnArgumentPtrInitList) {
102 EXPECT_THAT(GetLifetimes(R"(
103 int* target(int* a) {
104 return { a };
105 }
106 )"),
107 LifetimesAre({{"target", "a -> a"}}));
108}
109
110TEST_F(LifetimeAnalysisTest, ReturnArgumentRef) {
111 EXPECT_THAT(GetLifetimes(R"(
112 int& target(int& a) {
113 return a;
114 }
115 )"),
116 LifetimesAre({{"target", "a -> a"}}));
117}
118
119TEST_F(LifetimeAnalysisTest, ReturnFirstArgumentPtr) {
120 EXPECT_THAT(GetLifetimes(R"(
121 int* target(int* a, int* b) {
122 return a;
123 }
124 )"),
125 LifetimesAre({{"target", "a, b -> a"}}));
126}
127
128TEST_F(LifetimeAnalysisTest, ReturnFirstArgumentRef) {
129 EXPECT_THAT(GetLifetimes(R"(
130 int& target(int& a, int& b) {
131 return a;
132 }
133 )"),
134 LifetimesAre({{"target", "a, b -> a"}}));
135}
136
137TEST_F(LifetimeAnalysisTest, ReturnRefFromPtr) {
138 EXPECT_THAT(GetLifetimes(R"(
139 int& target(int* a) {
140 return *a;
141 }
142 )"),
143 LifetimesAre({{"target", "a -> a"}}));
144}
145
146TEST_F(LifetimeAnalysisTest, ReturnPtrFromRef) {
147 EXPECT_THAT(GetLifetimes(R"(
148 int* target(int& a) {
149 return &a;
150 }
151 )"),
152 LifetimesAre({{"target", "a -> a"}}));
153}
154
155TEST_F(LifetimeAnalysisTest, ReturnDereferencedArgument) {
156 EXPECT_THAT(GetLifetimes(R"(
157 int* target(int** a) {
158 return *a;
159 }
160 )"),
161 LifetimesAre({{"target", "(a, b) -> a"}}));
162}
163
164TEST_F(LifetimeAnalysisTest, ReturnLocalViaPtr) {
165 EXPECT_THAT(GetLifetimes(R"(
166 int* target() {
167 int a = 42;
168 return &a;
169 }
170 )"),
171 LifetimesAre({{"target",
172 "ERROR: function returns reference to a local"}}));
173}
174
175TEST_F(LifetimeAnalysisTest, ReturnLocalViaRef) {
176 EXPECT_THAT(GetLifetimes(R"(
177 int& target() {
178 int a = 42;
179 return a;
180 }
181 )"),
182 LifetimesAre({{"target",
183 "ERROR: function returns reference to a local"}}));
184}
185
186TEST_F(LifetimeAnalysisTest, ReturnStaticViaPtr) {
187 EXPECT_THAT(GetLifetimes(R"(
188 int* target() {
189 static int a = 42;
190 return &a;
191 }
192 )"),
Luca Versaridc8eb2a2023-01-16 10:10:50 -0800193 LifetimesAre({{"target", "-> a"}}));
Luca Versari99fddff2022-05-25 10:22:32 -0700194}
195
196TEST_F(LifetimeAnalysisTest, StringLiteral) {
197 EXPECT_THAT(GetLifetimes(R"(
198 const char* target() {
199 return "this is a string literal";
200 }
201 )"),
Luca Versaridc8eb2a2023-01-16 10:10:50 -0800202 LifetimesAre({{"target", "-> a"}}));
Luca Versari99fddff2022-05-25 10:22:32 -0700203}
204
205TEST_F(LifetimeAnalysisTest, OutParameter) {
206 EXPECT_THAT(GetLifetimes(R"(
207 void target(int& a) {
208 a = 42;
209 }
210 )"),
211 LifetimesAre({{"target", "a"}}));
212}
213
214TEST_F(LifetimeAnalysisTest, AssigningToPtrParamDoesNotChangeLifetime) {
215 EXPECT_THAT(GetLifetimes(R"(
216 void target(int* p) {
217 int a = 42;
218 p = &a;
219 }
220 )"),
221 LifetimesAre({{"target", "a"}}));
222}
223
224TEST_F(LifetimeAnalysisTest, PtrInitializationTransfersLifetimes) {
225 EXPECT_THAT(GetLifetimes(R"(
226 int* target(int* p) {
227 int* p2 = p;
228 return p2;
229 }
230 )"),
231 LifetimesAre({{"target", "a -> a"}}));
232}
233
234TEST_F(LifetimeAnalysisTest, PtrAssignmentTransfersLifetimes) {
235 EXPECT_THAT(GetLifetimes(R"(
236 int* target(int* p) {
237 int* p2;
238 p2 = p;
239 return p2;
240 }
241 )"),
242 LifetimesAre({{"target", "a -> a"}}));
243}
244
245TEST_F(LifetimeAnalysisTest, RefInitializationTransfersLifetimes) {
246 EXPECT_THAT(GetLifetimes(R"(
247 int& target(int& r) {
248 int& r2 = r;
249 return r2;
250 }
251 )"),
252 LifetimesAre({{"target", "a -> a"}}));
253}
254
255TEST_F(LifetimeAnalysisTest, RefAssignmentDoesNotTransferLifetimes) {
256 EXPECT_THAT(GetLifetimes(R"(
257 int& target(int& r) {
258 int a = 42;
259 int& r2 = a;
260 r2 = r;
261 return r2;
262 }
263 )"),
264 LifetimesAre({{"target",
265 "ERROR: function returns reference to a local"}}));
266}
267
268TEST_F(LifetimeAnalysisTest, ReturnLocalSneaky_Initialization) {
269 EXPECT_THAT(GetLifetimes(R"(
270 int* target(int* arg1) {
271 // Initialization should be aware that outer pointer is invariant in its
272 // type.
273 int** pp = &arg1;
274 int local = 42;
275 *pp = &local;
276 return arg1;
277 }
278 )"),
279 LifetimesAre({{"target",
280 "ERROR: function returns reference to a local"}}));
281}
282
283TEST_F(LifetimeAnalysisTest, ReturnLocalSneaky_Assignment) {
284 EXPECT_THAT(GetLifetimes(R"(
285 int* target(int* arg1) {
286 // Assignment should be aware that outer pointer is invariant in its type.
287 int** pp;
288 pp = &arg1;
289 int local = 42;
290 *pp = &local;
291 return arg1;
292 }
293 )"),
294 LifetimesAre({{"target",
295 "ERROR: function returns reference to a local"}}));
296}
297
298TEST_F(LifetimeAnalysisTest, ReturnLocalSneaky2_Initialization) {
299 EXPECT_THAT(GetLifetimes(R"(
300 int* target(int* arg1) {
301 // Initialization should be aware that outer pointer is invariant in its
302 // type.
303 int** pp = &arg1;
304 int local = 42;
305 arg1 = &local;
306 return *pp;
307 }
308 )"),
309 LifetimesAre({{"target",
310 "ERROR: function returns reference to a local"}}));
311}
312
313TEST_F(LifetimeAnalysisTest, ReturnLocalSneaky2_Assignment) {
314 EXPECT_THAT(GetLifetimes(R"(
315 int* target(int* arg1) {
316 // Assignment should be aware that outer pointer is invariant in its type.
317 int** pp;
318 pp = &arg1;
319 int local = 42;
320 arg1 = &local;
321 return *pp;
322 }
323 )"),
324 LifetimesAre({{"target",
325 "ERROR: function returns reference to a local"}}));
326}
327
328TEST_F(LifetimeAnalysisTest, ReturnLocalSneaky3_Initialization) {
329 EXPECT_THAT(GetLifetimes(R"(
330 int* target(int* arg1) {
331 int*& pp = arg1;
332 int local = 42;
333 arg1 = &local;
334 return pp;
335 }
336 )"),
337 LifetimesAre({{"target",
338 "ERROR: function returns reference to a local"}}));
339}
340
341TEST_F(LifetimeAnalysisTest, SwapPointers) {
342 EXPECT_THAT(GetLifetimes(R"(
343 void swap_ptr(int** pp1, int** pp2) {
344 int* tmp = *pp2;
345 *pp2 = *pp1;
346 *pp1 = tmp;
347 }
348 )"),
349 LifetimesAre({{"swap_ptr", "(a, b), (a, c)"}}));
350}
351
352TEST_F(LifetimeAnalysisTest, DuplicatePointer) {
353 EXPECT_THAT(GetLifetimes(R"(
354 void duplicate_ptr(int* from, int** to1, int** to2) {
355 *to1 = from;
356 *to2 = from;
357 }
358 )"),
359 LifetimesAre({{"duplicate_ptr", "a, (a, b), (a, c)"}}));
360}
361
362TEST_F(LifetimeAnalysisTest, Aliasing) {
363 EXPECT_THAT(GetLifetimes(R"(
364 int* target(int** a, int** b, int* c) {
365 *a = c;
366 return *b;
367 }
368 )"),
369 LifetimesAre({{"target", "(a, b), (c, d), a -> c"}}));
370}
371
372TEST_F(LifetimeAnalysisTest, IncompleteType) {
373 // Test that we can handle pointers to incomplete types.
374 EXPECT_THAT(GetLifetimes(R"(
375 struct S;
376 S* target(S* s) {
377 return s;
378 }
379 )"),
380 LifetimesAre({{"target", "a -> a"}}));
381}
382
383TEST_F(LifetimeAnalysisTest, DISABLED_IncompleteTypeTemplate) {
384 // TODO(mboehme): Disabled because it returns the wrong lifetimes.
385 // S<int*> is never instantiated because we only deal with pointers to it,
386 // so it's an incomplete type.
387 //
388 // We can handle incomplete types in principle, but in this case, because
389 // we don't create any pointees for the fields of `S<int*>`, we will produce
390 // these incorrect lifetimes:
391 // (a, b) -> (c, b)
392 // Even more strangely, the lifetimes we infer change (to the correct ones)
393 // once we happen to instantiate S<int*> somewhere else in the same
394 // translation unit.
395 //
396 // I'm not sure how best to solve this. We could simply force instantiation
397 // of all uninstantiated templates we see, but I believe this might change the
398 // semantics of the program in subtle ways.
399 //
400 // The better alternative seems to be: If we're unifying lifetimes of an
401 // object that is of an instantiated class template type, unify the lifetimes
402 // of its template arguments too. This can be overly restrictive -- think of a
403 // class template that doesn't actually use its template arguments in any of
404 // its fields, e.g. `template <class T> struct S {};`. However, it seems to be
405 // the only option that produces consistent results without requiring us to
406 // instantiate class templates that could otherwise be used as incomplete
407 // types.
408 EXPECT_THAT(GetLifetimes(R"(
409 template <class T>
410 struct S {
411 T t;
412 };
413
414 S<int*>* target(S<int*>* s) {
415 return s;
416 }
417 )"),
418 LifetimesAre({{"target", "(a, b) -> (a, b)"}}));
419}
420
Luca Versariefeaf272023-01-16 10:19:28 -0800421TEST_F(LifetimeAnalysisTest, UndefinedFunctionNoLifetimeElision) {
Luca Versari99fddff2022-05-25 10:22:32 -0700422 EXPECT_THAT(
423 GetLifetimes(R"(
424 int* f(int* a);
425 int* target(int* a) {
426 return f(a);
427 }
428 )"),
429 LifetimesAre({{"f", "ERROR: Lifetime elision not enabled for 'f'"},
430 {"target",
431 "ERROR: No lifetimes for callee 'f': Lifetime elision not "
432 "enabled for 'f'"}}));
433}
434
Luca Versariefeaf272023-01-16 10:19:28 -0800435TEST_F(LifetimeAnalysisTest, UndefinedFunctionLifetimeElision) {
Luca Versari99fddff2022-05-25 10:22:32 -0700436 EXPECT_THAT(GetLifetimes(R"(
437 #pragma clang lifetime_elision
438 int* f(int* a);
439 int* target(int* a) {
440 return f(a);
441 }
442 )"),
443 LifetimesAre({{"f", "a -> a"}, {"target", "a -> a"}}));
444}
445
446TEST_F(LifetimeAnalysisTest, ForwardDeclaration) {
447 EXPECT_THAT(GetLifetimes(R"(
448 int* f(int* a);
449 int* target(int* a) {
450 return f(a);
451 }
452 int* f(int* a) {
453 return a;
454 }
455 )"),
456 LifetimesAre({{"f", "a -> a"}, {"target", "a -> a"}}));
457}
458
Luca Versariefeaf272023-01-16 10:19:28 -0800459TEST_F(LifetimeAnalysisTest, OverwriteSingleDestination) {
Martin Brænneea74ebb2022-06-08 06:06:34 -0700460 EXPECT_THAT(GetLifetimes(R"(
461 int* target(int* a, int* b) {
462 int** pp = &b;
463 // There is only one thing that `pp` can be pointing at, so the analysis
464 // should conclude that `b` is being overwritten with `a`.
465 *pp = a;
466 return b;
467 }
468 )"),
469 LifetimesAre({{"target", "a, b -> a"}}));
470}
471
Luca Versariefeaf272023-01-16 10:19:28 -0800472TEST_F(LifetimeAnalysisTest, DISABLED_OverwriteSingleDestinationVariant) {
473 // TODO(veluca): analysis currently concludes something "overly restrictive"
474 // on this function. Figure out when we run on real world code whether this is
475 // an actual problem we might want to do something about.
Martin Brænneea74ebb2022-06-08 06:06:34 -0700476 EXPECT_THAT(GetLifetimes(R"(
477 // Similar to above, but potentially leave `pp` uninitialized.
478 int* target(int* a, int* b) {
479 int** pp;
480 if (*a > 0) {
481 pp = &b;
482 }
483 // If `pp` is uninitialized, the following is UB, so the analysis can
484 // assume that `pp` was initialized to point to `b`.
485 // This particular test function is pretty terrible style, but it seems
486 // plausible that similar situations can come up in more reasonable code.
487 *pp = a;
488 return b;
489 }
490 )"),
491 LifetimesAre({{"target", "a, b -> a"}}));
492}
493
Luca Versariefeaf272023-01-16 10:19:28 -0800494TEST_F(LifetimeAnalysisTest, OverwriteMultipleDestinations) {
Martin Brænneea74ebb2022-06-08 06:06:34 -0700495 EXPECT_THAT(GetLifetimes(R"(
496 // This is a regression test. The analysis used to conclude falsely that `b`
497 // was unconditionally being overwritten with `a` in the assignment and was
498 // therefore producing the wrong lifetimes "a, b -> a".
499 int* target(int* a, int* b) {
500 int** pp = *a > 0? &a : &b;
501 // The analysis should understand that the following assignment _might_
502 // overwrite `b` with `a` but does not necessarily do so.
503 *pp = a;
504 return b;
505 }
506 )"),
507 LifetimesAre({{"target", "a, a -> a"}}));
508}
509
Luca Versari99fddff2022-05-25 10:22:32 -0700510} // namespace
511} // namespace lifetimes
512} // namespace tidy
513} // namespace clang