xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp (revision 222dd235ffc39b3695a3c002593097bec216a8fa)
1af79372dSMike Crowe // RUN: %check_clang_tidy \
2af79372dSMike Crowe // RUN:   -std=c++20 %s modernize-use-std-format %t -- \
3*222dd235SCongcong Cai // RUN:   -config="{CheckOptions: {modernize-use-std-format.StrictMode: true}}" \
4a199fb12SMike Crowe // RUN:   -- -isystem %clang_tidy_headers \
5a199fb12SMike Crowe // RUN:      -DPRI_CMDLINE_MACRO="\"s\"" \
6a199fb12SMike Crowe // RUN:      -D__PRI_CMDLINE_MACRO="\"s\""
7af79372dSMike Crowe // RUN: %check_clang_tidy \
8af79372dSMike Crowe // RUN:   -std=c++20 %s modernize-use-std-format %t -- \
9*222dd235SCongcong Cai // RUN:   -config="{CheckOptions: {modernize-use-std-format.StrictMode: false}}" \
10a199fb12SMike Crowe // RUN:   -- -isystem %clang_tidy_headers \
11a199fb12SMike Crowe // RUN:      -DPRI_CMDLINE_MACRO="\"s\"" \
12a199fb12SMike Crowe // RUN:      -D__PRI_CMDLINE_MACRO="\"s\""
13af79372dSMike Crowe #include <string>
14af79372dSMike Crowe // CHECK-FIXES: #include <format>
15a199fb12SMike Crowe #include <inttypes.h>
16af79372dSMike Crowe 
17af79372dSMike Crowe namespace absl
18af79372dSMike Crowe {
194589bf90SMike Crowe template <typename S, typename... Args>
204589bf90SMike Crowe std::string StrFormat(const S &format, const Args&... args);
21af79372dSMike Crowe } // namespace absl
22af79372dSMike Crowe 
23af79372dSMike Crowe template <typename T>
24af79372dSMike Crowe struct iterator {
25af79372dSMike Crowe   T *operator->();
26af79372dSMike Crowe   T &operator*();
27af79372dSMike Crowe };
28af79372dSMike Crowe 
29af79372dSMike Crowe std::string StrFormat_simple() {
30af79372dSMike Crowe   return absl::StrFormat("Hello");
31af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
32af79372dSMike Crowe   // CHECK-FIXES: return std::format("Hello");
33af79372dSMike Crowe }
34af79372dSMike Crowe 
35af79372dSMike Crowe std::string StrFormat_complex(const char *name, double value) {
36af79372dSMike Crowe   return absl::StrFormat("'%s'='%f'", name, value);
37af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
38af79372dSMike Crowe   // CHECK-FIXES: return std::format("'{}'='{:f}'", name, value);
39af79372dSMike Crowe }
40af79372dSMike Crowe 
41af79372dSMike Crowe std::string StrFormat_integer_conversions() {
42af79372dSMike Crowe   return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B');
43af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
44af79372dSMike Crowe   // CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B');
45af79372dSMike Crowe }
46af79372dSMike Crowe 
47af79372dSMike Crowe // FormatConverter is capable of removing newlines from the end of the format
48af79372dSMike Crowe // string. Ensure that isn't incorrectly happening for std::format.
49af79372dSMike Crowe std::string StrFormat_no_newline_removal() {
50af79372dSMike Crowe   return absl::StrFormat("a line\n");
51af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
52af79372dSMike Crowe   // CHECK-FIXES: return std::format("a line\n");
53af79372dSMike Crowe }
54af79372dSMike Crowe 
55af79372dSMike Crowe // FormatConverter is capable of removing newlines from the end of the format
56af79372dSMike Crowe // string. Ensure that isn't incorrectly happening for std::format.
57af79372dSMike Crowe std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) {
58af79372dSMike Crowe   return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data());
59af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
60af79372dSMike Crowe   // CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2);
61af79372dSMike Crowe }
62af79372dSMike Crowe 
63af79372dSMike Crowe std::string StrFormat_strict_conversion() {
64af79372dSMike Crowe   const unsigned char uc = 'A';
65af79372dSMike Crowe   return absl::StrFormat("Integer %hhd from unsigned char\n", uc);
66af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
67af79372dSMike Crowe   // CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc);
68af79372dSMike Crowe }
69af79372dSMike Crowe 
70af79372dSMike Crowe std::string StrFormat_field_width_and_precision() {
71af79372dSMike Crowe   auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718);
72af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
73af79372dSMike Crowe   // CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5);
74af79372dSMike Crowe 
75af79372dSMike Crowe   auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2);
76af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
77af79372dSMike Crowe   // CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2);
78af79372dSMike Crowe 
79af79372dSMike Crowe   const int width = 10, precision = 3;
80af79372dSMike Crowe   const unsigned int ui1 = 42, ui2 = 43, ui3 = 44;
81af79372dSMike Crowe   auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3);
82af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
83af79372dSMike Crowe   // CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5);
84af79372dSMike Crowe   // CHECK-FIXES-STRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", static_cast<int>(ui1), 3, static_cast<int>(ui2), 4, 2, static_cast<int>(ui3), 5);
85af79372dSMike Crowe 
86af79372dSMike Crowe   auto s4 = absl::StrFormat("c_str removal width only:%*s width and precision:%*.*s precision only:%.*s", 3, s1.c_str(), 4, 2, s2.c_str(), 5, s3.c_str());
87af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
88af79372dSMike Crowe   // CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5);
89af79372dSMike Crowe 
90af79372dSMike Crowe   const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3;
91af79372dSMike Crowe   auto s5 = absl::StrFormat("c_str() removal pointer width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, ps1->c_str(), 4, 2, ps2->c_str(), 5, ps3->c_str());
92af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
93af79372dSMike Crowe   // CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5);
94af79372dSMike Crowe 
95af79372dSMike Crowe   iterator<std::string> is1, is2, is3;
96af79372dSMike Crowe   auto s6 = absl::StrFormat("c_str() removal iterator width only:%-*s width and precision:%-*.*s precision only:%-.*s", 3, is1->c_str(), 4, 2, is2->c_str(), 5, is3->c_str());
97af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
98af79372dSMike Crowe   // CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5);
99af79372dSMike Crowe 
100af79372dSMike Crowe   return s1 + s2 + s3 + s4 + s5 + s6;
101af79372dSMike Crowe }
102af79372dSMike Crowe 
103af79372dSMike Crowe std::string StrFormat_macros() {
104af79372dSMike Crowe   // The function call is replaced even though it comes from a macro.
105af79372dSMike Crowe #define FORMAT absl::StrFormat
106af79372dSMike Crowe   auto s1 = FORMAT("Hello %d", 42);
107af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
108af79372dSMike Crowe   // CHECK-FIXES: std::format("Hello {}", 42);
109af79372dSMike Crowe 
110af79372dSMike Crowe   // Arguments that are macros aren't replaced with their value, even if they are rearranged.
111af79372dSMike Crowe #define VALUE 3.14159265358979323846
112af79372dSMike Crowe #define WIDTH 10
113af79372dSMike Crowe #define PRECISION 4
114af79372dSMike Crowe   auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE);
115af79372dSMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
116af79372dSMike Crowe   // CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION);
117a199fb12SMike Crowe 
118a199fb12SMike Crowe   const uint64_t u64 = 42;
119a199fb12SMike Crowe   const uint32_t u32 = 32;
120a199fb12SMike Crowe   std::string s;
121a199fb12SMike Crowe 
122a199fb12SMike Crowe   auto s4 = absl::StrFormat("Replaceable macro at end %" PRIu64, u64);
123a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
124a199fb12SMike Crowe   // CHECK-FIXES: std::format("Replaceable macro at end {}", u64);
125a199fb12SMike Crowe 
126a199fb12SMike Crowe   auto s5 = absl::StrFormat("Replaceable macros in middle %" PRIu64 " %" PRIu32 "\n", u64, u32);
127a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
128a199fb12SMike Crowe   // CHECK-FIXES: std::format("Replaceable macros in middle {} {}\n", u64, u32);
129a199fb12SMike Crowe 
130a199fb12SMike Crowe // These need PRI and __PRI prefixes so that the check get as far as looking for
131a199fb12SMike Crowe // where the macro comes from.
132a199fb12SMike Crowe #define PRI_FMT_MACRO "s"
133a199fb12SMike Crowe #define __PRI_FMT_MACRO "s"
134a199fb12SMike Crowe 
135a199fb12SMike Crowe   auto s6 = absl::StrFormat("Unreplaceable macro at end %" PRI_FMT_MACRO, s.c_str());
136a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]
137a199fb12SMike Crowe 
138a199fb12SMike Crowe   auto s7 = absl::StrFormat(__PRI_FMT_MACRO " Unreplaceable macro at beginning %s", s);
139a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format]
140a199fb12SMike Crowe 
141a199fb12SMike Crowe   auto s8 = absl::StrFormat("Unreplacemable macro %" PRI_FMT_MACRO " in the middle", s);
142a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]
143a199fb12SMike Crowe 
144a199fb12SMike Crowe   auto s9 = absl::StrFormat("First macro is replaceable %" PRIu64 " but second one is not %" __PRI_FMT_MACRO, u64, s);
145a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_FMT_MACRO' [modernize-use-std-format]
146a199fb12SMike Crowe 
147a199fb12SMike Crowe   // Needs a PRI prefix so that we get as far as looking for where the macro comes from
148a199fb12SMike Crowe   auto s10 = absl::StrFormat(" macro from command line %" PRI_CMDLINE_MACRO, s);
149a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_CMDLINE_MACRO' [modernize-use-std-format]
150a199fb12SMike Crowe 
151a199fb12SMike Crowe   // Needs a __PRI prefix so that we get as far as looking for where the macro comes from
152a199fb12SMike Crowe   auto s11 = absl::StrFormat(" macro from command line %" __PRI_CMDLINE_MACRO, s);
153a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro '__PRI_CMDLINE_MACRO' [modernize-use-std-format]
154a199fb12SMike Crowe 
155a199fb12SMike Crowe   // We ought to be able to fix this since the macro surrounds the whole call
156a199fb12SMike Crowe   // and therefore can't change the format string independently. This is
157a199fb12SMike Crowe   // required to be able to fix calls inside Catch2 macros for example.
158a199fb12SMike Crowe #define SURROUND_ALL(x) x
159a199fb12SMike Crowe   auto s12 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation %" PRIu64, u64));
160a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:27: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
161a199fb12SMike Crowe   // CHECK-FIXES: auto s12 = SURROUND_ALL(std::format("Macro surrounding entire invocation {}", u64));
162a199fb12SMike Crowe 
163a199fb12SMike Crowe   // But having that surrounding macro shouldn't stop us ignoring an
164a199fb12SMike Crowe   // unreplaceable macro elsewhere.
165a199fb12SMike Crowe   auto s13 = SURROUND_ALL(absl::StrFormat("Macro surrounding entire invocation with unreplaceable macro %" PRI_FMT_MACRO, s));
166a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:27: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'PRI_FMT_MACRO' [modernize-use-std-format]
167a199fb12SMike Crowe 
168a199fb12SMike Crowe   // At the moment at least the check will replace occurrences where the
169a199fb12SMike Crowe   // function name is the result of expanding a macro.
170a199fb12SMike Crowe #define SURROUND_FUNCTION_NAME(x) absl:: x
171a199fb12SMike Crowe   auto s14 = SURROUND_FUNCTION_NAME(StrFormat)("Hello %d", 4442);
172a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:14: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
173a199fb12SMike Crowe   // CHECK-FIXES: auto s14 = std::format("Hello {}", 4442);
174a199fb12SMike Crowe 
175a199fb12SMike Crowe   // We can't safely fix occurrences where the macro may affect the format
176a199fb12SMike Crowe   // string differently in different builds.
177a199fb12SMike Crowe #define SURROUND_FORMAT(x) "!" x
178a199fb12SMike Crowe   auto s15 = absl::StrFormat(SURROUND_FORMAT("Hello %d"), 4443);
179a199fb12SMike Crowe   // CHECK-MESSAGES: [[@LINE-1]]:14: warning: unable to use 'std::format' instead of 'StrFormat' because format string contains unreplaceable macro 'SURROUND_FORMAT' [modernize-use-std-format]
180af79372dSMike Crowe }
181