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