mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-16 15:16:35 +00:00

A clang user pointed out that messages for the static analyzer undefined assignment checker use the term ‘garbage’, which might have a negative connotation to some users. This change updates the messages to use the term ‘uninitialized’. This is the usual reason why a value is undefined in the static analyzer and describes the logical error that a programmer should take action to fix. Out-of-bounds reads can also produce undefined values in the static analyzer. The right long-term design is to have to the array bounds checker cover out-of-bounds reads, so we do not cover that case in the updated messages. The recent improvements to the array bounds checker make it a candidate to add to the core set of checkers. rdar://133418644
581 lines
15 KiB
C++
581 lines
15 KiB
C++
// 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 uninitialized}}
|
|
}
|
|
|
|
void uninit_b(void) {
|
|
Uninit u;
|
|
|
|
auto [a, b] = u;
|
|
|
|
int x = b; // expected-warning{{Assigned value is uninitialized}}
|
|
}
|
|
|
|
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}}
|
|
}
|