1 // RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \ 2 // RUN: -- -isystem %clang_tidy_headers 3 4 #include <string.h> 5 #include <string> 6 7 std::string foo(std::string); 8 std::string bar(); 9 10 class sub_string : public std::string {}; 11 class sub_sub_string : public sub_string {}; 12 13 struct string_like { 14 bool starts_with(const char *s) const; 15 size_t find(const char *s, size_t pos = 0) const; 16 }; 17 18 struct string_like_camel { 19 bool startsWith(const char *s) const; 20 size_t find(const char *s, size_t pos = 0) const; 21 }; 22 23 struct prefer_underscore_version { 24 bool starts_with(const char *s) const; 25 bool startsWith(const char *s) const; 26 size_t find(const char *s, size_t pos = 0) const; 27 }; 28 29 struct prefer_underscore_version_flip { 30 bool startsWith(const char *s) const; 31 bool starts_with(const char *s) const; 32 size_t find(const char *s, size_t pos = 0) const; 33 }; 34 35 void test(std::string s, std::string_view sv, sub_string ss, sub_sub_string sss, 36 string_like sl, string_like_camel slc, prefer_underscore_version puv, 37 prefer_underscore_version_flip puvf) { 38 s.find("a") == 0; 39 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of find [modernize-use-starts-ends-with] 40 // CHECK-FIXES: s.starts_with("a"); 41 42 (((((s)).find("a")))) == ((0)); 43 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 44 // CHECK-FIXES: ((s)).starts_with("a"); 45 46 (s + "a").find("a") == ((0)); 47 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 48 // CHECK-FIXES: (s + "a").starts_with("a"); 49 50 s.find(s) == 0; 51 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 52 // CHECK-FIXES: s.starts_with(s); 53 54 s.find("aaa") != 0; 55 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 56 // CHECK-FIXES: !s.starts_with("aaa"); 57 58 s.find(foo(foo(bar()))) != 0; 59 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 60 // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); 61 62 if (s.find("....") == 0) { /* do something */ } 63 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 64 // CHECK-FIXES: if (s.starts_with("....")) 65 66 0 != s.find("a"); 67 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 68 // CHECK-FIXES: !s.starts_with("a"); 69 70 s.rfind("a", 0) == 0; 71 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of rfind [modernize-use-starts-ends-with] 72 // CHECK-FIXES: s.starts_with("a"); 73 74 s.rfind(s, 0) == 0; 75 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 76 // CHECK-FIXES: s.starts_with(s); 77 78 s.rfind("aaa", 0) != 0; 79 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 80 // CHECK-FIXES: !s.starts_with("aaa"); 81 82 s.rfind(foo(foo(bar())), 0) != 0; 83 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 84 // CHECK-FIXES: !s.starts_with(foo(foo(bar()))); 85 86 if (s.rfind("....", 0) == 0) { /* do something */ } 87 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 88 // CHECK-FIXES: if (s.starts_with("....")) 89 90 0 != s.rfind("a", 0); 91 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 92 // CHECK-FIXES: !s.starts_with("a"); 93 94 #define FIND find 95 s.FIND("a") == 0; 96 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 97 // CHECK-FIXES: s.starts_with("a") 98 99 #define PREFIX "a" 100 s.find(PREFIX) == 0; 101 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 102 // CHECK-FIXES: s.starts_with(PREFIX) 103 104 #define ZERO 0 105 s.find("a") == ZERO; 106 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 107 // CHECK-FIXES: s.starts_with("a") 108 109 sv.find("a") == 0; 110 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 111 // CHECK-FIXES: sv.starts_with("a"); 112 113 sv.rfind("a", 0) != 0; 114 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 115 // CHECK-FIXES: !sv.starts_with("a"); 116 117 ss.find("a") == 0; 118 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 119 // CHECK-FIXES: ss.starts_with("a"); 120 121 sss.find("a") == 0; 122 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 123 // CHECK-FIXES: ss.starts_with("a"); 124 125 sl.find("a") == 0; 126 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 127 // CHECK-FIXES: sl.starts_with("a"); 128 129 slc.find("a") == 0; 130 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith 131 // CHECK-FIXES: slc.startsWith("a"); 132 133 puv.find("a") == 0; 134 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 135 // CHECK-FIXES: puv.starts_with("a"); 136 137 puvf.find("a") == 0; 138 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 139 // CHECK-FIXES: puvf.starts_with("a"); 140 141 s.compare(0, 1, "a") == 0; 142 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare [modernize-use-starts-ends-with] 143 // CHECK-FIXES: s.starts_with("a"); 144 145 s.compare(0, 1, "a") != 0; 146 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of compare [modernize-use-starts-ends-with] 147 // CHECK-FIXES: !s.starts_with("a"); 148 149 s.compare(0, strlen("a"), "a") == 0; 150 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 151 // CHECK-FIXES: s.starts_with("a"); 152 153 s.compare(0, std::strlen("a"), "a") == 0; 154 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 155 // CHECK-FIXES: s.starts_with("a"); 156 157 s.compare(0, std::strlen(("a")), "a") == 0; 158 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 159 // CHECK-FIXES: s.starts_with("a"); 160 161 s.compare(0, std::strlen(("a")), (("a"))) == 0; 162 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 163 // CHECK-FIXES: s.starts_with("a"); 164 165 s.compare(0, s.size(), s) == 0; 166 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 167 // CHECK-FIXES: s.starts_with(s); 168 169 s.compare(0, s.length(), s) == 0; 170 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 171 // CHECK-FIXES: s.starts_with(s); 172 173 0 != s.compare(0, sv.length(), sv); 174 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 175 // CHECK-FIXES: s.starts_with(sv); 176 177 #define LENGTH(x) (x).length() 178 s.compare(0, LENGTH(s), s) == 0; 179 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 180 // CHECK-FIXES: s.starts_with(s); 181 182 s.compare(ZERO, LENGTH(s), s) == ZERO; 183 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 184 // CHECK-FIXES: s.starts_with(s); 185 186 s.compare(ZERO, LENGTH(sv), sv) != 0; 187 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with 188 // CHECK-FIXES: !s.starts_with(sv); 189 190 s.compare(s.size() - 6, 6, "suffix") == 0; 191 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 192 // CHECK-FIXES: s.ends_with("suffix"); 193 194 s.compare(s.size() - 6, strlen("abcdef"), "suffix") == 0; 195 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 196 // CHECK-FIXES: s.ends_with("suffix"); 197 198 std::string suffix = "suffix"; 199 s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0; 200 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 201 // CHECK-FIXES: s.ends_with(suffix); 202 203 s.rfind("suffix") == s.size() - 6; 204 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 205 // CHECK-FIXES: s.ends_with("suffix"); 206 207 s.rfind("suffix") == s.size() - strlen("suffix"); 208 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 209 // CHECK-FIXES: s.ends_with("suffix"); 210 211 s.rfind(suffix) == s.size() - suffix.size(); 212 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 213 // CHECK-FIXES: s.ends_with(suffix); 214 215 s.rfind(suffix, std::string::npos) == s.size() - suffix.size(); 216 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 217 // CHECK-FIXES: s.ends_with(suffix); 218 219 s.rfind(suffix) == (s.size() - suffix.size()); 220 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 221 // CHECK-FIXES: s.ends_with(suffix); 222 223 s.rfind(suffix, s.npos) == (s.size() - suffix.size()); 224 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 225 // CHECK-FIXES: s.ends_with(suffix); 226 227 s.rfind(suffix, s.npos) == (((s.size()) - (suffix.size()))); 228 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 229 // CHECK-FIXES: s.ends_with(suffix); 230 231 s.rfind(suffix) != s.size() - suffix.size(); 232 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 233 // CHECK-FIXES: !s.ends_with(suffix); 234 235 (s.size() - suffix.size()) == s.rfind(suffix); 236 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 237 // CHECK-FIXES: s.ends_with(suffix); 238 239 struct S { 240 std::string s; 241 } t; 242 t.s.rfind(suffix) == (t.s.size() - suffix.size()); 243 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with 244 // CHECK-FIXES: t.s.ends_with(suffix); 245 246 // Expressions that don't trigger the check are here. 247 #define EQ(x, y) ((x) == (y)) 248 EQ(s.find("a"), 0); 249 250 #define DOTFIND(x, y) (x).find(y) 251 DOTFIND(s, "a") == 0; 252 253 #define STARTS_WITH_COMPARE(x, y) (x).compare(0, (x).size(), (y)) 254 STARTS_WITH_COMPARE(s, s) == 0; 255 256 s.compare(0, 1, "ab") == 0; 257 s.rfind(suffix, 1) == s.size() - suffix.size(); 258 259 #define STR(x) std::string(x) 260 0 == STR(s).find("a"); 261 262 #define STRING s 263 if (0 == STRING.find("ala")) { /* do something */} 264 } 265 266 void test_substr() { 267 std::string str("hello world"); 268 std::string prefix = "hello"; 269 270 // Basic pattern 271 str.substr(0, 5) == "hello"; 272 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 273 // CHECK-FIXES: str.starts_with("hello"); 274 275 // With string literal on left side 276 "hello" == str.substr(0, 5); 277 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 278 // CHECK-FIXES: str.starts_with("hello"); 279 280 // Inequality comparison 281 str.substr(0, 5) != "world"; 282 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 283 // CHECK-FIXES: !str.starts_with("world"); 284 285 // Ensure non-zero start position is not transformed 286 str.substr(1, 5) == "hello"; 287 str.substr(0, 4) == "hello"; // Length mismatch 288 289 size_t len = 5; 290 str.substr(0, len) == "hello"; // Non-constant length 291 292 // String literal with size calculation 293 str.substr(0, strlen("hello")) == "hello"; 294 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 295 // CHECK-FIXES: str.starts_with("hello"); 296 297 str.substr(0, prefix.size()) == prefix; 298 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 299 // CHECK-FIXES: str.starts_with(prefix); 300 301 str.substr(0, prefix.length()) == prefix; 302 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 303 // CHECK-FIXES: str.starts_with(prefix); 304 305 // Tests to verify macro behavior 306 #define MSG "hello" 307 str.substr(0, strlen(MSG)) == MSG; 308 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with] 309 // CHECK-FIXES: str.starts_with(MSG); 310 311 #define STARTS_WITH(X, Y) (X).substr(0, (Y).size()) == (Y) 312 STARTS_WITH(str, prefix); 313 314 #define SUBSTR(X, A, B) (X).substr((A), (B)) 315 SUBSTR(str, 0, 6) == "prefix"; 316 317 #define STR() str 318 SUBSTR(STR(), 0, 6) == "prefix"; 319 "prefix" == SUBSTR(STR(), 0, 6); 320 321 str.substr(0, strlen("hello123")) == "hello"; 322 } 323 324 void test_operator_rewriting(std::string str, std::string prefix) { 325 str.substr(0, prefix.size()) == prefix; 326 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr 327 // CHECK-FIXES: str.starts_with(prefix); 328 329 str.substr(0, prefix.size()) != prefix; 330 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr 331 // CHECK-FIXES: !str.starts_with(prefix); 332 } 333