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 
char_param_tests()229 void char_param_tests() {
230   std::string ss;
231   ss.find('c') == std::string::npos;
232   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
233   // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ss, 'c');{{$}}
234 
235   std::string_view ssv;
236   ssv.find('c') == std::string_view::npos;
237   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
238   // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(ssv, 'c');{{$}}
239 
240   absl::string_view asv;
241   asv.find('c') == absl::string_view::npos;
242   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use !absl::StrContains instead of
243   // CHECK-FIXES: {{^[[:space:]]*}}!absl::StrContains(asv, 'c');{{$}}
244 }
245 
246 #define FOO(a, b, c, d) ((a).find(b) == std::string::npos ? (c) : (d))
247 
248 // Confirms that it does not match when a macro would be "torn" by the fix.
no_tearing_macros()249 void no_tearing_macros() {
250   std::string h = "helo";
251   FOO(h, "x", 5, 6);
252 }
253 
254 // Confirms that it does not match when the pos parameter is non-zero.
no_nonzero_pos()255 void no_nonzero_pos() {
256   std::string ss;
257   ss.find("a", 1) == std::string::npos;
258 
259   std::string_view ssv;
260   ssv.find("a", 2) == std::string_view::npos;
261 
262   absl::string_view asv;
263   asv.find("a", 3) == std::string_view::npos;
264 }
265 
266 // Confirms that it does not match when it's compared to something other than
267 // npos, even if the value is the same as npos.
no_non_npos()268 void no_non_npos() {
269   std::string ss;
270   ss.find("a") == 0;
271   ss.find("a") == 1;
272   ss.find("a") == -1;
273 
274   std::string_view ssv;
275   ssv.find("a") == 0;
276   ssv.find("a") == 1;
277   ssv.find("a") == -1;
278 
279   absl::string_view asv;
280   asv.find("a") == 0;
281   asv.find("a") == 1;
282   asv.find("a") == -1;
283 }
284 
285 // Confirms that it does not match if the two operands are the same.
no_symmetric_operands()286 void no_symmetric_operands() {
287   std::string ss;
288   ss.find("a") == ss.find("a");
289   std::string::npos == std::string::npos;
290 }
291