| 1 | // RUN: %check_clang_tidy --match-partial-fixes %s modernize-pass-by-value %t -- -- -fno-delayed-template-parsing |
| 2 | |
| 3 | namespace { |
| 4 | // POD types are trivially move constructible. |
| 5 | struct POD { |
| 6 | int a, b, c; |
| 7 | }; |
| 8 | |
| 9 | struct Movable { |
| 10 | int a, b, c; |
| 11 | Movable() = default; |
| 12 | Movable(const Movable &) {} |
| 13 | Movable(Movable &&) {} |
| 14 | }; |
| 15 | |
| 16 | struct NotMovable { |
| 17 | NotMovable() = default; |
| 18 | NotMovable(const NotMovable &) = default; |
| 19 | NotMovable(NotMovable &&) = delete; |
| 20 | int a, b, c; |
| 21 | }; |
| 22 | } |
| 23 | |
| 24 | struct A { |
| 25 | A(const Movable &M) : M(M) {} |
| 26 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move [modernize-pass-by-value] |
| 27 | // CHECK-FIXES: A(Movable M) : M(std::move(M)) {} |
| 28 | Movable M; |
| 29 | }; |
| 30 | |
| 31 | // Test that we aren't modifying other things than a parameter. |
| 32 | Movable GlobalObj; |
| 33 | struct B { |
| 34 | B(const Movable &M) : M(GlobalObj) {} |
| 35 | Movable M; |
| 36 | }; |
| 37 | |
| 38 | // Test that a parameter with more than one reference to it won't be changed. |
| 39 | struct C { |
| 40 | // Tests extra-reference in body. |
| 41 | C(const Movable &M) : M(M) { this->i = M.a; } |
| 42 | |
| 43 | // Tests extra-reference in init-list. |
| 44 | C(const Movable &M, int) : M(M), i(M.a) {} |
| 45 | Movable M; |
| 46 | int i; |
| 47 | }; |
| 48 | |
| 49 | // Test that both declaration and definition are updated. |
| 50 | struct D { |
| 51 | D(const Movable &M); |
| 52 | // CHECK-FIXES: D(Movable M); |
| 53 | Movable M; |
| 54 | }; |
| 55 | D::D(const Movable &M) : M(M) {} |
| 56 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
| 57 | // CHECK-FIXES: D::D(Movable M) : M(std::move(M)) {} |
| 58 | |
| 59 | // Test with default parameter. |
| 60 | struct E { |
| 61 | E(const Movable &M = Movable()) : M(M) {} |
| 62 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 63 | // CHECK-FIXES: E(Movable M = Movable()) : M(std::move(M)) {} |
| 64 | Movable M; |
| 65 | }; |
| 66 | |
| 67 | // Test with object that can't be moved. |
| 68 | struct F { |
| 69 | F(const NotMovable &NM) : NM(NM) {} |
| 70 | NotMovable NM; |
| 71 | }; |
| 72 | |
| 73 | // Test unnamed parameter in declaration. |
| 74 | struct G { |
| 75 | G(const Movable &); |
| 76 | // CHECK-FIXES: G(Movable ); |
| 77 | Movable M; |
| 78 | }; |
| 79 | G::G(const Movable &M) : M(M) {} |
| 80 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
| 81 | // CHECK-FIXES: G::G(Movable M) : M(std::move(M)) {} |
| 82 | |
| 83 | // Test parameter with and without qualifier. |
| 84 | namespace ns_H { |
| 85 | typedef ::Movable HMovable; |
| 86 | } |
| 87 | struct H { |
| 88 | H(const ns_H::HMovable &M); |
| 89 | // CHECK-FIXES: H(ns_H::HMovable M); |
| 90 | ns_H::HMovable M; |
| 91 | }; |
| 92 | using namespace ns_H; |
| 93 | H::H(const HMovable &M) : M(M) {} |
| 94 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
| 95 | // CHECK-FIXES: H(HMovable M) : M(std::move(M)) {} |
| 96 | |
| 97 | // Try messing up with macros. |
| 98 | #define MOVABLE_PARAM(Name) const Movable & Name |
| 99 | // CHECK-FIXES: #define MOVABLE_PARAM(Name) const Movable & Name |
| 100 | struct I { |
| 101 | I(MOVABLE_PARAM(M)) : M(M) {} |
| 102 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 103 | // CHECK-FIXES: I(MOVABLE_PARAM(M)) : M(M) {} |
| 104 | Movable M; |
| 105 | }; |
| 106 | #undef MOVABLE_PARAM |
| 107 | |
| 108 | // Test that templates aren't modified. |
| 109 | template <typename T> struct J { |
| 110 | J(const T &M) : M(M) {} |
| 111 | T M; |
| 112 | }; |
| 113 | J<Movable> j1(Movable()); |
| 114 | J<NotMovable> j2(NotMovable()); |
| 115 | |
| 116 | template<class T> |
| 117 | struct MovableTemplateT |
| 118 | { |
| 119 | MovableTemplateT() {} |
| 120 | MovableTemplateT(const MovableTemplateT& o) { } |
| 121 | MovableTemplateT(MovableTemplateT&& o) { } |
| 122 | }; |
| 123 | |
| 124 | template <class T> |
| 125 | struct J2 { |
| 126 | J2(const MovableTemplateT<T>& A); |
| 127 | MovableTemplateT<T> M; |
| 128 | }; |
| 129 | |
| 130 | template <class T> |
| 131 | J2<T>::J2(const MovableTemplateT<T>& A) : M(A) {} |
| 132 | J2<int> j3(MovableTemplateT<int>{}); |
| 133 | |
| 134 | struct K_Movable { |
| 135 | K_Movable() = default; |
| 136 | K_Movable(const K_Movable &) = default; |
| 137 | K_Movable(K_Movable &&o) { dummy = o.dummy; } |
| 138 | int dummy; |
| 139 | }; |
| 140 | |
| 141 | // Test with movable type with an user defined move constructor. |
| 142 | struct K { |
| 143 | K(const K_Movable &M) : M(M) {} |
| 144 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 145 | // CHECK-FIXES: K(K_Movable M) : M(std::move(M)) {} |
| 146 | K_Movable M; |
| 147 | }; |
| 148 | |
| 149 | template <typename T> struct L { |
| 150 | L(const Movable &M) : M(M) {} |
| 151 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 152 | // CHECK-FIXES: L(Movable M) : M(std::move(M)) {} |
| 153 | Movable M; |
| 154 | }; |
| 155 | L<int> l(Movable()); |
| 156 | |
| 157 | // Test with a non-instantiated template class. |
| 158 | template <typename T> struct N { |
| 159 | N(const Movable &M) : M(M) {} |
| 160 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 161 | // CHECK-FIXES: N(Movable M) : M(std::move(M)) {} |
| 162 | |
| 163 | Movable M; |
| 164 | T A; |
| 165 | }; |
| 166 | |
| 167 | // Test with value parameter. |
| 168 | struct O { |
| 169 | O(Movable M) : M(M) {} |
| 170 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 171 | // CHECK-FIXES: O(Movable M) : M(std::move(M)) {} |
| 172 | Movable M; |
| 173 | }; |
| 174 | |
| 175 | // Test with a const-value parameter. |
| 176 | struct P { |
| 177 | P(const Movable M) : M(M) {} |
| 178 | Movable M; |
| 179 | }; |
| 180 | |
| 181 | // Test with multiples parameters where some need to be changed and some don't. |
| 182 | // need to. |
| 183 | struct Q { |
| 184 | Q(const Movable &A, const Movable &B, const Movable &C, double D) |
| 185 | : A(A), B(B), C(C), D(D) {} |
| 186 | // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: pass by value and use std::move |
| 187 | // CHECK-MESSAGES: :[[@LINE-3]]:41: warning: pass by value and use std::move |
| 188 | // CHECK-FIXES: Q(const Movable &A, Movable B, Movable C, double D) |
| 189 | // CHECK-FIXES: : A(A), B(std::move(B)), C(std::move(C)), D(D) {} |
| 190 | const Movable &A; |
| 191 | Movable B; |
| 192 | Movable C; |
| 193 | double D; |
| 194 | }; |
| 195 | |
| 196 | // Test that value-parameters with a nested name specifier are left as-is. |
| 197 | namespace ns_R { |
| 198 | typedef ::Movable RMovable; |
| 199 | } |
| 200 | struct R { |
| 201 | R(ns_R::RMovable M) : M(M) {} |
| 202 | // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: pass by value and use std::move |
| 203 | // CHECK-FIXES: R(ns_R::RMovable M) : M(std::move(M)) {} |
| 204 | ns_R::RMovable M; |
| 205 | }; |
| 206 | |
| 207 | // Test with rvalue parameter. |
| 208 | struct S { |
| 209 | S(Movable &&M) : M(M) {} |
| 210 | Movable M; |
| 211 | }; |
| 212 | |
| 213 | template <typename T, int N> struct array { T A[N]; }; |
| 214 | |
| 215 | // Test that types that are trivially copyable will not use std::move. This will |
| 216 | // cause problems with performance-move-const-arg, as it will revert it. |
| 217 | struct T { |
| 218 | T(array<int, 10> a) : a_(a) {} |
| 219 | array<int, 10> a_; |
| 220 | }; |
| 221 | |
| 222 | struct U { |
| 223 | U(const POD &M) : M(M) {} |
| 224 | POD M; |
| 225 | }; |
| 226 | |
| 227 | // The rewrite can't look through `typedefs` and `using`. |
| 228 | // Test that we don't partially rewrite one decl without rewriting the other. |
| 229 | using MovableConstRef = const Movable &; |
| 230 | struct V { |
| 231 | V(MovableConstRef M); |
| 232 | // CHECK-FIXES: V(MovableConstRef M); |
| 233 | Movable M; |
| 234 | }; |
| 235 | V::V(const Movable &M) : M(M) {} |
| 236 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: pass by value and use std::move |
| 237 | // CHECK-FIXES: V::V(const Movable &M) : M(M) {} |
| 238 | |
| 239 | // Test with paired lvalue/rvalue overloads. |
| 240 | struct W1 { |
| 241 | W1(const Movable &M) : M(M) {} |
| 242 | W1(Movable &&M); |
| 243 | Movable M; |
| 244 | }; |
| 245 | struct W2 { |
| 246 | W2(const Movable &M, int) : M(M) {} |
| 247 | W2(Movable &&M, int); |
| 248 | Movable M; |
| 249 | }; |
| 250 | struct W3 { |
| 251 | W3(const W1 &, const Movable &M) : M(M) {} |
| 252 | W3(W1 &&, Movable &&M); |
| 253 | Movable M; |
| 254 | }; |
| 255 | |