| // 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.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() {} |
| double constMethod() const {} |
| 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)(); |
| } |