llvm/clang/test/Analysis/uninit-structured-binding-tuple.cpp

// 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}}
}