forked from OSchip/llvm-project
[analyzer] Structured binding to tuple-like types
Introducing support for creating structured binding to tuple-like types. Differential Revision: https://reviews.llvm.org/D128837
This commit is contained in:
parent
290c4bc7be
commit
a618d5e0dd
|
@ -2932,6 +2932,20 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
|
|||
}
|
||||
}
|
||||
|
||||
// If we bind to a tuple-like type, we iterate over the HoldingVars, and
|
||||
// create a DeclStmt for each of them.
|
||||
if (const auto *DD = dyn_cast<DecompositionDecl>(VD)) {
|
||||
for (auto BD : llvm::reverse(DD->bindings())) {
|
||||
if (auto *VD = BD->getHoldingVar()) {
|
||||
DeclGroupRef DG(VD);
|
||||
DeclStmt *DSNew =
|
||||
new (Context) DeclStmt(DG, VD->getLocation(), GetEndLoc(VD));
|
||||
cfg->addSyntheticDeclStmt(DSNew, DS);
|
||||
Block = VisitDeclSubExpr(DSNew);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoCreateBlock();
|
||||
appendStmt(Block, DS);
|
||||
|
||||
|
|
|
@ -72,6 +72,11 @@ bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const {
|
|||
bool alive = false;
|
||||
for (const BindingDecl *BD : DD->bindings())
|
||||
alive |= liveBindings.contains(BD);
|
||||
|
||||
// Note: the only known case this condition is necessary, is when a bindig
|
||||
// to a tuple-like structure is created. The HoldingVar initializers have a
|
||||
// DeclRefExpr to the DecompositionDecl.
|
||||
alive |= liveDecls.contains(DD);
|
||||
return alive;
|
||||
}
|
||||
return liveDecls.contains(D);
|
||||
|
@ -343,8 +348,12 @@ void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) {
|
|||
|
||||
if (const BindingDecl* BD = dyn_cast<BindingDecl>(D)) {
|
||||
Killed = !BD->getType()->isReferenceType();
|
||||
if (Killed)
|
||||
if (Killed) {
|
||||
if (const auto *HV = BD->getHoldingVar())
|
||||
val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV);
|
||||
|
||||
val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD);
|
||||
}
|
||||
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
|
||||
Killed = writeShouldKill(VD);
|
||||
if (Killed)
|
||||
|
@ -371,8 +380,12 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) {
|
|||
const Decl* D = DR->getDecl();
|
||||
bool InAssignment = LV.inAssignment[DR];
|
||||
if (const auto *BD = dyn_cast<BindingDecl>(D)) {
|
||||
if (!InAssignment)
|
||||
if (!InAssignment) {
|
||||
if (const auto *HV = BD->getHoldingVar())
|
||||
val.liveDecls = LV.DSetFact.add(val.liveDecls, HV);
|
||||
|
||||
val.liveBindings = LV.BSetFact.add(val.liveBindings, BD);
|
||||
}
|
||||
} else if (const auto *VD = dyn_cast<VarDecl>(D)) {
|
||||
if (!InAssignment && !isAlwaysAlive(VD))
|
||||
val.liveDecls = LV.DSetFact.add(val.liveDecls, VD);
|
||||
|
@ -382,8 +395,16 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) {
|
|||
void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {
|
||||
for (const auto *DI : DS->decls()) {
|
||||
if (const auto *DD = dyn_cast<DecompositionDecl>(DI)) {
|
||||
for (const auto *BD : DD->bindings())
|
||||
for (const auto *BD : DD->bindings()) {
|
||||
if (const auto *HV = BD->getHoldingVar())
|
||||
val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV);
|
||||
|
||||
val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD);
|
||||
}
|
||||
|
||||
// When a bindig to a tuple-like structure is created, the HoldingVar
|
||||
// initializers have a DeclRefExpr to the DecompositionDecl.
|
||||
val.liveDecls = LV.DSetFact.remove(val.liveDecls, DD);
|
||||
} else if (const auto *VD = dyn_cast<VarDecl>(DI)) {
|
||||
if (!isAlwaysAlive(VD))
|
||||
val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD);
|
||||
|
|
|
@ -2788,7 +2788,10 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
|||
|
||||
SVal Base = state->getLValue(DD, LCtx);
|
||||
if (DD->getType()->isReferenceType()) {
|
||||
Base = state->getSVal(Base.getAsRegion());
|
||||
if (const MemRegion *R = Base.getAsRegion())
|
||||
Base = state->getSVal(R);
|
||||
else
|
||||
Base = UnknownVal();
|
||||
}
|
||||
|
||||
SVal V = UnknownVal();
|
||||
|
@ -2809,15 +2812,27 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
|
|||
|
||||
V = state->getLValue(BD->getType(), Idx, Base);
|
||||
}
|
||||
// Handle binding to tuple-like strcutures
|
||||
else if (BD->getHoldingVar()) {
|
||||
// FIXME: handle tuples
|
||||
return;
|
||||
// Handle binding to tuple-like structures
|
||||
else if (const auto *HV = BD->getHoldingVar()) {
|
||||
V = state->getLValue(HV, LCtx);
|
||||
|
||||
if (HV->getType()->isReferenceType()) {
|
||||
if (const MemRegion *R = V.getAsRegion())
|
||||
V = state->getSVal(R);
|
||||
else
|
||||
V = UnknownVal();
|
||||
}
|
||||
} else
|
||||
llvm_unreachable("An unknown case of structured binding encountered!");
|
||||
|
||||
if (BD->getType()->isReferenceType())
|
||||
V = state->getSVal(V.getAsRegion());
|
||||
// In case of tuple-like types the references are already handled, so we
|
||||
// don't want to handle them again.
|
||||
if (BD->getType()->isReferenceType() && !BD->getHoldingVar()) {
|
||||
if (const MemRegion *R = V.getAsRegion())
|
||||
V = state->getSVal(R);
|
||||
else
|
||||
V = UnknownVal();
|
||||
}
|
||||
|
||||
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
|
||||
ProgramPoint::PostLValueKind);
|
||||
|
|
|
@ -115,7 +115,10 @@ void no_warning_on_tuple_types_copy(Mytuple t) {
|
|||
Mytuple getMytuple();
|
||||
|
||||
void deconstruct_tuple_types_warning() {
|
||||
auto [a, b] = getMytuple(); // expected-warning{{Value stored to '[a, b]' during its initialization is never read}}
|
||||
// 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() {
|
||||
|
|
|
@ -0,0 +1,580 @@
|
|||
// 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}}
|
||||
}
|
Loading…
Reference in New Issue