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