blob: 4927f33a7ee851e9e5cffdce80e1e62281e8ca60 [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 involving class templates.
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, StructTemplate) {
17 EXPECT_THAT(GetLifetimes(R"(
18 template <typename T>
19 struct S {
20 T a;
21 };
22 int* target(S<int*> s) {
23 return s.a;
24 }
25 )"),
26 LifetimesAre({{"target", "a -> a"}}));
27}
28
29TEST_F(LifetimeAnalysisTest, StructTemplatePtr) {
30 EXPECT_THAT(GetLifetimes(R"(
31 template <typename T>
32 struct S {
33 T a;
34 };
35 int* target(S<int*>* s) {
36 return s->a;
37 }
38 )"),
39 LifetimesAre({{"target", "(a, b) -> a"}}));
40}
41
42TEST_F(LifetimeAnalysisTest, StructTemplateInnerDoubleUsage) {
43 EXPECT_THAT(GetLifetimes(R"(
44 template <typename T>
45 struct S {
46 T a;
47 T b;
48 };
49 int* target(S<int**>* s) {
50 int l = 0;
51 *s->b = &l;
52 return *s->a;
53 }
54 )"),
55 LifetimesAre({{"target",
56 "ERROR: function returns reference to a local "
57 "through parameter 's'"}}));
58}
59
60TEST_F(LifetimeAnalysisTest, StructTwoTemplateArguments) {
61 EXPECT_THAT(GetLifetimes(R"(
62 template <typename T, typename U>
63 struct S {
64 T t;
65 U u;
66 };
67
68 int* return_t(S<int*, int*>& v) {
69 return v.t;
70 }
71
72 int* return_u(S<int*, int*>& v) {
73 return v.u;
74 }
75 )"),
76 LifetimesAre({{"return_t", "(<a, b>, c) -> a"},
77 {"return_u", "(<a, b>, c) -> b"}}));
78}
79
80TEST_F(LifetimeAnalysisTest, StructTwoTemplateArgumentsNestedClasses) {
81 EXPECT_THAT(GetLifetimes(R"(
82 template <typename T>
83 struct Outer {
84 template <typename U>
85 struct Inner {
86 T t;
87 U u;
88 };
89 };
90
91 int* return_t(Outer<int*>::Inner<int*>& inner) {
92 return inner.t;
93 }
94
95 int* return_u(Outer<int*>::Inner<int*>& inner) {
96 return inner.u;
97 }
98 )"),
99 LifetimesAre({{"return_t", "(<a>::<b>, c) -> a"},
100 {"return_u", "(<a>::<b>, c) -> b"}}));
101}
102
103TEST_F(LifetimeAnalysisTest, StructTwoTemplateArgumentsConstructInner) {
104 EXPECT_THAT(GetLifetimes(R"(
105 template <typename T>
106 struct Inner {
107 Inner (T a): a(a) {}
108 T a;
109 };
110 template <typename T, typename U>
111 struct Outer {
112 Outer(T a, U& b): a(a), b(b) {}
113 T a;
114 U b;
115 };
116 int* target(int* a, int* b) {
117 Inner<int*> is(b);
118 Outer<int*, Inner<int*>> s(a, is);
119 return s.b.a;
120 }
121 )"),
122 LifetimesContain({{"target", "a, b -> b"}}));
123}
124
125TEST_F(LifetimeAnalysisTest, StructTwoTemplateArgumentsTernary) {
126 EXPECT_THAT(GetLifetimes(R"(
127 template <typename T, typename U>
128 struct S {
129 T t;
130 U u;
131 };
132
133 int* f(S<int*, int*>& v) {
134 return *v.t < *v.u ? v.t : v.u;
135 }
136 )"),
137 LifetimesAre({{"f", "(<a, a>, b) -> a"}}));
138}
139
140TEST_F(LifetimeAnalysisTest, StructTemplateLocalVariable) {
141 EXPECT_THAT(GetLifetimes(R"(
142 template <typename T>
143 struct S {
144 T a;
145 };
146 const int* target(S<int*> s) {
147 S<const int*> t;
148 t.a = s.a;
149 return t.a;
150 }
151 )"),
152 LifetimesAre({{"target", "a -> a"}}));
153}
154
155TEST_F(LifetimeAnalysisTest, StructTemplatePointerToMember) {
156 EXPECT_THAT(GetLifetimes(R"(
157 template <typename T, typename U>
158 struct S {
159 T a;
160 U b;
161 };
162 int** target(S<int*, int*>& s) {
163 return &s.b;
164 }
165 )"),
166 LifetimesAre({{"target", "(<a, b>, c) -> (b, c)"}}));
167}
168
169TEST_F(LifetimeAnalysisTest, StructTemplateWithPointer) {
170 EXPECT_THAT(GetLifetimes(R"(
171 template <typename T>
172 struct [[clang::annotate("lifetime_params", "a")]] S {
173 [[clang::annotate("member_lifetimes", "a")]]
174 T* a;
175 };
176 int** target(S<int*>& s) {
177 return s.a;
178 }
179 )"),
180 LifetimesAre({{"target", "(<a> [b], c) -> (a, b)"}}));
181}
182
183TEST_F(LifetimeAnalysisTest, StructTemplateWithTemplate) {
184 EXPECT_THAT(GetLifetimes(R"(
185 template <typename T>
186 struct S {
187 T a;
188 };
189 int* target(S<S<int*>> s) {
190 return s.a.a;
191 }
192 )"),
193 LifetimesAre({{"target", "a -> a"}}));
194}
195
196TEST_F(LifetimeAnalysisTest, StructTemplateInnerTemplate) {
197 EXPECT_THAT(GetLifetimes(R"(
198 template <typename T>
199 struct U {
200 T a;
201 };
202 template <typename T>
203 struct S {
204 U<T> a;
205 };
206 int* target(S<int*>* s) {
207 return s->a.a;
208 }
209 )"),
210 LifetimesAre({{"target", "(a, b) -> a"}}));
211}
212
213TEST_F(LifetimeAnalysisTest, DISABLED_StructTemplateInnerTemplatePtr) {
214 // TODO(veluca): we don't correctly propagate lifetime arguments when creating
215 // template arguments for fields that use the template argument indirectly,
216 // such as behind a pointer or as template arguments to a struct passed as a
217 // template argument to the member.
218 EXPECT_THAT(GetLifetimes(R"(
219 template <typename T>
220 struct U {
221 T a;
222 };
223 template <typename T>
224 struct S {
225 U<T*> a;
226 };
227 int* target(S<int*>* s) {
228 return *s->a.a;
229 }
230 )"),
231 LifetimesAre({{"target", "(a, b) -> a"}}));
232}
233
234TEST_F(LifetimeAnalysisTest, StructTemplateSwapArguments) {
235 EXPECT_THAT(GetLifetimes(R"(
236 template <typename T, typename U>
237 struct [[clang::annotate("lifetime_params", "a")]] S {
238 T a;
239 U b;
240 [[clang::annotate("member_lifetimes", "a", "a")]]
241 S<U, T>* next;
242 };
243 int* target(S<int*, int*>* s) {
244 return s->next->a;
245 }
246 int* target_swtwice(S<int*, int*>* s) {
247 return s->next->next->a;
248 }
249 )"),
250 LifetimesAre({{"target", "(<a, b> [c], d) -> b"},
251 {"target_swtwice", "(<a, b> [c], d) -> a"}}));
252}
253
254TEST_F(LifetimeAnalysisTest, StructTemplateMemberCall) {
255 EXPECT_THAT(
256 GetLifetimes(R"(
257 template <typename T>
258 // TODO(mboehme): The real `vector` doesn't have lifetime parameters, but
259 // we use these here as we don't have the ability to do `lifetime_cast`s
260 // yet.
261 struct [[clang::annotate("lifetime_params", "a")]] vector {
262 T& operator[](int i) { return a[i]; }
263 [[clang::annotate("member_lifetimes", "a")]]
264 T* a;
265 };
266
267 int* get(vector<int*>& v, int i) {
268 return v[i];
269 }
270 )"),
271 LifetimesAre({{"vector<int *>::operator[]", "(<a> [b], c): () -> (a, b)"},
272 {"get", "(<a> [b], c), () -> a"}}));
273}
274
275TEST_F(LifetimeAnalysisTest, StructTwoTemplateArgumentsCall) {
276 EXPECT_THAT(
277 GetLifetimes(R"(
278 template <typename T, typename U>
279 struct S {
280 T t;
281 U u;
282 };
283
284 int* f(S<int*, int*>& v) {
285 return *v.t < *v.u ? v.t : v.u;
286 }
287
288 int* g(S<int*, int*>& v) {
289 return f(v);
290 }
291 )"),
292 LifetimesAre({{"f", "(<a, a>, b) -> a"}, {"g", "(<a, a>, b) -> a"}}));
293}
294
295TEST_F(LifetimeAnalysisTest, StructNoTemplateInnerTemplate) {
296 EXPECT_THAT(
297 GetLifetimes(R"(
298 template <typename T>
299 struct X {
300 T field;
301 };
302
303 struct Y {
304 X<int*> field;
305 };
306
307 int* target_byref(Y& s) {
308 return s.field.field;
309 }
310
311 int* target_byvalue(Y s) {
312 return s.field.field;
313 }
314 )"),
315 LifetimesContain({{"target_byref", "a -> a"},
316 {"target_byvalue",
317 "ERROR: function returns reference to a local"}}));
318}
319
320TEST_F(LifetimeAnalysisTest, StructTemplateReturn) {
321 EXPECT_THAT(GetLifetimes(R"(
322 template <typename T>
323 struct S {
324 T a;
325 };
326 S<int*> target(S<int*>& s) {
327 return s;
328 }
329 )"),
330 LifetimesAre({{"target", "(a, b) -> a"}}));
331}
332
333TEST_F(LifetimeAnalysisTest, StructTemplateReturnXvalue) {
334 EXPECT_THAT(GetLifetimes(R"(
335 template <typename T>
336 struct S {
337 T a;
338 };
339 S<int*> target(S<int*> s) {
340 return s;
341 }
342 )"),
343 LifetimesAre({{"target", "a -> a"}}));
344}
345
346TEST_F(LifetimeAnalysisTest, StructTemplateReturnCall) {
347 EXPECT_THAT(GetLifetimes(R"(
348 template <typename T>
349 struct S {
350 T a;
351 };
352 S<int*> take_by_ref(S<int*>& s) {
353 return s;
354 }
355 S<int*> take_by_value(S<int*> s) {
356 return s;
357 }
358 )"),
359 LifetimesAre({{"take_by_ref", "(a, b) -> a"},
360 {"take_by_value", "a -> a"}}));
361}
362
363TEST_F(LifetimeAnalysisTest, StructTemplateReturnLocal) {
364 EXPECT_THAT(GetLifetimes(R"(
365 template <typename T>
366 struct S {
367 T a;
368 };
369 S<int*> target(int* a) {
370 int i = 42;
371 return { &i };
372 }
373 )"),
374 LifetimesAre({{"target",
375 "ERROR: function returns reference to a local"}}));
376}
377
378TEST_F(LifetimeAnalysisTest, ReturnStructTemporaryConstructor) {
379 EXPECT_THAT(GetLifetimes(R"(
380 template <typename T>
381 struct S {
382 S(T a) : a(a) {}
383 T a;
384 };
385 S<int*> ConstructorCastSyntax(int* a) {
386 return S(a);
387 }
388 S<int*> ConstructTemporarySyntax(int* a) {
389 return S{a};
390 }
391 )"),
392 LifetimesAre({{"S<int *>::S", "(a, b): a"},
393 {"ConstructorCastSyntax", "a -> a"},
394 {"ConstructTemporarySyntax", "a -> a"}}));
395}
396
397TEST_F(LifetimeAnalysisTest, ReturnStructTemporaryInitializerList) {
398 EXPECT_THAT(GetLifetimes(R"(
399 template <typename T>
400 struct S {
401 T a;
402 };
403 S<int*> InitListExpr(int* a) {
404 return {a};
405 }
406 S<int*> CastWithInitListExpr(int* a) {
407 return S<int*>{a};
408 }
409 )"),
410 LifetimesAre({{"InitListExpr", "a -> a"},
411 {"CastWithInitListExpr", "a -> a"}}));
412}
413
414TEST_F(LifetimeAnalysisTest, ReturnUnionTemporaryInitializerList) {
415 EXPECT_THAT(GetLifetimes(R"(
416 template <typename T>
417 union S {
418 T a;
419 };
420 S<int*> target(int* a) {
421 return {a};
422 }
423 )"),
424 LifetimesAre({{"target", "a -> a"}}));
425}
426
427TEST_F(LifetimeAnalysisTest, DISABLED_StructTemplateReturnPassByValue) {
428 // TODO(veluca): disabled because calling a function with a pass-by-value
429 // struct is not yet supported -- see TODO in TransferLifetimesForCall.
430 EXPECT_THAT(GetLifetimes(R"(
431 template <typename T>
432 struct S {
433 T a;
434 };
435 S<int*> t(S<int*> s) {
436 return s;
437 }
438 S<int*> target(S<int*> s) {
439 return t(s);
440 }
441 )"),
442 LifetimesAre({{"t", "a -> a"}, {"target", "a -> a"}}));
443}
444
445TEST_F(LifetimeAnalysisTest, StructWithTemplateArgs) {
446 EXPECT_THAT(GetLifetimes(R"(
447template <typename T, typename U>
448struct S {
449 T t;
450 U u;
451};
452
453int* target(S<int*, int*>* s, int* t, int* u) {
454 s->t = t;
455 s->u = u;
456 return s->t;
457}
458 )"),
459 // With template arguments, now the struct and its fields can
460 // have different lifetimes.
461 LifetimesAre({{"target", "(<a, b>, c), a, b -> a"}}));
462}
463
464TEST_F(LifetimeAnalysisTest, ExampleFromRFC) {
465 // This is an example from the lifetimes RFC.
466 EXPECT_THAT(GetLifetimes(R"(
467template <typename T>
468struct R {
469 R(T t) : t(t) {}
470 T t;
471};
472
473bool some_condition();
474
475template <typename T>
476struct S {
477 S(T a, T b) : r(some_condition() ? R(a) : R(b)) {}
478 R<T> r;
479};
480
481int* target(int* a, int* b) {
482 S<int*> s(a, b);
483 return s.r.t;
484}
485 )"),
486 LifetimesContain({{"R<int *>::R", "(a, b): a"},
487 {"S<int *>::S", "(a, b): a, a"},
488 {"target", "a, a -> a"}}));
489}
490
491TEST_F(LifetimeAnalysisTest, VariadicTemplate) {
492 EXPECT_THAT(GetLifetimes(R"(
493 template <int idx, typename... Args> struct S {};
494 template <int idx, typename T, typename... Args>
495 struct S<idx, T, Args...> {
496 T t;
497 S<idx+1, Args...> nested;
498 };
499
500 template <typename... Args>
501 struct tuple: public S<0, Args...> {};
502
503 int* target(tuple<int*, int*>& s) {
504 return s.nested.t;
505 }
506 )"),
507 LifetimesAre({{"target", "(<a, b>, c) -> b"}}));
508}
509
510TEST_F(LifetimeAnalysisTest, DISABLED_VariadicTemplateConstructTrivial) {
511 EXPECT_THAT(GetLifetimes(R"(
512 template <int idx, typename... Args> struct S {};
513 template <int idx, typename T, typename... Args>
514 struct S<idx, T, Args...> {
515 T t;
516 S<idx+1, Args...> nested;
517 };
518
519 template <typename... Args>
520 struct tuple: public S<0, Args...> {};
521
522 void target(int* a, int* b) {
523 tuple<int*, int*> s = {a, b};
524 }
525 )"),
526 LifetimesAre({{"target", "a, b"}}));
527}
528
529TEST_F(LifetimeAnalysisTest, VariadicTemplateConstruct) {
530 EXPECT_THAT(GetLifetimes(R"(
531 template <typename... Args> struct S { S() {} };
532 template <typename T, typename... Args>
533 struct S<T, Args...> {
534 T t;
535 S<Args...> nested;
536 S(T t, Args... args): t(t), nested(args...) {}
537 };
538
539 void target(int* a, int* b) {
540 S<int*, int*> s = {a, b};
541 }
542 )"),
543 LifetimesContain({{"target", "a, b"}}));
544}
545
546TEST_F(LifetimeAnalysisTest, NoexceptTemplate) {
547 EXPECT_THAT(GetLifetimes(R"(
548 template <typename T>
549 struct S {
550 S() noexcept(isnoexcept<T>()) {}
551 template <typename U>
552 static constexpr bool isnoexcept() { return true; }
553 };
554
555 void f() {
556 S<int> s;
557 }
558 )"),
559 LifetimesContain({{"f", ""}}));
560}
561
562TEST_F(LifetimeAnalysisTest, TypeTemplateArgAfterNonType) {
563 // Minimized repro for a crash from b/228325046.
564 EXPECT_THAT(GetLifetimes(R"(
565 template<int _Idx, typename _Head>
566 struct _Head_base
567 {
568 constexpr _Head_base(_Head&& __h)
569 : _M_head_impl(__h) { }
570
571 _Head _M_head_impl;
572 };
573
574 void f() {
575 _Head_base<0, void*> head_base(nullptr);
576 }
577 )"),
578 LifetimesContain({{"f", ""}}));
579}
580
581TEST_F(LifetimeAnalysisTest,
582 TemplateContainingTypedefInstantiatedAnotherTemplate) {
583 // Minimized repro for a crash from b/228325046.
584 // The scenario that triggered the crash is:
585 // - We have a template (in this case `remove_reference`) containing a typedef
586 // - That typedef depends on a template parameter
587 // - We instantiate the template with an argument that is another template
588 // The bug was that we weren't desugaring the typedef and hence coming up with
589 // a different value for the depth of the template argument than
590 // TemplateTypeParmType::getDepth() uses.
591 EXPECT_THAT(GetLifetimes(R"(
592 namespace std {
593 template <typename T1, typename T2> struct pair {
594 T1 t1;
595 T2 t2;
596 };
597
598 template<typename _Tp>
599 struct remove_reference
600 { typedef _Tp type; };
601
602 template<typename _Tp>
603 constexpr _Tp&&
604 forward(typename std::remove_reference<_Tp>::type& __t) noexcept
605 { return static_cast<_Tp&&>(__t); }
606 }
607
608 void f() {
609 std::pair<int, int> p;
610 std::forward<decltype(p)>(p);
611 }
612 )"),
613 LifetimesContain({{"f", ""}}));
614}
615
616TEST_F(LifetimeAnalysisTest, DISABLED_ReturnPointerToTemplate) {
617 EXPECT_THAT(
618 GetLifetimes(R"(
619 template <class T> struct S { T t; };
620 S<int*>* target(S<int*>* s) {
621 return s;
622 }
623 )"),
624 // TODO(b/230456778): This currently erroneously returns (a, b) -> (c, b)
625 LifetimesAre({{"f", "(a, b) -> (a, b)"}}));
626}
627
628} // namespace
629} // namespace lifetimes
630} // namespace tidy
631} // namespace clang