xref: /llvm-project/clang/test/Analysis/lifetime-extended-regions.cpp (revision 060137038ab9246b377e190ae3c6f272fa57cbfc)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\
2 // RUN:                    -analyzer-checker=debug.ExprInspection\
3 // RUN:                    -Wno-dangling -Wno-c++1z-extensions\
4 // RUN:                    -verify=expected,cpp14\
5 // RUN:                    -x c++ -std=c++14 %s
6 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\
7 // RUN:                    -analyzer-checker=debug.ExprInspection\
8 // RUN:                    -analyzer-config elide-constructors=false\
9 // RUN:                    -Wno-dangling -Wno-c++1z-extensions\
10 // RUN:                    -verify=expected,cpp14\
11 // RUN:                    -x c++ -std=c++14 %s
12 // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core\
13 // RUN:                    -analyzer-checker=debug.ExprInspection\
14 // RUN:                    -Wno-dangling -verify=expected,cpp17\
15 // RUN:                    -x c++ -std=c++17 %s
16 
17 template<typename T>
18 void clang_analyzer_dump(T&&) {}
19 
20 template<typename T>
21 T create() { return T{}; }
22 
23 template<typename T>
24 T const& select(bool cond, T const& t, T const& u) { return cond ? t : u; }
25 
26 struct Composite {
27   int x;
28   int y;
29 };
30 
31 struct Derived : Composite {
32   int z;
33 };
34 
35 template<typename T>
36 struct Array {
37   T array[20];
38 
39   T&& front() && { return static_cast<T&&>(array[0]); }
40 };
41 
42 void whole_object() {
43   int const& i = 10; // extends `int`
44   clang_analyzer_dump(i); // expected-warning-re {{&lifetime_extended_object{int, i, S{{[0-9]+}}} }}
45   Composite&& c = Composite{}; // extends `Composite`
46   clang_analyzer_dump(c); // expected-warning-re {{&lifetime_extended_object{Composite, c, S{{[0-9]+}}} }}
47   auto&& a = Array<int>{}; // extends `Array<int>`
48   clang_analyzer_dump(a); // expected-warning-re {{&lifetime_extended_object{Array<int>, a, S{{[0-9]+}}} }}
49   Composite&& d = Derived{}; // extends `Derived`
50   clang_analyzer_dump(d); // expected-warning-re {{&Base{lifetime_extended_object{Derived, d, S{{[0-9]+}}},Composite} }}
51 }
52 
53 void member_access() {
54   int&& x = Composite{}.x;  // extends `Composite`
55   clang_analyzer_dump(x); // expected-warning-re {{&lifetime_extended_object{Composite, x, S{{[0-9]+}}}.x }}
56   int&& y = create<Composite>().y; // extends `Composite`
57   clang_analyzer_dump(y); // expected-warning-re {{&lifetime_extended_object{struct Composite, y, S{{[0-9]+}}}.y }}
58   int&& d = Array<int>{}.front(); // dangles `Array<int>`
59   clang_analyzer_dump(d); // expected-warning-re {{&Element{temp_object{Array<int>, S{{[0-9]+}}}.array,0 S64b,int} }}
60 }
61 
62 void array_subscript() {
63   int&& i = Array<int>{}.array[0]; // extends `Array<int>`
64   clang_analyzer_dump(i); // expected-warning-re {{&Element{lifetime_extended_object{Array<int>, i, S{{[0-9]+}}}.array,0 S64b,int} }}
65   auto&& c = Array<Composite>{}.array[0]; // extends `Array<int>`
66   clang_analyzer_dump(c); // expected-warning-re {{&Element{lifetime_extended_object{Array<Composite>, c, S{{[0-9]+}}}.array,0 S64b,struct Composite} }}
67   auto&& x = Array<Composite>{}.array[0].x; // extends `Array<Composite>`
68   clang_analyzer_dump(x); // expected-warning-re {{&Element{lifetime_extended_object{Array<Composite>, x, S{{[0-9]+}}}.array,0 S64b,struct Composite}.x }}
69 }
70 
71 void ternary(bool cond) {
72   Composite cc;
73   // Value category mismatch of the operands (lvalue and xvalue), ternary produces prvalue
74   auto&& ternaryProducesPRvalue = cond ? Composite{}.x : cc.x; // extends prvalue of 'int', `Composite` in true branch is destroyed
75   clang_analyzer_dump(ternaryProducesPRvalue); // expected-warning-re {{&lifetime_extended_object{int, ternaryProducesPRvalue, S{{[0-9]+}}} }}
76 
77   // Value category agrees (xvalues), lifetime extension is triggered
78   auto&& branchesExtended = cond ? Composite{}.x : static_cast<Composite&&>(cc).x; // extends `Composite` in true branch
79   clang_analyzer_dump(branchesExtended);
80   // expected-warning-re@-1 {{&lifetime_extended_object{Composite, branchesExtended, S{{[0-9]+}}}.x }}
81   // expected-warning@-2 {{&cc.x }}
82 
83   // Object of different types in branches are lifetime extended
84   auto&& extendingDifferentTypes = cond ? Composite{}.x : Array<int>{}.array[0]; // extends `Composite` or `Array<int>`
85   clang_analyzer_dump(extendingDifferentTypes);
86   // expected-warning-re@-1 {{&lifetime_extended_object{Composite, extendingDifferentTypes, S{{[0-9]+}}}.x }}
87   // expected-warning-re@-2 {{&Element{lifetime_extended_object{Array<int>, extendingDifferentTypes, S{{[0-9]+}}}.array,0 S64b,int} }}
88 
89   Composite const& variableAndExtended = cond ? static_cast<Composite&&>(cc) : Array<Composite>{}.array[0]; // extends `Array<Composite>` in false branch
90   clang_analyzer_dump(variableAndExtended);
91   // expected-warning@-1 {{&cc }}
92   // expected-warning-re@-2 {{&Element{lifetime_extended_object{Array<Composite>, variableAndExtended, S{{[0-9]+}}}.array,0 S64b,struct Composite} }}
93 
94   int const& extendAndDangling = cond ? Array<int>{}.array[0] : Array<int>{}.front(); // extends `Array<int>` only in true branch, false branch dangles
95   clang_analyzer_dump(extendAndDangling);
96   // expected-warning-re@-1 {{&Element{lifetime_extended_object{Array<int>, extendAndDangling, S{{[0-9]+}}}.array,0 S64b,int} }}
97   // expected-warning-re@-2 {{&Element{temp_object{Array<int>, S{{[0-9]+}}}.array,0 S64b,int} }}
98 }
99 
100 struct RefAggregate {
101   int const& rx;
102   Composite&& ry = Composite{};
103 };
104 
105 void aggregateWithReferences() {
106   RefAggregate multipleExtensions = {10, Composite{}}; // extends `int` and `Composite`
107   clang_analyzer_dump(multipleExtensions.rx); // expected-warning-re {{&lifetime_extended_object{int, multipleExtensions, S{{[0-9]+}}} }}
108   clang_analyzer_dump(multipleExtensions.ry); // expected-warning-re {{&lifetime_extended_object{Composite, multipleExtensions, S{{[0-9]+}}} }}
109 
110   RefAggregate danglingAndExtended{Array<int>{}.front(), Composite{}}; // extends only `Composite`, `Array<int>` dangles
111   clang_analyzer_dump(danglingAndExtended.rx); // expected-warning-re {{&Element{temp_object{Array<int>, S{{[0-9]+}}}.array,0 S64b,int} }}
112   clang_analyzer_dump(danglingAndExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, danglingAndExtended, S{{[0-9]+}}} }}
113 
114   int i = 10;
115   RefAggregate varAndExtended{i, Composite{}};  // extends `Composite`
116   clang_analyzer_dump(varAndExtended.rx); // expected-warning {{&i }}
117   clang_analyzer_dump(varAndExtended.ry); // expected-warning-re {{&lifetime_extended_object{Composite, varAndExtended, S{{[0-9]+}}} }}
118 
119   auto const& viaReference = RefAggregate{10, Composite{}}; // extends `int`, `Composite`, and `RefAggregate`
120   clang_analyzer_dump(viaReference);    // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
121   clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
122   clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
123 
124   // FIXME: clang currently support extending lifetime of object bound to reference members of aggregates,
125   // that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change.
126   // The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
127   RefAggregate defaultInitExtended{i};
128   clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
129 }
130 
131 void lambda() {
132   auto const& lambdaRef = [capture = create<Composite>()] {};
133   clang_analyzer_dump(lambdaRef); // expected-warning-re {{lifetime_extended_object{class (lambda at {{[^)]+}}), lambdaRef, S{{[0-9]+}}} }}
134 
135   // The capture [&refCapture = create<Composite const>()] { ... } per [expr.prim.lambda.capture] p6 equivalent to:
136   //   auto& refCapture = create<Composite const>(); // Well-formed, deduces auto = Composite const, and performs lifetime extension
137   //   [&refCapture] { ... }
138   // Where 'refCapture' has the same lifetime as the lambda itself.
139   // However, compilers differ: Clang lifetime-extends from C++17, GCC rejects the code, and MSVC dangles
140   // See also CWG2737 (https://cplusplus.github.io/CWG/issues/2737.html)
141   auto const refExtendingCapture = [&refCapture = create<Composite const>()] {
142      clang_analyzer_dump(refCapture);
143      // cpp14-warning-re@-1 {{&temp_object{const struct Composite, S{{[0-9]+}}} }}
144      // cpp17-warning-re@-2 {{&lifetime_extended_object{const struct Composite, refExtendingCapture, S{{[0-9]+}}} }}
145   };
146   refExtendingCapture();
147 }
148 
149 void viaStructuredBinding() {
150   auto&& [x, y] = Composite{}; // extends `Composite` and binds it to unnamed decomposed object
151   clang_analyzer_dump(x); // expected-warning-re {{&lifetime_extended_object{Composite, D{{[0-9]+}}, S{{[0-9]+}}}.x }}
152   clang_analyzer_dump(y); // expected-warning-re {{&lifetime_extended_object{Composite, D{{[0-9]+}}, S{{[0-9]+}}}.y }}
153 
154   auto&& [rx, ry] = RefAggregate{10, Composite{}}; // extends `int`, `Composite`, and `RefAggregate`, and binds them to unnamed decomposed object
155   clang_analyzer_dump(rx); // expected-warning-re {{&lifetime_extended_object{int, D{{[0-9]+}}, S{{[0-9]+}}} }}
156   clang_analyzer_dump(ry); // expected-warning-re {{&lifetime_extended_object{Composite, D{{[0-9]+}}, S{{[0-9]+}}} }}
157 }
158 
159 void propagation(bool cond) {
160   int const& le = Composite{}.x;
161   // May return lifetime-extended region or dangling temporary
162   auto&& oneDangling = select(cond, le, 10); // does not extend lifetime of arguments
163   clang_analyzer_dump(oneDangling);
164   // expected-warning-re@-1 {{&lifetime_extended_object{Composite, le, S{{[0-9]+}}}.x }}
165   // expected-warning-re@-2 {{&temp_object{int, S{{[0-9]+}}} }}
166 
167   // Always dangles
168   auto&& bothDangling = select(cond, 10, 20); // does not extend lifetime of arguments
169   clang_analyzer_dump(bothDangling);
170   // expected-warning-re@-1 {{&temp_object{int, S{{[0-9]+}}} }}
171   // expected-warning-re@-2 {{&temp_object{int, S{{[0-9]+}}} }}
172 }
173