|  | // RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=core,deadcode -verify %s | 
|  |  | 
|  | typedef unsigned long size_t; | 
|  |  | 
|  | // Machinery required for custom structured bindings decomposition. | 
|  | namespace std { | 
|  | template <class T> class tuple_size; | 
|  | template <class T> | 
|  | constexpr size_t tuple_size_v = tuple_size<T>::value; | 
|  | template <size_t I, class T> class tuple_element; | 
|  |  | 
|  | template<class T, T v> | 
|  | struct integral_constant { | 
|  | static constexpr T value = v; | 
|  | typedef T value_type; | 
|  | typedef integral_constant type; | 
|  | constexpr operator value_type() const noexcept { return value; } | 
|  | }; | 
|  | } | 
|  |  | 
|  | struct S { | 
|  | int a; | 
|  | double b; | 
|  | S(int a, double b) : a(a), b(b) {}; | 
|  | }; | 
|  |  | 
|  | S GetNumbers(); | 
|  |  | 
|  | int used_binding() { | 
|  | const auto [a, b] = GetNumbers(); // no-warning | 
|  | return a + b; | 
|  | } | 
|  |  | 
|  | void no_warning_on_copy(S s) { | 
|  | // Copy constructor might have side effects. | 
|  | const auto [a, b] = s; // no-warning | 
|  | } | 
|  |  | 
|  |  | 
|  | int unused_binding_ignored() { | 
|  | const auto [a, b] = GetNumbers(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}} | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int unused_binding_liveness_required() { | 
|  | auto [a2, b2] = GetNumbers(); // expected-warning{{Value stored to '[a2, b2]' during its initialization is never read}} | 
|  | a2 = 10; | 
|  | b2 = 20; | 
|  | return a2 + b2; | 
|  | } | 
|  |  | 
|  | int kill_one_binding() { | 
|  | auto [a, b] = GetNumbers(); // no-warning | 
|  | a = 100; | 
|  | return a + b; | 
|  |  | 
|  | } | 
|  |  | 
|  | int kill_one_binding2() { | 
|  | auto [a, b] = GetNumbers(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}} | 
|  | a = 100; | 
|  | return a; | 
|  | } | 
|  |  | 
|  | void use_const_reference_bindings() { | 
|  | const auto &[a, b] = GetNumbers(); // no-warning | 
|  | } | 
|  |  | 
|  | void use_reference_bindings() { | 
|  | S s(0, 0); | 
|  | auto &[a, b] = s; // no-warning | 
|  | a = 200; | 
|  | } | 
|  |  | 
|  | int read_through_pointer() { | 
|  | auto [a, b] = GetNumbers(); // no-warning | 
|  | int *z = &a; | 
|  | return *z; | 
|  | } | 
|  |  | 
|  | auto [globalA, globalB] = GetNumbers(); // no-warning, globals | 
|  | auto [globalC, globalD] = GetNumbers(); // no-warning, globals | 
|  |  | 
|  | void use_globals() { | 
|  | globalA = 300; // no-warning | 
|  | globalB = 200; | 
|  | } | 
|  |  | 
|  | struct Mytuple { | 
|  | int a; | 
|  | int b; | 
|  |  | 
|  | template <size_t N> | 
|  | int get() const { | 
|  | if      constexpr (N == 0) return a; | 
|  | else if constexpr (N == 1) return b; | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace std { | 
|  | template<> | 
|  | struct tuple_size<Mytuple> | 
|  | : std::integral_constant<size_t, 2> {}; | 
|  |  | 
|  | template<size_t N> | 
|  | struct tuple_element<N, Mytuple> { | 
|  | using type = int; | 
|  | }; | 
|  | } | 
|  |  | 
|  | void no_warning_on_tuple_types_copy(Mytuple t) { | 
|  | auto [a, b] = t; // no-warning | 
|  | } | 
|  |  | 
|  | Mytuple getMytuple(); | 
|  |  | 
|  | void deconstruct_tuple_types_warning() { | 
|  | // The initializers reference the decomposed region, so the warning is not reported | 
|  | // FIXME: ideally we want to ignore that the initializers reference the decomposed region, and report the warning, | 
|  | // though the first step towards that is to handle DeadCode if the initializer is CXXConstructExpr. | 
|  | auto [a, b] = getMytuple(); // no-warning | 
|  | } | 
|  |  | 
|  | int deconstruct_tuple_types_no_warning() { | 
|  | auto [a, b] = getMytuple(); // no-warning | 
|  | return a + b; | 
|  | } |