xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-format.cpp (revision af79372d6349cfba6beff26d54b7ad1b798fc4d5)
1 // RUN: %check_clang_tidy \
2 // RUN:   -std=c++20 %s modernize-use-std-format %t -- \
3 // RUN:   -config="{CheckOptions: {StrictMode: true}}" \
4 // RUN:   -- -isystem %clang_tidy_headers
5 // RUN: %check_clang_tidy \
6 // RUN:   -std=c++20 %s modernize-use-std-format %t -- \
7 // RUN:   -config="{CheckOptions: {StrictMode: false}}" \
8 // RUN:   -- -isystem %clang_tidy_headers
9 #include <string>
10 // CHECK-FIXES: #include <format>
11 
12 namespace absl
13 {
14 // Use const char * for the format since the real type is hard to mock up.
15 template <typename... Args>
16 std::string StrFormat(const char *format, const Args&... args);
17 } // namespace absl
18 
19 template <typename T>
20 struct iterator {
21   T *operator->();
22   T &operator*();
23 };
24 
25 std::string StrFormat_simple() {
26   return absl::StrFormat("Hello");
27   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
28   // CHECK-FIXES: return std::format("Hello");
29 }
30 
31 std::string StrFormat_complex(const char *name, double value) {
32   return absl::StrFormat("'%s'='%f'", name, value);
33   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
34   // CHECK-FIXES: return std::format("'{}'='{:f}'", name, value);
35 }
36 
37 std::string StrFormat_integer_conversions() {
38   return absl::StrFormat("int:%d int:%d char:%c char:%c", 65, 'A', 66, 'B');
39   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
40   // CHECK-FIXES: return std::format("int:{} int:{:d} char:{:c} char:{}", 65, 'A', 66, 'B');
41 }
42 
43 // FormatConverter is capable of removing newlines from the end of the format
44 // string. Ensure that isn't incorrectly happening for std::format.
45 std::string StrFormat_no_newline_removal() {
46   return absl::StrFormat("a line\n");
47   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
48   // CHECK-FIXES: return std::format("a line\n");
49 }
50 
51 // FormatConverter is capable of removing newlines from the end of the format
52 // string. Ensure that isn't incorrectly happening for std::format.
53 std::string StrFormat_cstr_removal(const std::string &s1, const std::string *s2) {
54   return absl::StrFormat("%s %s %s %s", s1.c_str(), s1.data(), s2->c_str(), s2->data());
55   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
56   // CHECK-FIXES: return std::format("{} {} {} {}", s1, s1, *s2, *s2);
57 }
58 
59 std::string StrFormat_strict_conversion() {
60   const unsigned char uc = 'A';
61   return absl::StrFormat("Integer %hhd from unsigned char\n", uc);
62   // CHECK-MESSAGES: [[@LINE-1]]:10: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
63   // CHECK-FIXES: return std::format("Integer {} from unsigned char\n", uc);
64 }
65 
66 std::string StrFormat_field_width_and_precision() {
67   auto s1 = absl::StrFormat("width only:%*d width and precision:%*.*f precision only:%.*f", 3, 42, 4, 2, 3.14159265358979323846, 5, 2.718);
68   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
69   // CHECK-FIXES: std::format("width only:{:{}} width and precision:{:{}.{}f} precision only:{:.{}f}", 42, 3, 3.14159265358979323846, 4, 2, 2.718, 5);
70 
71   auto s2 = absl::StrFormat("width and precision positional:%1$*2$.*3$f after", 3.14159265358979323846, 4, 2);
72   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
73   // CHECK-FIXES: std::format("width and precision positional:{0:{1}.{2}f} after", 3.14159265358979323846, 4, 2);
74 
75   const int width = 10, precision = 3;
76   const unsigned int ui1 = 42, ui2 = 43, ui3 = 44;
77   auto s3 = absl::StrFormat("casts width only:%*d width and precision:%*.*d precision only:%.*d\n", 3, ui1, 4, 2, ui2, 5, ui3);
78   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
79   // CHECK-FIXES-NOTSTRICT: std::format("casts width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", ui1, 3, ui2, 4, 2, ui3, 5);
80   // 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);
81 
82   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());
83   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
84   // CHECK-FIXES: std::format("c_str removal width only:{:>{}} width and precision:{:>{}.{}} precision only:{:.{}}", s1, 3, s2, 4, 2, s3, 5);
85 
86   const std::string *ps1 = &s1, *ps2 = &s2, *ps3 = &s3;
87   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());
88   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
89   // CHECK-FIXES: std::format("c_str() removal pointer width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *ps1, 3, *ps2, 4, 2, *ps3, 5);
90 
91   iterator<std::string> is1, is2, is3;
92   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());
93   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
94   // CHECK-FIXES: std::format("c_str() removal iterator width only:{:{}} width and precision:{:{}.{}} precision only:{:.{}}", *is1, 3, *is2, 4, 2, *is3, 5);
95 
96   return s1 + s2 + s3 + s4 + s5 + s6;
97 }
98 
99 std::string StrFormat_macros() {
100   // The function call is replaced even though it comes from a macro.
101 #define FORMAT absl::StrFormat
102   auto s1 = FORMAT("Hello %d", 42);
103   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
104   // CHECK-FIXES: std::format("Hello {}", 42);
105 
106   // The format string is replaced even though it comes from a macro, this
107   // behaviour is required so that that <inttypes.h> macros are replaced.
108 #define FORMAT_STRING "Hello %s"
109   auto s2 = absl::StrFormat(FORMAT_STRING, 42);
110   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
111   // CHECK-FIXES: std::format("Hello {}", 42);
112 
113   // Arguments that are macros aren't replaced with their value, even if they are rearranged.
114 #define VALUE 3.14159265358979323846
115 #define WIDTH 10
116 #define PRECISION 4
117   auto s3 = absl::StrFormat("Hello %*.*f", WIDTH, PRECISION, VALUE);
118   // CHECK-MESSAGES: [[@LINE-1]]:13: warning: use 'std::format' instead of 'StrFormat' [modernize-use-std-format]
119   // CHECK-FIXES: std::format("Hello {:{}.{}f}", VALUE, WIDTH, PRECISION);
120 }
121