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