| 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 | |