xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-string-cstr.cpp (revision 9c8bc090a0c291efb41cc4b5c51ea0c340961ad1)
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 
f1(const std::string & s)19 void f1(const std::string &s) {
20   f1(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.data());
24   // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: redundant call to 'data' [readability-redundant-string-cstr]
25   // CHECK-FIXES: {{^  }}f1(s);{{$}}
26 }
f2(const llvm::StringRef r)27 void f2(const llvm::StringRef r) {
28   std::string s;
29   f2(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 }
f3(const llvm::StringRef & r)34 void f3(const llvm::StringRef &r) {
35   std::string s;
36   f3(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 }
f4(const std::string & s)41 void f4(const std::string &s) {
42   const std::string* ptr = &s;
43   f1(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 }
f5(const std::string & s)47 void f5(const std::string &s) {
48   std::string tmp;
49   tmp.append(s.c_str());
50   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
51   // CHECK-FIXES: {{^  }}tmp.append(s);{{$}}
52   tmp.assign(s.c_str());
53   // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant call {{.*}}
54   // CHECK-FIXES: {{^  }}tmp.assign(s);{{$}}
55 
56   if (tmp.compare(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(1, 2, 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.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.c_str(), 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.c_str(), 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(1, 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 }
f6(const std::string & s)100 void f6(const std::string &s) {
101   std::string tmp;
102   tmp.append(s.c_str(), 2);
103   tmp.assign(s.c_str(), 2);
104 
105   if (tmp.compare(s) == 0) return;
106   if (tmp.compare(1, 2, 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.c_str(), 2, 4) == 0) return;
117 
118   tmp.insert(1, s);
119   tmp.insert(1, s.c_str(), 2);
120 }
f7(std::string_view sv)121 void f7(std::string_view sv) {
122   std::string s;
123   f7(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(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 
g1(const std::wstring & s)133 void g1(const std::wstring &s) {
134   g1(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 }
g2(std::wstring_view sv)138 void g2(std::wstring_view sv) {
139   std::wstring s;
140   g2(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(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 
h1(const std::u16string & s)150 void h1(const std::u16string &s) {
151   h1(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 }
h2(std::u16string_view sv)155 void h2(std::u16string_view sv) {
156   std::u16string s;
157   h2(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(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 
k1(const std::u32string & s)167 void k1(const std::u32string &s) {
168   k1(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 }
k2(std::u32string_view sv)172 void k2(std::u32string_view sv) {
173   std::u32string s;
174   k2(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(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 
dummy(const char *)190 void dummy(const char*) {}
191 
invalid(const NotAString & s)192 void invalid(const NotAString &s) {
193   dummy(s.c_str());
194 }
195 
196 // Test for rvalue std::string.
m1(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->
it(iterator<std::string> i)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 {
funcPR45286::Foo238   void func(const std::string &) {}
func2PR45286::Foo239   void func2(std::string &&) {}
240 };
241 
bar()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