| 1 | // RUN: %check_clang_tidy %s readability-make-member-function-const %t |
| 2 | |
| 3 | struct Str { |
| 4 | void const_method() const; |
| 5 | void non_const_method(); |
| 6 | }; |
| 7 | |
| 8 | namespace Diagnose { |
| 9 | struct A; |
| 10 | |
| 11 | void free_const_use(const A *); |
| 12 | void free_const_use(const A &); |
| 13 | |
| 14 | struct A { |
| 15 | int M; |
| 16 | const int ConstM; |
| 17 | struct { |
| 18 | int M; |
| 19 | } Struct; |
| 20 | Str S; |
| 21 | Str &Sref; |
| 22 | |
| 23 | void already_const() const; |
| 24 | |
| 25 | int read_field() { |
| 26 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'read_field' can be made const |
| 27 | // CHECK-FIXES: {{^}} int read_field() const { |
| 28 | return M; |
| 29 | } |
| 30 | |
| 31 | int read_struct_field() { |
| 32 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'read_struct_field' can be made const |
| 33 | // CHECK-FIXES: {{^}} int read_struct_field() const { |
| 34 | return Struct.M; |
| 35 | } |
| 36 | |
| 37 | int read_const_field() { |
| 38 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'read_const_field' can be made const |
| 39 | // CHECK-FIXES: {{^}} int read_const_field() const { |
| 40 | return ConstM; |
| 41 | } |
| 42 | |
| 43 | int read_fields_in_parentheses() { |
| 44 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'read_fields_in_parentheses' can be made const |
| 45 | // CHECK-FIXES: {{^}} int read_fields_in_parentheses() const { |
| 46 | return (this)->M + (((((Struct.M))))) + ((this->ConstM)); |
| 47 | } |
| 48 | |
| 49 | void call_const_member() { |
| 50 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'call_const_member' can be made const |
| 51 | // CHECK-FIXES: {{^}} void call_const_member() const { |
| 52 | already_const(); |
| 53 | } |
| 54 | |
| 55 | void call_const_member_on_public_field() { |
| 56 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'call_const_member_on_public_field' can be made const |
| 57 | // CHECK-FIXES: {{^}} void call_const_member_on_public_field() const { |
| 58 | S.const_method(); |
| 59 | } |
| 60 | |
| 61 | void call_const_member_on_public_field_ref() { |
| 62 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'call_const_member_on_public_field_ref' can be made const |
| 63 | // CHECK-FIXES: {{^}} void call_const_member_on_public_field_ref() const { |
| 64 | Sref.const_method(); |
| 65 | } |
| 66 | |
| 67 | const Str &return_public_field_ref() { |
| 68 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: method 'return_public_field_ref' can be made const |
| 69 | // CHECK-FIXES: {{^}} const Str &return_public_field_ref() const { |
| 70 | return S; |
| 71 | } |
| 72 | |
| 73 | const A *return_this_const() { |
| 74 | // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: method 'return_this_const' can be made const |
| 75 | // CHECK-FIXES: {{^}} const A *return_this_const() const { |
| 76 | return this; |
| 77 | } |
| 78 | |
| 79 | const A &return_this_const_ref() { |
| 80 | // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: method 'return_this_const_ref' can be made const |
| 81 | // CHECK-FIXES: {{^}} const A &return_this_const_ref() const { |
| 82 | return *this; |
| 83 | } |
| 84 | |
| 85 | void const_use() { |
| 86 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'const_use' can be made const |
| 87 | // CHECK-FIXES: {{^}} void const_use() const { |
| 88 | free_const_use(this); |
| 89 | } |
| 90 | |
| 91 | void const_use_ref() { |
| 92 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'const_use_ref' can be made const |
| 93 | // CHECK-FIXES: {{^}} void const_use_ref() const { |
| 94 | free_const_use(*this); |
| 95 | } |
| 96 | |
| 97 | auto trailingReturn() -> int { |
| 98 | // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: method 'trailingReturn' can be made const |
| 99 | // CHECK-FIXES: {{^}} auto trailingReturn() const -> int { |
| 100 | return M; |
| 101 | } |
| 102 | |
| 103 | int volatileFunction() volatile { |
| 104 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'volatileFunction' can be made const |
| 105 | // CHECK-FIXES: {{^}} int volatileFunction() const volatile { |
| 106 | return M; |
| 107 | } |
| 108 | |
| 109 | int restrictFunction() __restrict { |
| 110 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'restrictFunction' can be made const |
| 111 | // CHECK-FIXES: {{^}} int restrictFunction() const __restrict { |
| 112 | return M; |
| 113 | } |
| 114 | |
| 115 | int refFunction() & { |
| 116 | // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: method 'refFunction' can be made const |
| 117 | // CHECK-FIXES: {{^}} int refFunction() const & { |
| 118 | return M; |
| 119 | } |
| 120 | |
| 121 | void out_of_line_call_const(); |
| 122 | // CHECK-FIXES: {{^}} void out_of_line_call_const() const; |
| 123 | }; |
| 124 | |
| 125 | void A::out_of_line_call_const() { |
| 126 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: method 'out_of_line_call_const' can be made const |
| 127 | // CHECK-FIXES: {{^}}void A::out_of_line_call_const() const { |
| 128 | already_const(); |
| 129 | } |
| 130 | } // namespace Diagnose |
| 131 | |
| 132 | namespace Keep { |
| 133 | struct Keep; |
| 134 | void free_non_const_use(Keep *); |
| 135 | void free_non_const_use(Keep &); |
| 136 | |
| 137 | struct Keep { |
| 138 | private: |
| 139 | void private_const_method() const; |
| 140 | Str PrivateS; |
| 141 | Str *Sptr; |
| 142 | Str &Sref; |
| 143 | |
| 144 | public: |
| 145 | int M; |
| 146 | Str S; |
| 147 | |
| 148 | void keepTrivial() {} |
| 149 | |
| 150 | // See readability-convert-member-functions-to-static instead. |
| 151 | void keepStatic() { int I = 0; } |
| 152 | |
| 153 | const int *keepConstCast() const; |
| 154 | int *keepConstCast() { // Needs to stay non-const. |
| 155 | return const_cast<int *>(static_cast<const Keep *>(this)->keepConstCast()); |
| 156 | } |
| 157 | |
| 158 | void non_const_use() { free_non_const_use(this); } |
| 159 | void non_const_use_ref() { free_non_const_use(*this); } |
| 160 | |
| 161 | Keep *return_this() { |
| 162 | return this; |
| 163 | } |
| 164 | |
| 165 | Keep &return_this_ref() { |
| 166 | return *this; |
| 167 | } |
| 168 | |
| 169 | void escape_this() { |
| 170 | Keep *Escaped = this; |
| 171 | } |
| 172 | |
| 173 | void call_private_const_method() { |
| 174 | private_const_method(); |
| 175 | } |
| 176 | |
| 177 | int keepConst() const { return M; } |
| 178 | |
| 179 | virtual int keepVirtual() { return M; } |
| 180 | |
| 181 | void writeField() { |
| 182 | M = 1; |
| 183 | } |
| 184 | |
| 185 | void callNonConstMember() { writeField(); } |
| 186 | |
| 187 | void call_non_const_member_on_field() { S.non_const_method(); } |
| 188 | |
| 189 | void call_const_member_on_private_field() { |
| 190 | // Technically, this method could be const-qualified, |
| 191 | // but it might not be logically const. |
| 192 | PrivateS.const_method(); |
| 193 | } |
| 194 | |
| 195 | const Str &return_private_field_ref() { |
| 196 | // Technically, this method could be const-qualified, |
| 197 | // but it might not be logically const. |
| 198 | return PrivateS; |
| 199 | } |
| 200 | |
| 201 | void call_non_const_member_on_pointee() { |
| 202 | Sptr->non_const_method(); |
| 203 | } |
| 204 | |
| 205 | void call_const_member_on_pointee() { |
| 206 | // Technically, this method could be const-qualified, |
| 207 | // but it might not be logically const. |
| 208 | Sptr->const_method(); |
| 209 | } |
| 210 | |
| 211 | Str *return_pointer() { |
| 212 | // Technically, this method could be const-qualified, |
| 213 | // but it might not be logically const. |
| 214 | return Sptr; |
| 215 | } |
| 216 | |
| 217 | const Str *return_const_pointer() { |
| 218 | // Technically, this method could be const-qualified, |
| 219 | // but it might not be logically const. |
| 220 | return Sptr; |
| 221 | } |
| 222 | |
| 223 | void call_non_const_member_on_ref() { |
| 224 | Sref.non_const_method(); |
| 225 | } |
| 226 | |
| 227 | void escaped_private_field() { |
| 228 | const auto &Escaped = Sref; |
| 229 | } |
| 230 | |
| 231 | Str &return_field_ref() { |
| 232 | // Technically, this method could be const-qualified, |
| 233 | // but it might not be logically const. |
| 234 | return Sref; |
| 235 | } |
| 236 | |
| 237 | const Str &return_field_const_ref() { |
| 238 | // Technically, this method could be const-qualified, |
| 239 | // but it might not be logically const. |
| 240 | return Sref; |
| 241 | } |
| 242 | }; |
| 243 | |
| 244 | struct KeepVirtualDerived : public Keep { |
| 245 | int keepVirtual() { return M; } |
| 246 | }; |
| 247 | |
| 248 | void KeepLambdas() { |
| 249 | auto F = +[]() { return 0; }; |
| 250 | auto F2 = []() { return 0; }; |
| 251 | } |
| 252 | |
| 253 | template <class Base> |
| 254 | struct KeepWithDependentBase : public Base { |
| 255 | int M; |
| 256 | // We cannot make this method const because it might need to override |
| 257 | // a function from Base. |
| 258 | int const_f() { return M; } |
| 259 | }; |
| 260 | |
| 261 | template <class T> |
| 262 | struct KeepClassTemplate { |
| 263 | int M; |
| 264 | // We cannot make this method const because a specialization |
| 265 | // might use *this differently. |
| 266 | int const_f() { return M; } |
| 267 | }; |
| 268 | |
| 269 | struct KeepMemberFunctionTemplate { |
| 270 | int M; |
| 271 | // We cannot make this method const because a specialization |
| 272 | // might use *this differently. |
| 273 | template <class T> |
| 274 | int const_f() { return M; } |
| 275 | }; |
| 276 | |
| 277 | void instantiate() { |
| 278 | struct S {}; |
| 279 | KeepWithDependentBase<S> I1; |
| 280 | I1.const_f(); |
| 281 | |
| 282 | KeepClassTemplate<int> I2; |
| 283 | I2.const_f(); |
| 284 | |
| 285 | KeepMemberFunctionTemplate I3; |
| 286 | I3.const_f<int>(); |
| 287 | } |
| 288 | |
| 289 | struct NoFixitInMacro { |
| 290 | int M; |
| 291 | |
| 292 | #define FUN const_use_macro() |
| 293 | int FUN { |
| 294 | return M; |
| 295 | } |
| 296 | |
| 297 | #define T(FunctionName, Keyword) \ |
| 298 | int FunctionName() Keyword { return M; } |
| 299 | #define EMPTY |
| 300 | T(A, EMPTY) |
| 301 | T(B, const) |
| 302 | |
| 303 | #define T2(FunctionName) \ |
| 304 | int FunctionName() { return M; } |
| 305 | T2(A2) |
| 306 | }; |
| 307 | |
| 308 | // Real-world code, see clang::ObjCInterfaceDecl. |
| 309 | class DataPattern { |
| 310 | int &data() const; |
| 311 | |
| 312 | public: |
| 313 | const int &get() const { |
| 314 | return const_cast<DataPattern *>(this)->get(); |
| 315 | } |
| 316 | |
| 317 | // This member function must stay non-const, even though |
| 318 | // it only calls other private const member functions. |
| 319 | int &get() { |
| 320 | return data(); |
| 321 | } |
| 322 | |
| 323 | void set() { |
| 324 | data() = 42; |
| 325 | } |
| 326 | }; |
| 327 | |
| 328 | struct MemberFunctionPointer { |
| 329 | void call_non_const(void (MemberFunctionPointer::*FP)()) { |
| 330 | (this->*FP)(); |
| 331 | } |
| 332 | |
| 333 | void call_const(void (MemberFunctionPointer::*FP)() const) { |
| 334 | (this->*FP)(); |
| 335 | } |
| 336 | }; |
| 337 | |
| 338 | } // namespace Keep |
| 339 | |