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
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(s: "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(s: "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(s: "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(str: s) == 0;
51 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
52 // CHECK-FIXES: s.starts_with(s);
53
54 s.find(s: "aaa") != 0;
55 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
56 // CHECK-FIXES: !s.starts_with("aaa");
57
58 s.find(str: 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(s: "....") == 0) { /* do something */ }
63 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
64 // CHECK-FIXES: if (s.starts_with("....")) { /* do something */ }
65
66 0 != s.find(s: "a");
67 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
68 // CHECK-FIXES: !s.starts_with("a");
69
70 s.rfind(s: "a", pos: 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(str: s, pos: 0) == 0;
75 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
76 // CHECK-FIXES: s.starts_with(s);
77
78 s.rfind(s: "aaa", pos: 0) != 0;
79 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
80 // CHECK-FIXES: !s.starts_with("aaa");
81
82 s.rfind(str: foo(foo(bar())), pos: 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(s: "....", pos: 0) == 0) { /* do something */ }
87 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
88 // CHECK-FIXES: if (s.starts_with("....")) { /* do something */ }
89
90 0 != s.rfind(s: "a", pos: 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(s: "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(s: "a") == ZERO;
106 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
107 // CHECK-FIXES: s.starts_with("a");
108
109 sv.find(str: "a") == 0;
110 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
111 // CHECK-FIXES: sv.starts_with("a");
112
113 sv.rfind(str: "a", pos: 0) != 0;
114 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
115 // CHECK-FIXES: !sv.starts_with("a");
116
117 ss.find(s: "a") == 0;
118 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
119 // CHECK-FIXES: ss.starts_with("a");
120
121 sss.find(s: "a") == 0;
122 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
123 // CHECK-FIXES: sss.starts_with("a");
124
125 sl.find(s: "a") == 0;
126 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
127 // CHECK-FIXES: sl.starts_with("a");
128
129 slc.find(s: "a") == 0;
130 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use startsWith
131 // CHECK-FIXES: slc.startsWith("a");
132
133 puv.find(s: "a") == 0;
134 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
135 // CHECK-FIXES: puv.starts_with("a");
136
137 puvf.find(s: "a") == 0;
138 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
139 // CHECK-FIXES: puvf.starts_with("a");
140
141 s.compare(pos: 0, n1: 1, s: "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(pos: 0, n1: 1, s: "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(pos: 0, n1: strlen(s: "a"), s: "a") == 0;
150 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
151 // CHECK-FIXES: s.starts_with("a");
152
153 s.compare(pos: 0, std::n1: strlen(s: "a"), s: "a") == 0;
154 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
155 // CHECK-FIXES: s.starts_with("a");
156
157 s.compare(pos: 0, std::n1: strlen(s: ("a")), s: "a") == 0;
158 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
159 // CHECK-FIXES: s.starts_with("a");
160
161 s.compare(pos: 0, std::n1: strlen(s: ("a")), s: (("a"))) == 0;
162 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
163 // CHECK-FIXES: s.starts_with("a");
164
165 s.compare(pos: 0, n: s.size(), str: s) == 0;
166 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
167 // CHECK-FIXES: s.starts_with(s);
168
169 s.compare(pos: 0, n: s.length(), str: 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(pos: 0, n: sv.length(), svt: 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(pos: 0, LENGTH(s), str: 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), str: 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), svt: sv) != 0;
187 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
188 // CHECK-FIXES: !s.starts_with(sv);
189
190 s.compare(pos: s.size() - 6, n1: 6, s: "suffix") == 0;
191 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
192 // CHECK-FIXES: s.ends_with("suffix");
193
194 s.compare(pos: s.size() - 6, n1: strlen(s: "abcdef"), s: "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(pos: s.size() - suffix.size(), n: suffix.size(), str: suffix) == 0;
200 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
201 // CHECK-FIXES: s.ends_with(suffix);
202
203 s.rfind(s: "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(s: "suffix") == s.size() - strlen(s: "suffix");
208 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
209 // CHECK-FIXES: s.ends_with("suffix");
210
211 s.rfind(str: 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(str: suffix, pos: 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(str: 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(str: suffix, pos: 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(str: suffix, pos: 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(str: 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(str: suffix);
236 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
237 // CHECK-FIXES: s.ends_with(suffix);
238
239 s.find(s: "a", pos: 0) == 0;
240 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
241 // CHECK-FIXES: s.starts_with("a");
242
243 s.find(str: s, ZERO) == 0;
244 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
245 // CHECK-FIXES: s.starts_with(s);
246
247 s.find(str: s, pos: 0) == ZERO;
248 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
249 // CHECK-FIXES: s.starts_with(s);
250
251 s.find(s: "aaa", pos: 0, n: 3) == 0;
252 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
253 // CHECK-FIXES: s.starts_with("aaa");
254
255 s.find(s: "aaa", ZERO, n: 3) == 0;
256 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
257 // CHECK-FIXES: s.starts_with("aaa");
258
259 s.find(s: "aaa", ZERO, n: strlen(s: ("aaa"))) == ZERO;
260 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
261 // CHECK-FIXES: s.starts_with("aaa");
262
263 s.rfind(s: "aaa", pos: 0, n: 3) != 0;
264 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
265 // CHECK-FIXES: !s.starts_with("aaa");
266
267 s.rfind(s: "aaa", ZERO, n: 3) != 0;
268 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
269 // CHECK-FIXES: !s.starts_with("aaa");
270
271 s.rfind(s: "aaa", ZERO, n: strlen(s: ("aaa"))) == ZERO;
272 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with
273 // CHECK-FIXES: s.starts_with("aaa");
274
275 struct S {
276 std::string s;
277 } t;
278 t.s.rfind(str: suffix) == (t.s.size() - suffix.size());
279 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use ends_with
280 // 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(pos: 0, n1: 1, s: "ab") == 0;
293 s.rfind(str: suffix, pos: 1) == s.size() - suffix.size();
294
295 #define STR(x) std::string(x)
296 0 == STR(s).find(s: "a");
297
298 #define STRING s
299 if (0 == STRING.find(s: "ala")) { /* do something */}
300
301 // Cases when literal-size and size parameters are different are not being matched.
302 s.find(s: "aaa", pos: 0, n: 2) == 0;
303 s.find(s: "aaa", pos: 0, n: strlen(s: "aa")) == 0;
304 s.rfind(s: "aaa", pos: 0, n: 2) == 0;
305 s.rfind(s: "aaa", pos: 0, n: strlen(s: "aa")) == 0;
306}
307
308void test_substr() {
309 std::string str("hello world");
310 std::string prefix = "hello";
311
312 // Basic pattern
313 str.substr(pos: 0, n: 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 side
318 "hello" == str.substr(pos: 0, n: 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 comparison
323 str.substr(pos: 0, n: 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 transformed
328 str.substr(pos: 1, n: 5) == "hello";
329 str.substr(pos: 0, n: 4) == "hello"; // Length mismatch
330
331 size_t len = 5;
332 str.substr(pos: 0, n: len) == "hello"; // Non-constant length
333
334 // String literal with size calculation
335 str.substr(pos: 0, n: strlen(s: "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(pos: 0, n: 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(pos: 0, n: 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 behavior
348 #define MSG "hello"
349 str.substr(pos: 0, n: 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() str
360 SUBSTR(STR(), 0, 6) == "prefix";
361 "prefix" == SUBSTR(STR(), 0, 6);
362
363 str.substr(pos: 0, n: strlen(s: "hello123")) == "hello";
364}
365
366void test_operator_rewriting(std::string str, std::string prefix) {
367 str.substr(pos: 0, n: prefix.size()) == prefix;
368 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr
369 // CHECK-FIXES: str.starts_with(prefix);
370
371 str.substr(pos: 0, n: prefix.size()) != prefix;
372 // CHECK-MESSAGES: :[[@LINE-1]]:{{[0-9]+}}: warning: use starts_with instead of substr
373 // CHECK-FIXES: !str.starts_with(prefix);
374}
375

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang-tools-extra/test/clang-tidy/checkers/modernize/use-starts-ends-with.cpp