1 // RUN: %check_clang_tidy -check-suffix=STDFORMAT -std=c++20 %s readability-redundant-string-cstr %t -- --  -isystem %clang_tidy_headers -DTEST_STDFORMAT
2 // RUN: %check_clang_tidy -check-suffixes=STDFORMAT,STDPRINT -std=c++2b %s readability-redundant-string-cstr %t -- --  -isystem %clang_tidy_headers -DTEST_STDFORMAT -DTEST_STDPRINT
3 #include <string>
4 
5 namespace std {
6   template<typename T>
7     struct type_identity { using type = T; };
8   template<typename T>
9     using type_identity_t = typename type_identity<T>::type;
10 
11   template <typename CharT, typename... Args>
12   struct basic_format_string {
basic_format_stringstd::basic_format_string13     consteval basic_format_string(const CharT *format) : str(format) {}
14     basic_string_view<CharT, std::char_traits<CharT>> str;
15   };
16 
17   template<typename... Args>
18     using format_string = basic_format_string<char, type_identity_t<Args>...>;
19 
20   template<typename... Args>
21     using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
22 
23 #if defined(TEST_STDFORMAT)
24   template<typename ...Args>
25   std::string format(format_string<Args...>, Args &&...);
26   template<typename ...Args>
27   std::string format(wformat_string<Args...>, Args &&...);
28 #endif // TEST_STDFORMAT
29 
30 #if defined(TEST_STDPRINT)
31   template<typename ...Args>
32   void print(format_string<Args...>, Args &&...);
33   template<typename ...Args>
34   void print(wformat_string<Args...>, Args &&...);
35 #endif // TEST_STDPRINT
36 }
37 
38 namespace notstd {
39 #if defined(TEST_STDFORMAT)
40   template<typename ...Args>
41   std::string format(const char *, Args &&...);
42   template<typename ...Args>
43   std::string format(const wchar_t *, Args &&...);
44 #endif // TEST_STDFORMAT
45 #if defined(TEST_STDPRINT)
46   template<typename ...Args>
47   void print(const char *, Args &&...);
48   template<typename ...Args>
49   void print(const wchar_t *, Args &&...);
50 #endif // TEST_STDPRINT
51 }
52 
53 std::string return_temporary();
54 std::wstring return_wtemporary();
55 
56 #if defined(TEST_STDFORMAT)
std_format(const std::string & s1,const std::string & s2,const std::string & s3)57 void std_format(const std::string &s1, const std::string &s2, const std::string &s3) {
58   auto r1 = std::format("One:{}\n", s1.c_str());
59   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:37: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
60   // CHECK-FIXES-STDFORMAT: {{^  }}auto r1 = std::format("One:{}\n", s1);
61 
62   auto r2 = std::format("One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_temporary().c_str());
63   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:61: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
64   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-2]]:77: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
65   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-3]]:89: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
66   // CHECK-FIXES-STDFORMAT: {{^  }}auto r2 = std::format("One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_temporary());
67 
68   using namespace std;
69   auto r3 = format("Four:{}\n", s1.c_str());
70   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:33: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
71   // CHECK-FIXES-STDFORMAT: {{^  }}auto r3 = format("Four:{}\n", s1);
72 }
73 
std_format_wide(const std::wstring & s1,const std::wstring & s2,const std::wstring & s3)74 void std_format_wide(const std::wstring &s1, const std::wstring &s2, const std::wstring &s3) {
75   auto r1 = std::format(L"One:{}\n", s1.c_str());
76   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:38: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
77   // CHECK-FIXES-STDFORMAT: {{^  }}auto r1 = std::format(L"One:{}\n", s1);
78 
79   auto r2 = std::format(L"One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_wtemporary().c_str());
80   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:62: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
81   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-2]]:78: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
82   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-3]]:90: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
83   // CHECK-FIXES-STDFORMAT: {{^  }}auto r2 = std::format(L"One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_wtemporary());
84 
85   using namespace std;
86   auto r3 = format(L"Four:{}\n", s1.c_str());
87   // CHECK-MESSAGES-STDFORMAT: :[[@LINE-1]]:34: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
88   // CHECK-FIXES-STDFORMAT: {{^  }}auto r3 = format(L"Four:{}\n", s1);
89 }
90 
91 // There's are c_str() calls here, so it shouldn't be touched.
std_format_no_cstr(const std::string & s1,const std::string & s2)92 std::string std_format_no_cstr(const std::string &s1, const std::string &s2) {
93   return std::format("One: {}, Two: {}\n", s1, s2);
94 }
95 
96 // There's are c_str() calls here, so it shouldn't be touched.
std_format_no_cstr_wide(const std::string & s1,const std::string & s2)97 std::string std_format_no_cstr_wide(const std::string &s1, const std::string &s2) {
98   return std::format(L"One: {}, Two: {}\n", s1, s2);
99 }
100 
101 // This is not std::format, so it shouldn't be fixed.
not_std_format(const std::string & s1)102 std::string not_std_format(const std::string &s1) {
103   return notstd::format("One: {}\n", s1.c_str());
104 
105   using namespace notstd;
106   format("One: {}\n", s1.c_str());
107 }
108 
109 // This is not std::format, so it shouldn't be fixed.
not_std_format_wide(const std::string & s1)110 std::string not_std_format_wide(const std::string &s1) {
111   return notstd::format(L"One: {}\n", s1.c_str());
112 
113   using namespace notstd;
114   format(L"One: {}\n", s1.c_str());
115 }
116 #endif // TEST_STDFORMAT
117 
118 #if defined(TEST_STDPRINT)
std_print(const std::string & s1,const std::string & s2,const std::string & s3)119 void std_print(const std::string &s1, const std::string &s2, const std::string &s3) {
120   std::print("One:{}\n", s1.c_str());
121   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
122   // CHECK-FIXES-STDPRINT: {{^  }}std::print("One:{}\n", s1);
123 
124   std::print("One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_temporary().c_str());
125   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:50: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
126   // CHECK-MESSAGES-STDPRINT: :[[@LINE-2]]:66: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
127   // CHECK-MESSAGES-STDPRINT: :[[@LINE-3]]:78: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
128   // CHECK-FIXES-STDPRINT: {{^  }}std::print("One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_temporary());
129 
130   using namespace std;
131   print("Four:{}\n", s1.c_str());
132   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:22: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
133   // CHECK-FIXES-STDPRINT: {{^  }}print("Four:{}\n", s1);
134 }
135 
std_print_wide(const std::wstring & s1,const std::wstring & s2,const std::wstring & s3)136 void std_print_wide(const std::wstring &s1, const std::wstring &s2, const std::wstring &s3) {
137   std::print(L"One:{}\n", s1.c_str());
138   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:27: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
139   // CHECK-FIXES-STDPRINT: {{^  }}std::print(L"One:{}\n", s1);
140 
141   std::print(L"One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_wtemporary().c_str());
142   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:51: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
143   // CHECK-MESSAGES-STDPRINT: :[[@LINE-2]]:67: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
144   // CHECK-MESSAGES-STDPRINT: :[[@LINE-3]]:79: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
145   // CHECK-FIXES-STDPRINT: {{^  }}std::print(L"One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_wtemporary());
146 
147   using namespace std;
148   print(L"Four:{}\n", s1.c_str());
149   // CHECK-MESSAGES-STDPRINT: :[[@LINE-1]]:23: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
150   // CHECK-FIXES-STDPRINT: {{^  }}print(L"Four:{}\n", s1);
151 }
152 
153 // There's no c_str() call here, so it shouldn't be touched.
std_print_no_cstr(const std::string & s1,const std::string & s2)154 void std_print_no_cstr(const std::string &s1, const std::string &s2) {
155   std::print("One: {}, Two: {}\n", s1, s2);
156 }
157 
158 // There's no c_str() call here, so it shouldn't be touched.
std_print_no_cstr_wide(const std::wstring & s1,const std::wstring & s2)159 void std_print_no_cstr_wide(const std::wstring &s1, const std::wstring &s2) {
160   std::print(L"One: {}, Two: {}\n", s1, s2);
161 }
162 
163 // This isn't std::print, so it shouldn't be fixed.
not_std_print(const std::string & s1)164 void not_std_print(const std::string &s1) {
165   notstd::print("One: {}\n", s1.c_str());
166 
167   using namespace notstd;
168   print("One: {}\n", s1.c_str());
169 }
170 
171 // This isn't std::print, so it shouldn't be fixed.
not_std_print_wide(const std::string & s1)172 void not_std_print_wide(const std::string &s1) {
173   notstd::print(L"One: {}\n", s1.c_str());
174 
175   using namespace notstd;
176   print(L"One: {}\n", s1.c_str());
177 }
178 #endif // TEST_STDPRINT
179 
180 #if defined(TEST_STDFORMAT)
181 // We can't declare these earlier since they make the "using namespace std"
182 // tests ambiguous.
183 template<typename ...Args>
184 std::string format(const char *, Args &&...);
185 template<typename ...Args>
186 std::string format(const wchar_t *, Args &&...);
187 
188 // This is not std::format, so it shouldn't be fixed.
not_std_format2(const std::wstring & s1)189 std::string not_std_format2(const std::wstring &s1) {
190   return format("One: {}\n", s1.c_str());
191 }
192 
193 // This is not std::format, so it shouldn't be fixed.
not_std_format2_wide(const std::wstring & s1)194 std::string not_std_format2_wide(const std::wstring &s1) {
195   return format(L"One: {}\n", s1.c_str());
196 }
197 #endif // TEST_STDFORMAT
198 
199 #if defined(TEST_STDPRINT)
200 template<typename ...Args>
201 void print(const char *, Args &&...);
202 template<typename ...Args>
203 void print(const wchar_t *, Args &&...);
204 
205 // This isn't std::print, so it shouldn't be fixed.
not_std_print2(const std::string & s1)206 void not_std_print2(const std::string &s1) {
207   print("One: {}\n", s1.c_str());
208 }
209 
210 // This isn't std::print, so it shouldn't be fixed.
not_std_print2_wide(const std::string & s1)211 void not_std_print2_wide(const std::string &s1) {
212   print(L"One: {}\n", s1.c_str());
213 }
214 #endif // TEST_STDPRINT
215