--- /dev/null
+// RUN: %clang_analyze_cc1 -Wno-ignored-reference-qualifiers -analyzer-checker=core,debug.ExprInspection -std=c++17 -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_eval(bool);
+
+namespace std {
+template <typename T>
+struct tuple_size {
+};
+
+template <std::size_t I, typename T>
+struct tuple_element {
+};
+
+// The std::pair in our system header simulator is not tuple-like, so a tuple-like mock is created here
+template <typename T1, typename T2>
+struct mock_pair {
+ T1 first;
+ T2 second;
+};
+template <typename T1, typename T2>
+struct tuple_size<mock_pair<T1, T2>> {
+ static const std::size_t value = 2;
+};
+
+template <typename T1, typename T2>
+struct tuple_element<0, mock_pair<T1, T2>> {
+ using type = T1;
+};
+
+template <typename T1, typename T2>
+struct tuple_element<1, mock_pair<T1, T2>> {
+ using type = T2;
+};
+
+template <std::size_t I, class T>
+using tuple_element_t = typename tuple_element<I, T>::type;
+
+template <std::size_t I, class T1, class T2>
+constexpr std::tuple_element_t<I, std::mock_pair<T1, T2>> &
+get(std::mock_pair<T1, T2> &p) noexcept {
+ if (I == 0)
+ return p.first;
+ else
+ return p.second;
+}
+
+template <std::size_t I, class T1, class T2>
+constexpr const std::tuple_element_t<I, std::mock_pair<T1, T2>> &
+get(const std::mock_pair<T1, T2> &p) noexcept {
+ if (I == 0)
+ return p.first;
+ else
+ return p.second;
+}
+
+template <std::size_t I, class T1, class T2>
+constexpr std::tuple_element_t<I, std::mock_pair<T1, T2>> &&
+get(std::mock_pair<T1, T2> &&p) noexcept {
+
+ if (I == 0)
+ return static_cast<std::tuple_element_t<I, std::mock_pair<T1, T2>> &&>(p.first);
+ else
+ return static_cast<std::tuple_element_t<I, std::mock_pair<T1, T2>> &&>(p.second);
+}
+
+template <std::size_t I, class T1, class T2>
+constexpr const std::tuple_element_t<I, std::mock_pair<T1, T2>> &&
+get(const std::mock_pair<T1, T2> &&p) noexcept {
+ if (I == 0)
+ return static_cast<std::tuple_element_t<I, std::mock_pair<T1, T2>> &&>(p.first);
+ else
+ return static_cast<std::tuple_element_t<I, std::mock_pair<T1, T2>> &&>(p.second);
+}
+
+} // namespace std
+// A utility that generates a tuple-like struct with 2 fields
+// of the same type. The fields are 'first' and 'second'
+#define GENERATE_TUPLE_LIKE_STRUCT(name, element_type) \
+ struct name { \
+ element_type first; \
+ element_type second; \
+ }; \
+ \
+ namespace std { \
+ template <> \
+ struct tuple_size<name> { \
+ static const std::size_t value = 2; \
+ }; \
+ \
+ template <std::size_t I> \
+ struct tuple_element<I, name> { \
+ using type = element_type; \
+ }; \
+ }
+
+void non_user_defined_by_value(void) {
+ std::mock_pair<int, int> p = {1, 2};
+
+ auto [u, v] = p;
+
+ clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 2); // expected-warning{{TRUE}}
+
+ int x = u;
+ u = 10;
+ int y = u;
+
+ clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(y == 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 1); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_by_lref(void) {
+ std::mock_pair<int, int> p = {1, 2};
+
+ auto &[u, v] = p;
+
+ int x = u;
+ u = 10;
+ int y = u;
+
+ clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(y == 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(v == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 2); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 5); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_by_rref(void) {
+ std::mock_pair<int, int> p = {1, 2};
+
+ auto &&[u, v] = p;
+
+ int x = u;
+ u = 10;
+ int y = u;
+
+ clang_analyzer_eval(x == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(u == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(y == 10); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(v == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 2); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 5); // expected-warning{{TRUE}}
+}
+
+GENERATE_TUPLE_LIKE_STRUCT(Test, int);
+
+template <std::size_t I>
+int get(Test t) {
+ if (I == 0) {
+ t.second = 10;
+ return t.first;
+ } else {
+ t.first = 20;
+ return t.second;
+ }
+}
+
+void user_defined_get_val_by_val(void) {
+ Test p{1, 2};
+ auto [u, v] = p;
+
+ clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+
+ u = 8;
+
+ int x = u;
+
+ clang_analyzer_eval(x == 8); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 2); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 5); // expected-warning{{TRUE}}
+}
+
+GENERATE_TUPLE_LIKE_STRUCT(Test2, int);
+
+template <std::size_t I>
+int get(Test2 &t) {
+ if (I == 0) {
+ t.second = 10;
+ return t.first;
+ } else {
+ t.first = 20;
+ return t.second;
+ }
+}
+
+void user_defined_get_val_by_lref(void) {
+ Test2 p{1, 2};
+
+ auto &[u, v] = p;
+
+ clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 10); // expected-warning{{TRUE}}
+
+ u = 8;
+
+ int x = u;
+
+ clang_analyzer_eval(x == 8); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first == 20); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 10); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 5); // expected-warning{{TRUE}}
+}
+
+void user_defined_get_val_by_rref(void) {
+ Test2 p{1, 2};
+
+ auto &&[u, v] = p;
+
+ clang_analyzer_eval(u == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 10); // expected-warning{{TRUE}}
+
+ u = 8;
+
+ int x = u;
+
+ clang_analyzer_eval(x == 8); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(v == 10); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first == 20); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 10); // expected-warning{{TRUE}}
+
+ p.first = 5;
+
+ clang_analyzer_eval(u == 8); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first == 5); // expected-warning{{TRUE}}
+}
+
+struct MixedTest {
+ int x;
+ char &&y;
+ int &z;
+};
+
+namespace std {
+template <>
+struct tuple_size<MixedTest> {
+ static const std::size_t value = 3;
+};
+
+template <>
+struct tuple_element<0, MixedTest> {
+ using type = int;
+};
+
+template <>
+struct tuple_element<1, MixedTest> {
+ using type = char &&;
+};
+
+template <>
+struct tuple_element<2, MixedTest> {
+ using type = int &;
+};
+
+template <std::size_t I, typename T>
+using tuple_element_t = typename tuple_element<I, T>::type;
+
+} // namespace std
+
+template <std::size_t I>
+const std::tuple_element_t<I, MixedTest> &get(const MixedTest &t) {}
+
+template <>
+const std::tuple_element_t<0, MixedTest> &get<0>(const MixedTest &t) {
+ return t.x;
+}
+
+template <>
+const std::tuple_element_t<1, MixedTest> &get<1>(const MixedTest &t) {
+ return t.y;
+}
+
+template <>
+const std::tuple_element_t<2, MixedTest> &get<2>(const MixedTest &t) {
+ return t.z;
+}
+
+void mixed_type_cref(void) {
+ int x = 1;
+ char y = 2;
+ int z = 3;
+
+ MixedTest m{x, std::move(y), z};
+ const auto &[a, b, c] = m;
+
+ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+ clang_analyzer_eval(c == 3); // expected-warning{{TRUE}}
+}
+
+template <std::size_t I>
+std::tuple_element_t<I, MixedTest> &get(MixedTest &t) {}
+
+template <>
+std::tuple_element_t<0, MixedTest> &get<0>(MixedTest &t) {
+ return t.x;
+}
+
+template <>
+std::tuple_element_t<1, MixedTest> &get<1>(MixedTest &t) {
+ return t.y;
+}
+
+template <>
+std::tuple_element_t<2, MixedTest> &get<2>(MixedTest &t) {
+ return t.z;
+}
+
+void mixed_type_lref(void) {
+ int x = 1;
+ char y = 2;
+ int z = 3;
+
+ MixedTest m{x, std::move(y), z};
+ auto &[a, b, c] = m;
+
+ a = 4;
+ b = 5;
+ c = 6;
+
+ clang_analyzer_eval(get<0>(m) == 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<1>(m) == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<2>(m) == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(get<0>(m) == 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<1>(m) == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<2>(m) == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(z == 6); // expected-warning{{TRUE}}
+}
+
+void mixed_type_rref(void) {
+ int x = 1;
+ char y = 2;
+ int z = 3;
+
+ MixedTest m{x, std::move(y), z};
+ auto &&[a, b, c] = m;
+
+ a = 4;
+ b = 5;
+ c = 6;
+
+ clang_analyzer_eval(get<0>(m) == 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<1>(m) == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<2>(m) == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(get<0>(m) == 4); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<1>(m) == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(get<2>(m) == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(z == 6); // expected-warning{{TRUE}}
+}
+
+void ref_val(void) {
+ int i = 1, j = 2;
+ std::mock_pair<int &, int &> p{i, j};
+
+ auto [a, b] = p;
+ clang_analyzer_eval(a == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b == 2); // expected-warning{{TRUE}}
+
+ a = 3;
+ b = 4;
+
+ clang_analyzer_eval(p.first == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(a == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b == 4); // expected-warning{{TRUE}}
+}
+
+struct Small_Non_POD {
+ int i;
+ int j;
+};
+
+void non_user_defined_small_non_pod_by_value(void) {
+ std::mock_pair<Small_Non_POD, Small_Non_POD> p{{1, 2}, {1, 2}};
+
+ auto [a, b] = p;
+
+ clang_analyzer_eval(a.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 2); // expected-warning{{TRUE}}
+
+ a.i = 3;
+ a.j = 4;
+
+ b.i = 5;
+ b.j = 6;
+
+ clang_analyzer_eval(a.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.second.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second.j == 2); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_small_non_pod_by_lref(void) {
+ std::mock_pair<Small_Non_POD, Small_Non_POD> p{{1, 2}, {1, 2}};
+
+ auto &[a, b] = p;
+
+ clang_analyzer_eval(a.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 2); // expected-warning{{TRUE}}
+
+ a.i = 3;
+ a.j = 4;
+
+ b.i = 5;
+ b.j = 6;
+
+ clang_analyzer_eval(a.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first.j == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.second.i == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second.j == 6); // expected-warning{{TRUE}}
+}
+
+void non_user_defined_small_non_pod_by_rref(void) {
+ std::mock_pair<Small_Non_POD, Small_Non_POD> p{{1, 2}, {1, 2}};
+
+ auto &&[a, b] = p;
+
+ clang_analyzer_eval(a.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 2); // expected-warning{{TRUE}}
+
+ a.i = 3;
+ a.j = 4;
+
+ b.i = 5;
+ b.j = 6;
+
+ clang_analyzer_eval(a.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 6); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.first.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.first.j == 4); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(p.second.i == 5); // expected-warning{{TRUE}}
+ clang_analyzer_eval(p.second.j == 6); // expected-warning{{TRUE}}
+}
+
+GENERATE_TUPLE_LIKE_STRUCT(Uninit, int);
+template <std::size_t I>
+int &get(Uninit &&t) {
+ if (I == 0) {
+ return t.first;
+ } else {
+ return t.second;
+ }
+}
+
+void uninit_a(void) {
+ Uninit u;
+
+ auto [a, b] = u;
+
+ int x = a; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+void uninit_b(void) {
+ Uninit u;
+
+ auto [a, b] = u;
+
+ int x = b; // expected-warning{{Assigned value is garbage or undefined}}
+}
+
+GENERATE_TUPLE_LIKE_STRUCT(UninitCall, int);
+template <std::size_t I>
+int get(UninitCall t) {
+ if (I == 0) {
+ return t.first;
+ } else {
+ return t.second;
+ }
+}
+
+void uninit_call(void) {
+ UninitCall u;
+
+ auto [a, b] = u;
+
+ int x = a;
+ // expected-warning@543{{Undefined or garbage value returned to caller}}
+}
+
+void syntax_2() {
+ std::mock_pair<Small_Non_POD, Small_Non_POD> p{{1, 2}, {3, 4}};
+
+ auto [a, b]{p};
+
+ clang_analyzer_eval(a.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 4); // expected-warning{{TRUE}}
+}
+
+void syntax_3() {
+ std::mock_pair<Small_Non_POD, Small_Non_POD> p{{1, 2}, {3, 4}};
+
+ auto [a, b](p);
+
+ clang_analyzer_eval(a.i == 1); // expected-warning{{TRUE}}
+ clang_analyzer_eval(a.j == 2); // expected-warning{{TRUE}}
+
+ clang_analyzer_eval(b.i == 3); // expected-warning{{TRUE}}
+ clang_analyzer_eval(b.j == 4); // expected-warning{{TRUE}}
+}