1 // RUN: %check_clang_tidy %s abseil-string-find-str-contains %t -- \
2 // RUN: -config="{CheckOptions: []}"
3
4 using size_t = decltype(sizeof(int));
5
6 namespace std {
7
8 // Lightweight standin for std::string.
9 template <typename C>
10 class basic_string {
11 public:
12 basic_string();
13 basic_string(const basic_string &);
14 basic_string(const C *);
15 ~basic_string();
16 int find(basic_string s, int pos = 0);
17 int find(const C *s, int pos = 0);
18 int find(char c, int pos = 0);
19 static constexpr size_t npos = -1;
20 };
21 typedef basic_string<char> string;
22
23 // Lightweight standin for std::string_view.
24 template <typename C>
25 class basic_string_view {
26 public:
27 basic_string_view();
28 basic_string_view(const basic_string_view &);
29 basic_string_view(const C *);
30 ~basic_string_view();
31 int find(basic_string_view s, int pos = 0);
32 int find(const C *s, int pos = 0);
33 int find(char c, int pos = 0);
34 static constexpr size_t npos = -1;
35 };
36 typedef basic_string_view<char> string_view;
37
38 } // namespace std
39
40 namespace absl {
41
42 // Lightweight standin for absl::string_view.
43 class string_view {
44 public:
45 string_view();
46 string_view(const string_view &);
47 string_view(const char *);
48 ~string_view();
49 int find(string_view s, int pos = 0);
50 int find(const char *s, int pos = 0);
51 int find(char c, int pos = 0);
52 static constexpr size_t npos = -1;
53 };
54
55 } // namespace absl
56
57 // Functions that take and return our various string-like types.
58 std::string foo_ss(std::string);
59 std::string_view foo_ssv(std::string_view);
60 absl::string_view foo_asv(absl::string_view);
61 std::string bar_ss();
62 std::string_view bar_ssv();
63 absl::string_view bar_asv();
64
65 // Confirms that find==npos and find!=npos work for each supported type, when
66 // npos comes from the correct type.
basic_tests()67 void basic_tests() {
68 std::string ss;
69 ss.find("a") == std::string::npos;
70 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of find() == npos
71 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}}
72
73 ss.find("a") != std::string::npos;
74 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of find() != npos
75 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}}
76
77 std::string::npos != ss.find("a");
78 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
79 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}}
80
81 std::string_view ssv;
82 ssv.find("a") == std::string_view::npos;
83 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
84 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}}
85
86 ssv.find("a") != std::string_view::npos;
87 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
88 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}}
89
90 std::string_view::npos != ssv.find("a");
91 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
92 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}}
93
94 absl::string_view asv;
95 asv.find("a") == absl::string_view::npos;
96 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
97 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}}
98
99 asv.find("a") != absl::string_view::npos;
100 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
101 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}}
102
103 absl::string_view::npos != asv.find("a");
104 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
105 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}}
106 }
107
108 // Confirms that it works even if you mix-and-match the type for find and for
109 // npos. (One of the reasons for this checker is to clean up cases that
110 // accidentally mix-and-match like this. absl::StrContains is less
111 // error-prone.)
mismatched_npos()112 void mismatched_npos() {
113 std::string ss;
114 ss.find("a") == std::string_view::npos;
115 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
116 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "a");{{$}}
117
118 ss.find("a") != absl::string_view::npos;
119 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
120 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, "a");{{$}}
121
122 std::string_view ssv;
123 ssv.find("a") == absl::string_view::npos;
124 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
125 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "a");{{$}}
126
127 ssv.find("a") != std::string::npos;
128 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
129 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, "a");{{$}}
130
131 absl::string_view asv;
132 asv.find("a") == std::string::npos;
133 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
134 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "a");{{$}}
135
136 asv.find("a") != std::string_view::npos;
137 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
138 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, "a");{{$}}
139 }
140
141 // Confirms that it works even when the needle or the haystack are more
142 // complicated expressions.
subexpression_tests()143 void subexpression_tests() {
144 std::string ss, ss2;
145 foo_ss(ss).find(ss2) == std::string::npos;
146 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
147 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ss(ss), ss2);{{$}}
148
149 ss.find(foo_ss(ss2)) != std::string::npos;
150 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
151 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ss, foo_ss(ss2));{{$}}
152
153 foo_ss(bar_ss()).find(foo_ss(ss2)) != std::string::npos;
154 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
155 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ss(bar_ss()), foo_ss(ss2));{{$}}
156
157 std::string_view ssv, ssv2;
158 foo_ssv(ssv).find(ssv2) == std::string_view::npos;
159 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
160 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_ssv(ssv), ssv2);{{$}}
161
162 ssv.find(foo_ssv(ssv2)) != std::string_view::npos;
163 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
164 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(ssv, foo_ssv(ssv2));{{$}}
165
166 foo_ssv(bar_ssv()).find(foo_ssv(ssv2)) != std::string_view::npos;
167 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
168 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_ssv(bar_ssv()), foo_ssv(ssv2));{{$}}
169
170 absl::string_view asv, asv2;
171 foo_asv(asv).find(asv2) == absl::string_view::npos;
172 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
173 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(foo_asv(asv), asv2);{{$}}
174
175 asv.find(foo_asv(asv2)) != absl::string_view::npos;
176 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
177 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(asv, foo_asv(asv2));{{$}}
178
179 foo_asv(bar_asv()).find(foo_asv(asv2)) != absl::string_view::npos;
180 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use absl::StrContains instead of
181 // CHECK-FIXES: {{^[[:space:]]*}}absl::StrContains(foo_asv(bar_asv()), foo_asv(asv2));{{$}}
182 }
183
184 // Confirms that it works with string literal, char* and const char* parameters.
string_literal_and_char_ptr_tests()185 void string_literal_and_char_ptr_tests() {
186 char *c = nullptr;
187 const char *cc = nullptr;
188
189 std::string ss;
190 ss.find("c") == std::string::npos;
191 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
192 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, "c");{{$}}
193
194 ss.find(c) == std::string::npos;
195 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
196 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, c);{{$}}
197
198 ss.find(cc) == std::string::npos;
199 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
200 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, cc);{{$}}
201
202 std::string_view ssv;
203 ssv.find("c") == std::string_view::npos;
204 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
205 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, "c");{{$}}
206
207 ssv.find(c) == std::string_view::npos;
208 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
209 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, c);{{$}}
210
211 ssv.find(cc) == std::string_view::npos;
212 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
213 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, cc);{{$}}
214
215 absl::string_view asv;
216 asv.find("c") == absl::string_view::npos;
217 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
218 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, "c");{{$}}
219
220 asv.find(c) == absl::string_view::npos;
221 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
222 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, c);{{$}}
223
224 asv.find(cc) == absl::string_view::npos;
225 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
226 // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, cc);{{$}}
227 }
228
229 // Confirms that it does *not* match when the parameter to find() is a char,
230 // because absl::StrContains is not implemented for char.
no_char_param_tests()231 void no_char_param_tests() {
232 std::string ss;
233 ss.find('c') == std::string::npos;
234
235 std::string_view ssv;
236 ssv.find('c') == std::string_view::npos;
237
238 absl::string_view asv;
239 asv.find('c') == absl::string_view::npos;
240 }
241
242 #define FOO(a, b, c, d) ((a).find(b) == std::string::npos ? (c) : (d))
243
244 // Confirms that it does not match when a macro would be "torn" by the fix.
no_tearing_macros()245 void no_tearing_macros() {
246 std::string h = "helo";
247 FOO(h, "x", 5, 6);
248 }
249
250 // Confirms that it does not match when the pos parameter is non-zero.
no_nonzero_pos()251 void no_nonzero_pos() {
252 std::string ss;
253 ss.find("a", 1) == std::string::npos;
254
255 std::string_view ssv;
256 ssv.find("a", 2) == std::string_view::npos;
257
258 absl::string_view asv;
259 asv.find("a", 3) == std::string_view::npos;
260 }
261
262 // Confirms that it does not match when it's compared to something other than
263 // npos, even if the value is the same as npos.
no_non_npos()264 void no_non_npos() {
265 std::string ss;
266 ss.find("a") == 0;
267 ss.find("a") == 1;
268 ss.find("a") == -1;
269
270 std::string_view ssv;
271 ssv.find("a") == 0;
272 ssv.find("a") == 1;
273 ssv.find("a") == -1;
274
275 absl::string_view asv;
276 asv.find("a") == 0;
277 asv.find("a") == 1;
278 asv.find("a") == -1;
279 }
280
281 // Confirms that it does not match if the two operands are the same.
no_symmetric_operands()282 void no_symmetric_operands() {
283 std::string ss;
284 ss.find("a") == ss.find("a");
285 std::string::npos == std::string::npos;
286 }
287