375 lines · cpp
1// RUN: %check_clang_tidy -std=c++20 %s modernize-use-starts-ends-with %t -- \2// RUN: -- -isystem %clang_tidy_headers3 4#include <string.h>5#include <string>6 7std::string foo(std::string);8std::string bar();9 10class sub_string : public std::string {};11class sub_sub_string : public sub_string {};12 13struct string_like {14 bool starts_with(const char *s) const;15 size_t find(const char *s, size_t pos = 0) const;16};17 18struct string_like_camel {19 bool startsWith(const char *s) const;20 size_t find(const char *s, size_t pos = 0) const;21};22 23struct 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 29struct 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 35void 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_with44 // CHECK-FIXES: ((s)).starts_with("a");45 46 (s + "a").find("a") == ((0));47 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with48 // CHECK-FIXES: (s + "a").starts_with("a");49 50 s.find(s) == 0;51 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with52 // CHECK-FIXES: s.starts_with(s);53 54 s.find("aaa") != 0;55 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with56 // CHECK-FIXES: !s.starts_with("aaa");57 58 s.find(foo(foo(bar()))) != 0;59 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with60 // 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_with64 // CHECK-FIXES: if (s.starts_with("....")) { /* do something */ }65 66 0 != s.find("a");67 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with68 // 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_with76 // CHECK-FIXES: s.starts_with(s);77 78 s.rfind("aaa", 0) != 0;79 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with80 // 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_with84 // 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_with88 // CHECK-FIXES: if (s.starts_with("....")) { /* do something */ }89 90 0 != s.rfind("a", 0);91 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with92 // CHECK-FIXES: !s.starts_with("a");93 94 #define FIND find95 s.FIND("a") == 0;96 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with97 // 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_with102 // CHECK-FIXES: s.starts_with(PREFIX);103 104 #define ZERO 0105 s.find("a") == ZERO;106 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with107 // CHECK-FIXES: s.starts_with("a");108 109 sv.find("a") == 0;110 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with111 // CHECK-FIXES: sv.starts_with("a");112 113 sv.rfind("a", 0) != 0;114 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with115 // CHECK-FIXES: !sv.starts_with("a");116 117 ss.find("a") == 0;118 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with119 // CHECK-FIXES: ss.starts_with("a");120 121 sss.find("a") == 0;122 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with123 // CHECK-FIXES: sss.starts_with("a");124 125 sl.find("a") == 0;126 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with127 // CHECK-FIXES: sl.starts_with("a");128 129 slc.find("a") == 0;130 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith131 // CHECK-FIXES: slc.startsWith("a");132 133 puv.find("a") == 0;134 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with135 // CHECK-FIXES: puv.starts_with("a");136 137 puvf.find("a") == 0;138 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with139 // 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_with151 // 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_with155 // 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_with159 // 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_with163 // 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_with167 // 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_with171 // 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_with175 // 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_with180 // 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_with184 // 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_with188 // 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_with192 // 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_with196 // 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_with201 // CHECK-FIXES: s.ends_with(suffix);202 203 s.rfind("suffix") == s.size() - 6;204 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with205 // 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_with209 // 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_with213 // 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_with217 // 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_with221 // 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_with225 // 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_with229 // 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_with233 // 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_with237 // CHECK-FIXES: s.ends_with(suffix);238 239 s.find("a", 0) == 0;240 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with241 // CHECK-FIXES: s.starts_with("a");242 243 s.find(s, ZERO) == 0;244 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with245 // CHECK-FIXES: s.starts_with(s);246 247 s.find(s, 0) == ZERO;248 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with249 // CHECK-FIXES: s.starts_with(s);250 251 s.find("aaa", 0, 3) == 0;252 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with253 // CHECK-FIXES: s.starts_with("aaa");254 255 s.find("aaa", ZERO, 3) == 0;256 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with257 // CHECK-FIXES: s.starts_with("aaa");258 259 s.find("aaa", ZERO, strlen(("aaa"))) == ZERO;260 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with261 // CHECK-FIXES: s.starts_with("aaa");262 263 s.rfind("aaa", 0, 3) != 0;264 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with265 // CHECK-FIXES: !s.starts_with("aaa");266 267 s.rfind("aaa", ZERO, 3) != 0;268 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with269 // CHECK-FIXES: !s.starts_with("aaa");270 271 s.rfind("aaa", ZERO, strlen(("aaa"))) == ZERO;272 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with273 // CHECK-FIXES: s.starts_with("aaa");274 275 struct S {276 std::string s;277 } t;278 t.s.rfind(suffix) == (t.s.size() - suffix.size());279 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with280 // CHECK-FIXES: t.s.ends_with(suffix);281 282 // Expressions that don't trigger the check are here.283 #define EQ(x, y) ((x) == (y))284 EQ(s.find("a"), 0);285 286 #define DOTFIND(x, y) (x).find(y)287 DOTFIND(s, "a") == 0;288 289 #define STARTS_WITH_COMPARE(x, y) (x).compare(0, (x).size(), (y))290 STARTS_WITH_COMPARE(s, s) == 0;291 292 s.compare(0, 1, "ab") == 0;293 s.rfind(suffix, 1) == s.size() - suffix.size();294 295 #define STR(x) std::string(x)296 0 == STR(s).find("a");297 298 #define STRING s299 if (0 == STRING.find("ala")) { /* do something */}300 301 // Cases when literal-size and size parameters are different are not being matched.302 s.find("aaa", 0, 2) == 0;303 s.find("aaa", 0, strlen("aa")) == 0;304 s.rfind("aaa", 0, 2) == 0;305 s.rfind("aaa", 0, strlen("aa")) == 0;306}307 308void test_substr() {309 std::string str("hello world");310 std::string prefix = "hello";311 312 // Basic pattern313 str.substr(0, 5) == "hello";314 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]315 // CHECK-FIXES: str.starts_with("hello");316 317 // With string literal on left side318 "hello" == str.substr(0, 5);319 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]320 // CHECK-FIXES: str.starts_with("hello");321 322 // Inequality comparison323 str.substr(0, 5) != "world";324 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]325 // CHECK-FIXES: !str.starts_with("world");326 327 // Ensure non-zero start position is not transformed328 str.substr(1, 5) == "hello";329 str.substr(0, 4) == "hello"; // Length mismatch330 331 size_t len = 5;332 str.substr(0, len) == "hello"; // Non-constant length333 334 // String literal with size calculation335 str.substr(0, strlen("hello")) == "hello";336 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]337 // CHECK-FIXES: str.starts_with("hello");338 339 str.substr(0, prefix.size()) == prefix;340 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]341 // CHECK-FIXES: str.starts_with(prefix);342 343 str.substr(0, prefix.length()) == prefix;344 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]345 // CHECK-FIXES: str.starts_with(prefix);346 347 // Tests to verify macro behavior348 #define MSG "hello"349 str.substr(0, strlen(MSG)) == MSG;350 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr [modernize-use-starts-ends-with]351 // CHECK-FIXES: str.starts_with(MSG);352 353 #define STARTS_WITH(X, Y) (X).substr(0, (Y).size()) == (Y)354 STARTS_WITH(str, prefix);355 356 #define SUBSTR(X, A, B) (X).substr((A), (B))357 SUBSTR(str, 0, 6) == "prefix";358 359 #define STR() str360 SUBSTR(STR(), 0, 6) == "prefix";361 "prefix" == SUBSTR(STR(), 0, 6);362 363 str.substr(0, strlen("hello123")) == "hello";364}365 366void test_operator_rewriting(std::string str, std::string prefix) {367 str.substr(0, prefix.size()) == prefix;368 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr369 // CHECK-FIXES: str.starts_with(prefix);370 371 str.substr(0, prefix.size()) != prefix;372 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr373 // CHECK-FIXES: !str.starts_with(prefix);374}375