1 | // RUN: %check_clang_tidy %s readability-redundant-string-cstr %t -- -- -isystem %clang_tidy_headers |
2 | #include <string> |
3 | |
4 | template <typename T> |
5 | struct iterator { |
6 | T *operator->(); |
7 | T &operator*(); |
8 | }; |
9 | |
10 | namespace llvm { |
11 | struct StringRef { |
12 | StringRef(const char *p); |
13 | StringRef(const std::string &); |
14 | }; |
15 | } |
16 | |
17 | // Tests for std::string. |
18 | |
19 | void f1(const std::string &s) { |
20 | f1(s: s.c_str()); |
21 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
22 | // CHECK-FIXES: {{^ }}f1(s);{{$}} |
23 | f1(s: s.data()); |
24 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr] |
25 | // CHECK-FIXES: {{^ }}f1(s);{{$}} |
26 | } |
27 | void f2(const llvm::StringRef r) { |
28 | std::string s; |
29 | f2(r: s.c_str()); |
30 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}} |
31 | // CHECK-FIXES: {{^ }}std::string s;{{$}} |
32 | // CHECK-FIXES-NEXT: {{^ }}f2(s);{{$}} |
33 | } |
34 | void f3(const llvm::StringRef &r) { |
35 | std::string s; |
36 | f3(r: s.c_str()); |
37 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call {{.*}} |
38 | // CHECK-FIXES: {{^ }}std::string s;{{$}} |
39 | // CHECK-FIXES-NEXT: {{^ }}f3(s);{{$}} |
40 | } |
41 | void f4(const std::string &s) { |
42 | const std::string* ptr = &s; |
43 | f1(s: ptr->c_str()); |
44 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
45 | // CHECK-FIXES: {{^ }}f1(*ptr);{{$}} |
46 | } |
47 | void f5(const std::string &s) { |
48 | std::string tmp; |
49 | tmp.append(s: s.c_str()); |
50 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}} |
51 | // CHECK-FIXES: {{^ }}tmp.append(s);{{$}} |
52 | tmp.assign(s: s.c_str()); |
53 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}} |
54 | // CHECK-FIXES: {{^ }}tmp.assign(s);{{$}} |
55 | |
56 | if (tmp.compare(s: s.c_str()) == 0) return; |
57 | // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: redundant call {{.*}} |
58 | // CHECK-FIXES: {{^ }}if (tmp.compare(s) == 0) return;{{$}} |
59 | |
60 | if (tmp.compare(pos: 1, n1: 2, s: s.c_str()) == 0) return; |
61 | // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: redundant call {{.*}} |
62 | // CHECK-FIXES: {{^ }}if (tmp.compare(1, 2, s) == 0) return;{{$}} |
63 | |
64 | if (tmp.find(s: s.c_str()) == 0) return; |
65 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}} |
66 | // CHECK-FIXES: {{^ }}if (tmp.find(s) == 0) return;{{$}} |
67 | |
68 | if (tmp.find(s: s.c_str(), pos: 2) == 0) return; |
69 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}} |
70 | // CHECK-FIXES: {{^ }}if (tmp.find(s, 2) == 0) return;{{$}} |
71 | |
72 | if (tmp.find(s: s.c_str(), pos: 2) == 0) return; |
73 | // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant call {{.*}} |
74 | // CHECK-FIXES: {{^ }}if (tmp.find(s, 2) == 0) return;{{$}} |
75 | |
76 | tmp.insert(pos: 1, s: s.c_str()); |
77 | // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant call {{.*}} |
78 | // CHECK-FIXES: {{^ }}tmp.insert(1, s);{{$}} |
79 | |
80 | tmp = s.c_str(); |
81 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}} |
82 | // CHECK-FIXES: {{^ }}tmp = s;{{$}} |
83 | |
84 | tmp += s.c_str(); |
85 | // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant call {{.*}} |
86 | // CHECK-FIXES: {{^ }}tmp += s;{{$}} |
87 | |
88 | if (tmp == s.c_str()) return; |
89 | // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}} |
90 | // CHECK-FIXES: {{^ }}if (tmp == s) return;{{$}} |
91 | |
92 | tmp = s + s.c_str(); |
93 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant call {{.*}} |
94 | // CHECK-FIXES: {{^ }}tmp = s + s;{{$}} |
95 | |
96 | tmp = s.c_str() + s; |
97 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call {{.*}} |
98 | // CHECK-FIXES: {{^ }}tmp = s + s;{{$}} |
99 | } |
100 | void f6(const std::string &s) { |
101 | std::string tmp; |
102 | tmp.append(s: s.c_str(), n: 2); |
103 | tmp.assign(s: s.c_str(), n: 2); |
104 | |
105 | if (tmp.compare(str: s) == 0) return; |
106 | if (tmp.compare(pos: 1, n: 2, str: s) == 0) return; |
107 | |
108 | tmp = s; |
109 | tmp += s; |
110 | |
111 | if (tmp == s) |
112 | return; |
113 | |
114 | tmp = s + s; |
115 | |
116 | if (tmp.find(s: s.c_str(), pos: 2, n: 4) == 0) return; |
117 | |
118 | tmp.insert(pos1: 1, str: s); |
119 | tmp.insert(pos: 1, s: s.c_str(), n: 2); |
120 | } |
121 | void f7(std::string_view sv) { |
122 | std::string s; |
123 | f7(sv: s.c_str()); |
124 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
125 | // CHECK-FIXES: {{^ }}f7(s);{{$}} |
126 | f7(sv: s.data()); |
127 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr] |
128 | // CHECK-FIXES: {{^ }}f7(s);{{$}} |
129 | } |
130 | |
131 | // Tests for std::wstring. |
132 | |
133 | void g1(const std::wstring &s) { |
134 | g1(s: s.c_str()); |
135 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
136 | // CHECK-FIXES: {{^ }}g1(s);{{$}} |
137 | } |
138 | void g2(std::wstring_view sv) { |
139 | std::wstring s; |
140 | g2(sv: s.c_str()); |
141 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
142 | // CHECK-FIXES: {{^ }}g2(s);{{$}} |
143 | g2(sv: s.data()); |
144 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr] |
145 | // CHECK-FIXES: {{^ }}g2(s);{{$}} |
146 | } |
147 | |
148 | // Tests for std::u16string. |
149 | |
150 | void h1(const std::u16string &s) { |
151 | h1(s: s.c_str()); |
152 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
153 | // CHECK-FIXES: {{^ }}h1(s);{{$}} |
154 | } |
155 | void h2(std::u16string_view sv) { |
156 | std::u16string s; |
157 | h2(sv: s.c_str()); |
158 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
159 | // CHECK-FIXES: {{^ }}h2(s);{{$}} |
160 | h2(sv: s.data()); |
161 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr] |
162 | // CHECK-FIXES: {{^ }}h2(s);{{$}} |
163 | } |
164 | |
165 | // Tests for std::u32string. |
166 | |
167 | void k1(const std::u32string &s) { |
168 | k1(s: s.c_str()); |
169 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
170 | // CHECK-FIXES: {{^ }}k1(s);{{$}} |
171 | } |
172 | void k2(std::u32string_view sv) { |
173 | std::u32string s; |
174 | k2(sv: s.c_str()); |
175 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
176 | // CHECK-FIXES: {{^ }}k2(s);{{$}} |
177 | k2(sv: s.data()); |
178 | // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr] |
179 | // CHECK-FIXES: {{^ }}k2(s);{{$}} |
180 | } |
181 | |
182 | // Tests on similar classes that aren't good candidates for this checker. |
183 | |
184 | struct NotAString { |
185 | NotAString(); |
186 | NotAString(const NotAString&); |
187 | const char *c_str() const; |
188 | }; |
189 | |
190 | void dummy(const char*) {} |
191 | |
192 | void invalid(const NotAString &s) { |
193 | dummy(s.c_str()); |
194 | } |
195 | |
196 | // Test for rvalue std::string. |
197 | void m1(std::string&&) { |
198 | std::string s; |
199 | |
200 | m1(s.c_str()); |
201 | |
202 | void (*m1p1)(std::string&&); |
203 | m1p1 = m1; |
204 | m1p1(s.c_str()); |
205 | |
206 | using m1tp = void (*)(std::string &&); |
207 | m1tp m1p2 = m1; |
208 | m1p2(s.c_str()); |
209 | } |
210 | |
211 | // Test for overloaded operator-> |
212 | void it(iterator<std::string> i) |
213 | { |
214 | std::string tmp; |
215 | tmp = i->c_str(); |
216 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
217 | // CHECK-FIXES: {{^ }}tmp = *i;{{$}} |
218 | |
219 | // An unlikely situation and the outcome is not ideal, but at least the fix doesn't generate broken code. |
220 | tmp = i.operator->()->c_str(); |
221 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
222 | // CHECK-FIXES: {{^ }}tmp = *i.operator->();{{$}} |
223 | |
224 | // The fix contains an unnecessary set of parentheses, but these have no effect. |
225 | iterator<std::string> *pi = &i; |
226 | tmp = (*pi)->c_str(); |
227 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
228 | // CHECK-FIXES: {{^ }}tmp = *(*pi);{{$}} |
229 | |
230 | // An unlikely situation, but at least the fix doesn't generate broken code. |
231 | tmp = pi->operator->()->c_str(); |
232 | // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
233 | // CHECK-FIXES: {{^ }}tmp = *pi->operator->();{{$}} |
234 | } |
235 | |
236 | namespace PR45286 { |
237 | struct Foo { |
238 | void func(const std::string &) {} |
239 | void func2(std::string &&) {} |
240 | }; |
241 | |
242 | void bar() { |
243 | std::string Str{"aaa" }; |
244 | Foo Foo; |
245 | Foo.func(Str.c_str()); |
246 | // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
247 | // CHECK-FIXES: {{^ }}Foo.func(Str);{{$}} |
248 | |
249 | // Ensure it doesn't transform Binding to r values |
250 | Foo.func2(Str.c_str()); |
251 | |
252 | // Ensure its not confused by parens |
253 | Foo.func((Str.c_str())); |
254 | // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: redundant call to 'c_str' [readability-redundant-string-cstr] |
255 | // CHECK-FIXES: {{^ }}Foo.func((Str));{{$}} |
256 | Foo.func2((Str.c_str())); |
257 | } |
258 | } // namespace PR45286 |
259 | |