1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DDUMMY -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DV0 -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DV1 -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DV2 -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DV3 -verify %s
// RUN: %clang_cc1 -fsyntax-only -Wself-assign-field -DV4 -verify %s
#ifdef DUMMY
struct S {};
#else
struct S {
#if defined(V0)
S() = default;
#elif defined(V1)
S &operator=(const S &) = default;
#elif defined(V2)
S &operator=(S &) = default;
#elif defined(V3)
S &operator=(const S &);
#elif defined(V4)
S &operator=(S &);
#else
#error Define something!
#endif
S &operator*=(const S &);
S &operator/=(const S &);
S &operator%=(const S &);
S &operator+=(const S &);
S &operator-=(const S &);
S &operator<<=(const S &);
S &operator>>=(const S &);
S &operator&=(const S &);
S &operator|=(const S &);
S &operator^=(const S &);
S &operator=(const volatile S &) volatile;
};
#endif
struct C {
S a;
S b;
void f() {
a = a; // expected-warning {{assigning field to itself}}
b = b; // expected-warning {{assigning field to itself}}
a = b;
this->a = a; // expected-warning {{assigning field to itself}}
this->b = b; // expected-warning {{assigning field to itself}}
a = this->a; // expected-warning {{assigning field to itself}}
b = this->b; // expected-warning {{assigning field to itself}}
this->a = this->a; // expected-warning {{assigning field to itself}}
this->b = this->b; // expected-warning {{assigning field to itself}}
a = b;
a = this->b;
this->a = b;
this->a = this->b;
#ifndef DUMMY
a *= a;
a /= a; // expected-warning {{assigning field to itself}}
a %= a; // expected-warning {{assigning field to itself}}
a += a;
a -= a; // expected-warning {{assigning field to itself}}
a <<= a;
a >>= a;
a &= a; // expected-warning {{assigning field to itself}}
a |= a; // expected-warning {{assigning field to itself}}
a ^= a; // expected-warning {{assigning field to itself}}
#endif
}
void false_positives() {
#define OP =
#define LHS a
#define RHS a
// These shouldn't warn due to the use of the preprocessor.
a OP a;
LHS = a;
a = RHS;
LHS OP RHS;
#undef OP
#undef LHS
#undef RHS
// Ways to silence the warning.
a = *&a;
a = (S &)a;
a = static_cast<decltype(a) &>(a);
}
#ifndef DUMMY
volatile S vol_a;
void vol_test() {
// Volatile stores aren't side-effect free.
vol_a = vol_a;
volatile S &vol_a_ref = vol_a;
vol_a_ref = vol_a_ref;
}
#endif
};
// Do not diagnose self-assigment in an unevaluated context
struct SNoExcept {
SNoExcept() = default;
SNoExcept &operator=(const SNoExcept &) noexcept;
};
struct false_positives_unevaluated_ctx_class {
SNoExcept a;
void false_positives_unevaluated_ctx(SNoExcept a) noexcept(noexcept(a = a)) {
decltype(a = a) b = a;
static_assert(noexcept(a = a), "");
static_assert(sizeof(a = a), "");
}
};
template <typename T>
struct TemplateClass {
T var;
void f() {
var = var; // expected-warning {{assigning field to itself}}
}
};
void instantiate() {
{
TemplateClass<int> c;
c.f();
}
{
TemplateClass<S> c;
c.f();
}
}
// It may make sense not to warn on the rest of the tests.
// It may be a valid use-case to self-assign to tell the compiler that
// it is ok to vectorize the store.
void f0(C *s, C *t) {
s->a = s->a;
t->a = s->a;
}
void f1(C &s, C &t) {
s.a = s.a;
t.a = s.a;
}
struct T {
C *s;
};
void f2(T *t, T *t2) {
t->s->a = t->s->a;
t2->s->a = t->s->a;
}
void f3(T &t, T &t2) {
t.s->a = t.s->a;
t2.s->a = t.s->a;
}
|