
Fixed: #132931 const array is immutable in C/C++ language design, we don't need to check constness for it.
1018 lines
31 KiB
C++
1018 lines
31 KiB
C++
// RUN: %check_clang_tidy %s misc-const-correctness %t -- \
|
|
// RUN: -config="{CheckOptions: {\
|
|
// RUN: misc-const-correctness.TransformValues: true, \
|
|
// RUN: misc-const-correctness.WarnPointersAsValues: false, \
|
|
// RUN: misc-const-correctness.WarnPointersAsPointers: false, \
|
|
// RUN: misc-const-correctness.TransformPointersAsValues: false \
|
|
// RUN: }}" -- -fno-delayed-template-parsing -fexceptions
|
|
|
|
// ------- Provide test samples for primitive builtins ---------
|
|
// - every 'p_*' variable is a 'potential_const_*' variable
|
|
// - every 'np_*' variable is a 'non_potential_const_*' variable
|
|
|
|
bool global;
|
|
char np_global = 0; // globals can't be known to be const
|
|
|
|
// FIXME: 'static' globals are not matched right now. They could be analyzed but aren't right now.
|
|
static int p_static_global = 42;
|
|
|
|
namespace foo {
|
|
int scoped;
|
|
float np_scoped = 1; // namespace variables are like globals
|
|
} // namespace foo
|
|
|
|
// FIXME: Similary to 'static' globals, anonymous globals are not matched and analyzed.
|
|
namespace {
|
|
int np_anonymous_global;
|
|
int p_anonymous_global = 43;
|
|
} // namespace
|
|
|
|
// Lambdas should be ignored, because they do not follow the normal variable
|
|
// semantic (e.g. the type is only known to the compiler).
|
|
void lambdas() {
|
|
auto Lambda = [](int i) { return i < 0; };
|
|
}
|
|
|
|
void some_function(double, wchar_t);
|
|
|
|
void some_function(double np_arg0, wchar_t np_arg1) {
|
|
int p_local0 = 2;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
|
|
int np_local0;
|
|
const int np_local1 = 42;
|
|
|
|
unsigned int np_local2 = 3;
|
|
np_local2 <<= 4;
|
|
|
|
int np_local3 = 4;
|
|
++np_local3;
|
|
int np_local4 = 4;
|
|
np_local4++;
|
|
|
|
int np_local5 = 4;
|
|
--np_local5;
|
|
int np_local6 = 4;
|
|
np_local6--;
|
|
}
|
|
|
|
int function_try_block() try {
|
|
int p_local0 = 0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
return p_local0;
|
|
} catch (...) {
|
|
return 0;
|
|
}
|
|
|
|
void nested_scopes() {
|
|
int p_local0 = 2;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
int np_local0 = 42;
|
|
|
|
{
|
|
int p_local1 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:5: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
np_local0 *= 2;
|
|
}
|
|
}
|
|
|
|
void ignore_reference_to_pointers() {
|
|
int *np_local0 = nullptr;
|
|
int *&np_local1 = np_local0;
|
|
}
|
|
|
|
void some_lambda_environment_capture_all_by_reference(double np_arg0) {
|
|
int np_local0 = 0;
|
|
int p_local0 = 1;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
|
|
int np_local2;
|
|
const int np_local3 = 2;
|
|
|
|
// Capturing all variables by reference prohibits making them const.
|
|
[&]() { ++np_local0; };
|
|
|
|
int p_local1 = 0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
}
|
|
|
|
void some_lambda_environment_capture_all_by_value(double np_arg0) {
|
|
int np_local0 = 0;
|
|
int p_local0 = 1;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
|
|
int np_local1;
|
|
const int np_local2 = 2;
|
|
|
|
// Capturing by value has no influence on them.
|
|
[=]() { (void)p_local0; };
|
|
|
|
np_local0 += 10;
|
|
}
|
|
|
|
void function_inout_pointer(int *inout);
|
|
void function_in_pointer(const int *in);
|
|
|
|
void some_pointer_taking(int *out) {
|
|
int np_local0 = 42;
|
|
const int *const p0_np_local0 = &np_local0;
|
|
int *const p1_np_local0 = &np_local0;
|
|
|
|
int np_local1 = 42;
|
|
const int *const p0_np_local1 = &np_local1;
|
|
int *const p1_np_local1 = &np_local1;
|
|
*p1_np_local0 = 43;
|
|
|
|
int np_local2 = 42;
|
|
function_inout_pointer(&np_local2);
|
|
|
|
// Prevents const.
|
|
int np_local3 = 42;
|
|
out = &np_local3; // This returns and invalid address, its just about the AST
|
|
|
|
int p_local1 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
const int *const p0_p_local1 = &p_local1;
|
|
|
|
int p_local2 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local2
|
|
function_in_pointer(&p_local2);
|
|
}
|
|
|
|
void function_inout_ref(int &inout);
|
|
void function_in_ref(const int &in);
|
|
|
|
void some_reference_taking() {
|
|
int np_local0 = 42;
|
|
const int &r0_np_local0 = np_local0;
|
|
int &r1_np_local0 = np_local0;
|
|
r1_np_local0 = 43;
|
|
const int &r2_np_local0 = r1_np_local0;
|
|
|
|
int np_local1 = 42;
|
|
function_inout_ref(np_local1);
|
|
|
|
int p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
const int &r0_p_local0 = p_local0;
|
|
|
|
int p_local1 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
function_in_ref(p_local1);
|
|
}
|
|
|
|
double *non_const_pointer_return() {
|
|
double p_local0 = 0.0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local0
|
|
double np_local0 = 24.4;
|
|
|
|
return &np_local0;
|
|
}
|
|
|
|
const double *const_pointer_return() {
|
|
double p_local0 = 0.0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local0
|
|
double p_local1 = 24.4;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local1
|
|
return &p_local1;
|
|
}
|
|
|
|
// Also see const-correctness-values.cpp-before-cxx23.cpp for `non_const_ref_return` and `return_non_const_pointer_ref`
|
|
const double &const_ref_return() {
|
|
double p_local0 = 0.0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local0
|
|
double p_local1 = 24.4;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local1
|
|
return p_local1;
|
|
}
|
|
|
|
void overloaded_arguments(const int &in);
|
|
void overloaded_arguments(int &inout);
|
|
void overloaded_arguments(const int *in);
|
|
void overloaded_arguments(int *inout);
|
|
|
|
void function_calling() {
|
|
int np_local0 = 42;
|
|
overloaded_arguments(np_local0);
|
|
|
|
const int np_local1 = 42;
|
|
overloaded_arguments(np_local1);
|
|
|
|
int np_local2 = 42;
|
|
overloaded_arguments(&np_local2);
|
|
|
|
const int np_local3 = 42;
|
|
overloaded_arguments(&np_local3);
|
|
}
|
|
|
|
template <typename T>
|
|
void define_locals(T np_arg0, T &np_arg1, int np_arg2) {
|
|
T np_local0 = 0;
|
|
np_local0 += np_arg0 * np_arg1;
|
|
|
|
T np_local1 = 42;
|
|
np_local0 += np_local1;
|
|
|
|
// Used as argument to an overloaded function with const and non-const.
|
|
T np_local2 = 42;
|
|
overloaded_arguments(np_local2);
|
|
|
|
int np_local4 = 42;
|
|
// non-template values are ok still.
|
|
int p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
np_local4 += p_local0;
|
|
}
|
|
|
|
template <typename T>
|
|
void more_template_locals() {
|
|
const T np_local0 = {};
|
|
auto np_local1 = T{};
|
|
T &np_local2 = np_local1;
|
|
T *np_local_ptr = &np_local1;
|
|
|
|
const auto np_local3 = T{};
|
|
// FIXME: False positive, the reference points to a template type and needs
|
|
// to be excluded from analysis, but somehow isn't (matchers don't work)
|
|
auto &np_local4 = np_local3;
|
|
|
|
const auto *np_local5 = &np_local3;
|
|
auto *np_local6 = &np_local1;
|
|
|
|
using TypedefToTemplate = T;
|
|
TypedefToTemplate np_local7{};
|
|
// FIXME: False positive, the reference points to a template type and needs
|
|
// to be excluded from analysis, but somehow isn't (matchers don't work)
|
|
// auto &np_local8 = np_local7;
|
|
const auto &np_local9 = np_local7;
|
|
auto np_local10 = np_local7;
|
|
auto *np_local11 = &np_local10;
|
|
const auto *const np_local12 = &np_local10;
|
|
|
|
// FIXME: False positive, the reference points to a template type and needs
|
|
// to be excluded from analysis, but somehow isn't (matchers don't work)
|
|
// TypedefToTemplate &np_local13 = np_local7;
|
|
TypedefToTemplate *np_local14 = &np_local7;
|
|
}
|
|
|
|
void template_instantiation() {
|
|
const int np_local0 = 42;
|
|
int np_local1 = 42;
|
|
|
|
define_locals(np_local0, np_local1, np_local0);
|
|
define_locals(np_local1, np_local1, np_local1);
|
|
more_template_locals<int>();
|
|
}
|
|
|
|
struct ConstNonConstClass {
|
|
ConstNonConstClass();
|
|
ConstNonConstClass(double &np_local0);
|
|
double nonConstMethod() { return 0; }
|
|
double constMethod() const { return 0; }
|
|
double modifyingMethod(double &np_arg0) const;
|
|
|
|
double NonConstMember;
|
|
const double ConstMember;
|
|
|
|
double &NonConstMemberRef;
|
|
const double &ConstMemberRef;
|
|
|
|
double *NonConstMemberPtr;
|
|
const double *ConstMemberPtr;
|
|
};
|
|
|
|
void direct_class_access() {
|
|
ConstNonConstClass np_local0;
|
|
|
|
np_local0.constMethod();
|
|
np_local0.nonConstMethod();
|
|
|
|
ConstNonConstClass p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local0
|
|
p_local0.constMethod();
|
|
|
|
ConstNonConstClass p_local1;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'ConstNonConstClass' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local1
|
|
double np_local1;
|
|
p_local1.modifyingMethod(np_local1);
|
|
|
|
double np_local2;
|
|
ConstNonConstClass p_local2(np_local2);
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'ConstNonConstClass' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local2(np_local2)
|
|
|
|
ConstNonConstClass np_local3;
|
|
np_local3.NonConstMember = 42.;
|
|
|
|
ConstNonConstClass np_local4;
|
|
np_local4.NonConstMemberRef = 42.;
|
|
|
|
ConstNonConstClass p_local3;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'ConstNonConstClass' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local3
|
|
const double val0 = p_local3.NonConstMember;
|
|
const double val1 = p_local3.NonConstMemberRef;
|
|
const double val2 = *p_local3.NonConstMemberPtr;
|
|
|
|
ConstNonConstClass p_local4;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local4' of type 'ConstNonConstClass' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local4
|
|
*np_local4.NonConstMemberPtr = 42.;
|
|
}
|
|
|
|
void class_access_array() {
|
|
ConstNonConstClass np_local0[2];
|
|
np_local0[0].constMethod();
|
|
np_local0[1].constMethod();
|
|
np_local0[1].nonConstMethod();
|
|
|
|
ConstNonConstClass p_local0[2];
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'ConstNonConstClass[2]' can be declared 'const'
|
|
// CHECK-FIXES: ConstNonConstClass const p_local0[2]
|
|
p_local0[0].constMethod();
|
|
np_local0[1].constMethod();
|
|
}
|
|
|
|
struct OperatorsAsConstAsPossible {
|
|
OperatorsAsConstAsPossible &operator+=(const OperatorsAsConstAsPossible &rhs);
|
|
OperatorsAsConstAsPossible operator+(const OperatorsAsConstAsPossible &rhs) const;
|
|
};
|
|
|
|
struct NonConstOperators {
|
|
};
|
|
NonConstOperators operator+(NonConstOperators &lhs, NonConstOperators &rhs);
|
|
NonConstOperators operator-(NonConstOperators lhs, NonConstOperators rhs);
|
|
|
|
void internal_operator_calls() {
|
|
OperatorsAsConstAsPossible np_local0;
|
|
OperatorsAsConstAsPossible np_local1;
|
|
OperatorsAsConstAsPossible p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'OperatorsAsConstAsPossible' can be declared 'const'
|
|
// CHECK-FIXES: OperatorsAsConstAsPossible const p_local0
|
|
OperatorsAsConstAsPossible p_local1;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'OperatorsAsConstAsPossible' can be declared 'const'
|
|
// CHECK-FIXES: OperatorsAsConstAsPossible const p_local1
|
|
|
|
np_local0 += p_local0;
|
|
np_local1 = p_local0 + p_local1;
|
|
|
|
NonConstOperators np_local2;
|
|
NonConstOperators np_local3;
|
|
NonConstOperators np_local4;
|
|
|
|
np_local2 = np_local3 + np_local4;
|
|
|
|
NonConstOperators p_local2;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'NonConstOperators' can be declared 'const'
|
|
// CHECK-FIXES: NonConstOperators const p_local2
|
|
NonConstOperators p_local3 = p_local2 - p_local2;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'NonConstOperators' can be declared 'const'
|
|
// CHECK-FIXES: NonConstOperators const p_local3
|
|
}
|
|
|
|
struct MyVector {
|
|
double *begin();
|
|
const double *begin() const;
|
|
|
|
double *end();
|
|
const double *end() const;
|
|
|
|
double &operator[](int index);
|
|
double operator[](int index) const;
|
|
|
|
double values[100];
|
|
};
|
|
|
|
void vector_usage() {
|
|
double np_local0[10];
|
|
np_local0[5] = 42.;
|
|
|
|
MyVector np_local1;
|
|
np_local1[5] = 42.;
|
|
|
|
double p_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local0[10]
|
|
double p_local1 = p_local0[5];
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local1
|
|
|
|
// The following subscript calls suprisingly choose the non-const operator
|
|
// version.
|
|
MyVector np_local2;
|
|
double p_local2 = np_local2[42];
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local2
|
|
|
|
MyVector np_local3;
|
|
const double np_local4 = np_local3[42];
|
|
|
|
// This subscript results in const overloaded operator.
|
|
const MyVector np_local5{};
|
|
double p_local3 = np_local5[42];
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'double' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local3
|
|
}
|
|
|
|
void const_handle(const double &np_local0);
|
|
void const_handle(const double *np_local0);
|
|
|
|
void non_const_handle(double &np_local0);
|
|
void non_const_handle(double *np_local0);
|
|
|
|
void handle_from_array() {
|
|
// Non-const handle from non-const array forbids declaring the array as const
|
|
double np_local0[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
double *p_local0 = &np_local0[1]; // Could be `double *const`, but warning deactivated by default
|
|
|
|
double np_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
double &non_const_ref = np_local1[1];
|
|
non_const_ref = 42.;
|
|
|
|
double np_local2[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
double *np_local3;
|
|
np_local3 = &np_local2[5];
|
|
|
|
double np_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
non_const_handle(np_local4[2]);
|
|
double np_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
non_const_handle(&np_local5[2]);
|
|
|
|
// Constant handles are ok
|
|
double p_local1[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local1[10]
|
|
const double *p_local2 = &p_local1[2]; // Could be `const double *const`, but warning deactivated by default
|
|
|
|
double p_local3[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local3[10]
|
|
const double &const_ref = p_local3[2];
|
|
|
|
double p_local4[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local4' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local4[10]
|
|
const double *const_ptr;
|
|
const_ptr = &p_local4[2];
|
|
|
|
double p_local5[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local5' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local5[10]
|
|
const_handle(p_local5[2]);
|
|
double p_local6[10] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 9.};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local6' of type 'double[10]' can be declared 'const'
|
|
// CHECK-FIXES: double const p_local6[10]
|
|
const_handle(&p_local6[2]);
|
|
}
|
|
|
|
void range_for() {
|
|
int np_local0[2] = {1, 2};
|
|
for (int &non_const_ref : np_local0) {
|
|
non_const_ref = 42;
|
|
}
|
|
|
|
int np_local1[2] = {1, 2};
|
|
for (auto &non_const_ref : np_local1) {
|
|
non_const_ref = 43;
|
|
}
|
|
|
|
int np_local2[2] = {1, 2};
|
|
for (auto &&non_const_ref : np_local2) {
|
|
non_const_ref = 44;
|
|
}
|
|
|
|
int *np_local3[2] = {&np_local0[0], &np_local0[1]};
|
|
for (int *non_const_ptr : np_local3) {
|
|
*non_const_ptr = 45;
|
|
}
|
|
|
|
// FIXME same as above, but silenced
|
|
int *const np_local4[2] = {&np_local0[0], &np_local0[1]};
|
|
for (auto *non_const_ptr : np_local4) {
|
|
*non_const_ptr = 46;
|
|
}
|
|
|
|
int p_local0[2] = {1, 2};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int[2]' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0[2]
|
|
for (int value : p_local0) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'value' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const value
|
|
}
|
|
|
|
int p_local1[2] = {1, 2};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int[2]' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1[2]
|
|
for (const int &const_ref : p_local1) {
|
|
}
|
|
}
|
|
|
|
void arrays_of_pointers_are_ignored() {
|
|
int *np_local0[2] = {nullptr, nullptr};
|
|
|
|
using intPtr = int*;
|
|
intPtr np_local1[2] = {nullptr, nullptr};
|
|
}
|
|
|
|
inline void *operator new(decltype(sizeof(void *)), void *p) { return p; }
|
|
|
|
struct Value {
|
|
};
|
|
void placement_new() {
|
|
Value Mem;
|
|
Value *V = new (&Mem) Value;
|
|
}
|
|
|
|
struct ModifyingConversion {
|
|
operator int() { return 15; }
|
|
};
|
|
struct NonModifyingConversion {
|
|
operator int() const { return 15; }
|
|
};
|
|
void conversion_operators() {
|
|
ModifyingConversion np_local0;
|
|
NonModifyingConversion p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'NonModifyingConversion' can be declared 'const'
|
|
// CHECK-FIXES: NonModifyingConversion const p_local0
|
|
|
|
int np_local1 = np_local0;
|
|
np_local1 = p_local0;
|
|
}
|
|
|
|
void casts() {
|
|
decltype(sizeof(void *)) p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'decltype(sizeof(void *))'
|
|
// CHECK-FIXES: decltype(sizeof(void *)) const p_local0
|
|
auto np_local0 = reinterpret_cast<void *>(p_local0);
|
|
np_local0 = nullptr;
|
|
|
|
int p_local1 = 43;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
short p_local2 = static_cast<short>(p_local1);
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local2' of type 'short' can be declared 'const'
|
|
// CHECK-FIXES: short const p_local2
|
|
|
|
int np_local1 = p_local2;
|
|
int &np_local2 = static_cast<int &>(np_local1);
|
|
np_local2 = 5;
|
|
}
|
|
|
|
void ternary_operator() {
|
|
int np_local0 = 1, np_local1 = 2;
|
|
int &np_local2 = true ? np_local0 : np_local1;
|
|
np_local2 = 2;
|
|
|
|
int p_local0 = 3, np_local3 = 5;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-NOT-FIXES: int const p_local0 = 3
|
|
const int &np_local4 = true ? p_local0 : ++np_local3;
|
|
|
|
int np_local5[3] = {1, 2, 3};
|
|
int &np_local6 = np_local5[1] < np_local5[2] ? np_local5[0] : np_local5[2];
|
|
np_local6 = 42;
|
|
|
|
int np_local7[3] = {1, 2, 3};
|
|
int *np_local8 = np_local7[1] < np_local7[2] ? &np_local7[0] : &np_local7[2];
|
|
*np_local8 = 42;
|
|
}
|
|
|
|
// Taken from libcxx/include/type_traits and improved readability.
|
|
template <class Tp, Tp v>
|
|
struct integral_constant {
|
|
static constexpr const Tp value = v;
|
|
using value_type = Tp;
|
|
using type = integral_constant;
|
|
constexpr operator value_type() const noexcept { return value; }
|
|
constexpr value_type operator()() const noexcept { return value; }
|
|
};
|
|
|
|
template <typename T>
|
|
struct is_integral : integral_constant<bool, false> {};
|
|
template <>
|
|
struct is_integral<int> : integral_constant<bool, true> {};
|
|
|
|
template <typename T>
|
|
struct not_integral : integral_constant<bool, false> {};
|
|
template <>
|
|
struct not_integral<double> : integral_constant<bool, true> {};
|
|
|
|
template <bool, typename Tp = void>
|
|
struct enable_if {};
|
|
|
|
template <typename Tp>
|
|
struct enable_if<true, Tp> { using type = Tp; };
|
|
|
|
template <typename T>
|
|
struct TMPClass {
|
|
T alwaysConst() const { return T{}; }
|
|
|
|
template <typename T2 = T, typename = typename enable_if<is_integral<T2>::value>::type>
|
|
T sometimesConst() const { return T{}; }
|
|
|
|
template <typename T2 = T, typename = typename enable_if<not_integral<T2>::value>::type>
|
|
T sometimesConst() { return T{}; }
|
|
};
|
|
|
|
void meta_type() {
|
|
TMPClass<int> p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'TMPClass<int>' can be declared 'const'
|
|
// CHECK-FIXES: TMPClass<int> const p_local0
|
|
p_local0.alwaysConst();
|
|
p_local0.sometimesConst();
|
|
|
|
TMPClass<double> p_local1;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'TMPClass<double>' can be declared 'const'
|
|
// CHECK-FIXES: TMPClass<double> const p_local1
|
|
p_local1.alwaysConst();
|
|
|
|
TMPClass<double> np_local0;
|
|
np_local0.alwaysConst();
|
|
np_local0.sometimesConst();
|
|
}
|
|
|
|
// This test is the essence from llvm/lib/Support/MemoryBuffer.cpp at line 450
|
|
template <typename T>
|
|
struct to_construct : T {
|
|
to_construct(int &j) {}
|
|
};
|
|
template <typename T>
|
|
void placement_new_in_unique_ptr() {
|
|
int p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
int np_local0 = p_local0;
|
|
new to_construct<T>(np_local0);
|
|
}
|
|
|
|
struct stream_obj {};
|
|
stream_obj &operator>>(stream_obj &o, unsigned &foo);
|
|
void input_operator() {
|
|
stream_obj np_local0;
|
|
unsigned np_local1 = 42;
|
|
np_local0 >> np_local1;
|
|
}
|
|
|
|
struct stream_obj_template {};
|
|
template <typename IStream>
|
|
IStream &operator>>(IStream &o, unsigned &foo);
|
|
|
|
template <typename Stream>
|
|
void input_operator_template() {
|
|
Stream np_local0;
|
|
unsigned np_local1 = 42;
|
|
np_local0 >> np_local1;
|
|
}
|
|
|
|
// Test bit fields
|
|
struct HardwareRegister {
|
|
unsigned field : 5;
|
|
unsigned : 7;
|
|
unsigned another : 20;
|
|
};
|
|
|
|
void TestRegisters() {
|
|
HardwareRegister np_reg0;
|
|
np_reg0.field = 3;
|
|
|
|
HardwareRegister p_reg1{3, 22};
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_reg1' of type 'HardwareRegister' can be declared 'const'
|
|
// CHECK-FIXES: HardwareRegister const p_reg1
|
|
const unsigned p_val = p_reg1.another;
|
|
}
|
|
|
|
struct IntWrapper {
|
|
IntWrapper &operator=(unsigned value) { return *this; }
|
|
template <typename Istream>
|
|
friend Istream &operator>>(Istream &is, IntWrapper &rhs);
|
|
};
|
|
struct IntMaker {
|
|
friend IntMaker &operator>>(IntMaker &, unsigned &);
|
|
};
|
|
template <typename Istream>
|
|
Istream &operator>>(Istream &is, IntWrapper &rhs) {
|
|
unsigned np_local0 = 0;
|
|
is >> np_local0;
|
|
return is;
|
|
}
|
|
|
|
struct Actuator {
|
|
int actuations;
|
|
};
|
|
struct Sensor {
|
|
int observations;
|
|
};
|
|
struct System : public Actuator, public Sensor {
|
|
};
|
|
int some_computation(int arg);
|
|
int test_inheritance() {
|
|
System np_sys;
|
|
np_sys.actuations = 5;
|
|
return some_computation(np_sys.actuations);
|
|
}
|
|
struct AnotherActuator : Actuator {
|
|
};
|
|
Actuator &test_return_polymorphic() {
|
|
static AnotherActuator np_local0;
|
|
return np_local0;
|
|
}
|
|
|
|
using f_signature = int *(*)(int &);
|
|
int *my_alloc(int &size) { return new int[size]; }
|
|
struct A {
|
|
int f(int &i) { return i + 1; }
|
|
int (A::*x)(int &);
|
|
};
|
|
void f() {
|
|
int p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
int np_local0 = 42;
|
|
f_signature action = my_alloc;
|
|
action(np_local0);
|
|
my_alloc(np_local0);
|
|
|
|
int np_local1 = 42;
|
|
A a;
|
|
a.x = &A::f;
|
|
(a.*(a.x))(np_local1);
|
|
}
|
|
|
|
struct nested_data {
|
|
int more_data;
|
|
};
|
|
struct repro_assignment_to_reference {
|
|
int my_data;
|
|
nested_data nested;
|
|
};
|
|
void assignment_reference() {
|
|
repro_assignment_to_reference np_local0{42};
|
|
int &np_local1 = np_local0.my_data;
|
|
np_local1++;
|
|
|
|
repro_assignment_to_reference np_local2;
|
|
int &np_local3 = np_local2.nested.more_data;
|
|
np_local3++;
|
|
}
|
|
|
|
struct non_const_iterator {
|
|
int data[42];
|
|
|
|
int *begin() { return &data[0]; }
|
|
int *end() { return &data[41]; }
|
|
};
|
|
|
|
// The problem is, that 'begin()' and 'end()' are not const overloaded, so
|
|
// they are always a mutation. If 'np_local1' is fixed to const it results in
|
|
// a compilation error.
|
|
void for_bad_iterators() {
|
|
non_const_iterator np_local0;
|
|
non_const_iterator &np_local1 = np_local0;
|
|
|
|
for (int np_local2 : np_local1) {
|
|
np_local2++;
|
|
}
|
|
|
|
non_const_iterator np_local3;
|
|
for (int p_local0 : np_local3)
|
|
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
;
|
|
|
|
// Horrible code constructs...
|
|
{
|
|
non_const_iterator np_local4;
|
|
np_local4.data[0]++;
|
|
non_const_iterator np_local5;
|
|
for (int p_local1 : np_local4, np_local5)
|
|
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
;
|
|
|
|
non_const_iterator np_local6;
|
|
non_const_iterator np_local7;
|
|
for (int p_local2 : 1 > 2 ? np_local6 : np_local7)
|
|
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: variable 'p_local2' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local2
|
|
;
|
|
|
|
non_const_iterator np_local8;
|
|
non_const_iterator np_local9;
|
|
for (int p_local3 : 2 > 1 ? np_local8 : (np_local8, np_local9))
|
|
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: variable 'p_local3' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local3
|
|
;
|
|
}
|
|
}
|
|
|
|
struct good_iterator {
|
|
int data[3] = {1, 2, 3};
|
|
|
|
int *begin() { return &data[0]; }
|
|
int *end() { return &data[2]; }
|
|
const int *begin() const { return &data[0]; }
|
|
const int *end() const { return &data[2]; }
|
|
};
|
|
|
|
void good_iterators() {
|
|
good_iterator p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'good_iterator' can be declared 'const'
|
|
// CHECK-FIXES: good_iterator const p_local0
|
|
good_iterator &p_local1 = p_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local1' of type 'good_iterator &' can be declared 'const'
|
|
// CHECK-FIXES: good_iterator const&p_local1
|
|
|
|
for (int p_local2 : p_local1) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local2' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local2
|
|
(void)p_local2;
|
|
}
|
|
|
|
good_iterator p_local3;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local3' of type 'good_iterator' can be declared 'const'
|
|
// CHECK-FIXES: good_iterator const p_local3
|
|
for (int p_local4 : p_local3)
|
|
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local4' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local4
|
|
;
|
|
good_iterator np_local1;
|
|
for (int &np_local2 : np_local1)
|
|
np_local2++;
|
|
}
|
|
|
|
void for_bad_iterators_array() {
|
|
int np_local0[42];
|
|
int(&np_local1)[42] = np_local0;
|
|
|
|
for (int &np_local2 : np_local1) {
|
|
np_local2++;
|
|
}
|
|
}
|
|
void for_ok_iterators_array() {
|
|
int np_local0[42];
|
|
int(&p_local0)[42] = np_local0;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int (&)[42]' can be declared 'const'
|
|
// CHECK-FIXES: int const(&p_local0)[42]
|
|
|
|
for (int p_local1 : p_local0) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: variable 'p_local1' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local1
|
|
(void)p_local1;
|
|
}
|
|
}
|
|
|
|
void take_ref(int &);
|
|
void ternary_reference() {
|
|
int np_local0 = 42;
|
|
int np_local1 = 43;
|
|
take_ref((np_local0 > np_local1 ? np_local0 : (np_local0, np_local1)));
|
|
}
|
|
|
|
void complex_usage() {
|
|
int np_local0 = 42;
|
|
int p_local0 = 42;
|
|
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p_local0' of type 'int' can be declared 'const'
|
|
// CHECK-FIXES: int const p_local0
|
|
int np_local1 = 42;
|
|
(np_local0 == p_local0 ? np_local0 : (p_local0, np_local1))++;
|
|
}
|
|
|
|
void vlas() {
|
|
int N = 1; // Can't make N 'const' because VLAs make everything awful
|
|
sizeof(int[++N]);
|
|
}
|
|
|
|
struct base {
|
|
int member;
|
|
};
|
|
struct derived : base {};
|
|
struct another_struct {
|
|
derived member;
|
|
};
|
|
void another_struct_f() {
|
|
another_struct np_local0{};
|
|
base &np_local1 = np_local0.member;
|
|
np_local1.member++;
|
|
}
|
|
struct list_init {
|
|
int &member;
|
|
};
|
|
void create_false_positive() {
|
|
int np_local0 = 42;
|
|
list_init p_local0 = {np_local0};
|
|
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'p_local0' of type 'list_init' can be declared 'const'
|
|
// CHECK-FIXES: list_init const p_local0
|
|
}
|
|
struct list_init_derived {
|
|
base &member;
|
|
};
|
|
void list_init_derived_func() {
|
|
derived np_local0;
|
|
list_init_derived p_local0 = {np_local0};
|
|
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'p_local0' of type 'list_init_derived' can be declared 'const'
|
|
// CHECK-FIXES: list_init_derived const p_local0
|
|
}
|
|
template <typename L, typename R>
|
|
struct ref_pair {
|
|
L &first;
|
|
R &second;
|
|
};
|
|
template <typename T>
|
|
void list_init_template() {
|
|
T np_local0{};
|
|
ref_pair<T, T> p_local0 = {np_local0, np_local0};
|
|
}
|
|
void cast_in_class_hierarchy() {
|
|
derived np_local0;
|
|
base p_local1 = static_cast<base &>(np_local0);
|
|
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'p_local1' of type 'base' can be declared 'const'
|
|
// CHECK-FIXES: base const p_local1
|
|
}
|
|
|
|
void function_ref_target(int);
|
|
using my_function_type = void (&)(int);
|
|
void func_references() {
|
|
// Could be const, because the reference is not adjusted but adding that
|
|
// has no effect and creates a compiler warning.
|
|
my_function_type ptr = function_ref_target;
|
|
}
|
|
|
|
template <typename T>
|
|
T &return_ref() {
|
|
static T global;
|
|
return global;
|
|
}
|
|
template <typename T>
|
|
T *return_ptr() { return &return_ref<T>(); }
|
|
|
|
void auto_usage_variants() {
|
|
auto auto_val0 = int{};
|
|
// CHECK-FIXES-NOT: auto const auto_val0
|
|
auto &auto_val1 = auto_val0;
|
|
auto *auto_val2 = &auto_val0;
|
|
|
|
auto auto_ref0 = return_ref<int>();
|
|
// CHECK-FIXES-NOT: auto const auto_ref0
|
|
auto &auto_ref1 = return_ref<int>(); // Bad
|
|
auto *auto_ref2 = return_ptr<int>();
|
|
|
|
auto auto_ptr0 = return_ptr<int>();
|
|
// CHECK-FIXES-NOT: auto const auto_ptr0
|
|
auto &auto_ptr1 = auto_ptr0;
|
|
auto *auto_ptr2 = return_ptr<int>();
|
|
|
|
using MyTypedef = int;
|
|
auto auto_td0 = MyTypedef{};
|
|
// CHECK-FIXES-NOT: auto const auto_td0
|
|
auto &auto_td1 = auto_td0;
|
|
auto *auto_td2 = &auto_td0;
|
|
}
|
|
|
|
using PointerToMemberFunction = int (Value::*)();
|
|
void member_pointer(Value &x, PointerToMemberFunction m) {
|
|
Value &member_pointer_tmp = x;
|
|
(member_pointer_tmp.*m)();
|
|
}
|
|
|
|
using PointerToConstMemberFunction = int (Value::*)() const;
|
|
void member_pointer_const(Value &x, PointerToConstMemberFunction m) {
|
|
Value &member_pointer_tmp = x;
|
|
// CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'member_pointer_tmp' of type 'Value &' can be declared 'const'
|
|
(member_pointer_tmp.*m)();
|
|
}
|
|
|
|
namespace gh127776_false_positive {
|
|
template <class T> struct vector { T &operator[](int t); };
|
|
template <typename T> void f() {
|
|
vector<int> x;
|
|
x[T{}] = 3;
|
|
}
|
|
} // namespace gh127776_false_positive
|
|
|
|
namespace gh132931_false_positive {
|
|
using T = const int;
|
|
void valid(int i) {
|
|
const int arr0[] = {1, 2, 3};
|
|
T arr1[] = {1, 2, 3};
|
|
}
|
|
} // namespace gh132931_false_positive
|