Index: clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp +++ clang-tools-extra/clang-tidy/readability/RedundantStringCStrCheck.cpp @@ -178,6 +178,16 @@ // directly. hasArgument(0, StringCStrCallExpr))), this); + + // Detect redundant 'c_str()' calls in parameters passed to std::print and + // std::format. + Finder->addMatcher( + traverse( + TK_AsIs, + callExpr( + callee(functionDecl(hasAnyName("::std::print", "::std::format"))), + forEachArgumentWithParam(StringCStrCallExpr, parmVarDecl()))), + this); } void RedundantStringCStrCheck::check(const MatchFinder::MatchResult &Result) { Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -123,6 +123,10 @@ Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Improved :doc:`readability-redundant-string-cstr + ` check to recognise + unnecessary ``std::string::c_str()`` and ``std::string::data()`` calls in + arguments to ``std::print`` and ``std::format``. Removed checks ^^^^^^^^^^^^^^ Index: clang-tools-extra/test/clang-tidy/checkers/readability/redundant-string-cstr.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/readability/redundant-string-cstr.cpp +++ clang-tools-extra/test/clang-tidy/checkers/readability/redundant-string-cstr.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s readability-redundant-string-cstr %t +// RUN: %check_clang_tidy -std=c++20 %s readability-redundant-string-cstr %t typedef unsigned __INT16_TYPE__ char16; typedef unsigned __INT32_TYPE__ char32; @@ -51,7 +51,8 @@ template struct basic_string_view { - basic_string_view(const C* s); + const C *str; + constexpr basic_string_view(const C* s) : str(s) {} }; typedef basic_string_view> string_view; typedef basic_string_view> wstring_view; @@ -291,3 +292,199 @@ Foo.func2((Str.c_str())); } } // namespace PR45286 + +namespace std { + template + struct type_identity { using type = T; }; + template + using type_identity_t = typename type_identity::type; + + template + struct basic_format_string { + consteval basic_format_string(const CharT *format) : str(format) {} + basic_string_view> str; + }; + + template + using format_string = basic_format_string...>; + + template + using wformat_string = basic_format_string...>; + + template + void print(format_string, Args &&...); + template + void print(wformat_string, Args &&...); + + template + std::string format(format_string, Args &&...); + template + std::string format(wformat_string, Args &&...); +} + +namespace notstd { + template + void print(const char *, Args &&...); + template + void print(const wchar_t *, Args &&...); + template + std::string format(const char *, Args &&...); + template + std::string format(const wchar_t *, Args &&...); +} + +std::string return_temporary(); +std::wstring return_wtemporary(); + +void std_print(const std::string &s1, const std::string &s2, const std::string &s3) { + std::print("One:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}std::print("One:{}\n", s1); + + std::print("One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_temporary().c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:50: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-2]]:66: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-3]]:78: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}std::print("One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_temporary()); + + using namespace std; + print("Four:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}print("Four:{}\n", s1); +} + +void std_print_wide(const std::wstring &s1, const std::wstring &s2, const std::wstring &s3) { + std::print(L"One:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}std::print(L"One:{}\n", s1); + + std::print(L"One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_wtemporary().c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:51: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-2]]:67: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-3]]:79: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}std::print(L"One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_wtemporary()); + + using namespace std; + print(L"Four:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}print(L"Four:{}\n", s1); +} + +// There's no c_str() call here, so it shouldn't be touched. +void std_print_no_cstr(const std::string &s1, const std::string &s2) { + std::print("One: {}, Two: {}\n", s1, s2); +} + +// There's no c_str() call here, so it shouldn't be touched. +void std_print_no_cstr_wide(const std::wstring &s1, const std::wstring &s2) { + std::print(L"One: {}, Two: {}\n", s1, s2); +} + +// This isn't std::print, so it shouldn't be fixed. +void not_std_print(const std::string &s1) { + notstd::print("One: {}\n", s1.c_str()); + + using namespace notstd; + print("One: {}\n", s1.c_str()); +} + +// This isn't std::print, so it shouldn't be fixed. +void not_std_print_wide(const std::string &s1) { + notstd::print(L"One: {}\n", s1.c_str()); + + using namespace notstd; + print(L"One: {}\n", s1.c_str()); +} + +void std_format(const std::string &s1, const std::string &s2, const std::string &s3) { + auto r1 = std::format("One:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r1 = std::format("One:{}\n", s1); + + // Ideally we'd fix both the second and fourth parameters here, but that doesn't work. + auto r2 = std::format("One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_temporary().c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:61: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-2]]:77: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-3]]:89: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r2 = std::format("One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_temporary()); + + using namespace std; + auto r3 = format("Four:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:33: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r3 = format("Four:{}\n", s1); +} + +void std_format_wide(const std::wstring &s1, const std::wstring &s2, const std::wstring &s3) { + auto r1 = std::format(L"One:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r1 = std::format(L"One:{}\n", s1); + + // Ideally we'd fix both the second and fourth parameters here, but that doesn't work. + auto r2 = std::format(L"One:{} Two:{} Three:{} Four:{}\n", s1.c_str(), s2, s3.c_str(), return_wtemporary().c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:62: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-2]]:78: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-MESSAGES: :[[@LINE-3]]:90: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r2 = std::format(L"One:{} Two:{} Three:{} Four:{}\n", s1, s2, s3, return_wtemporary()); + + using namespace std; + auto r3 = format(L"Four:{}\n", s1.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r3 = format(L"Four:{}\n", s1); +} + +// There's are c_str() calls here, so it shouldn't be touched. +std::string std_format_no_cstr(const std::string &s1, const std::string &s2) { + return std::format("One: {}, Two: {}\n", s1, s2); +} + +// There's are c_str() calls here, so it shouldn't be touched. +std::string std_format_no_cstr_wide(const std::string &s1, const std::string &s2) { + return std::format(L"One: {}, Two: {}\n", s1, s2); +} + +// This is not std::format, so it shouldn't be fixed. +std::string not_std_format(const std::string &s1) { + return notstd::format("One: {}\n", s1.c_str()); + + using namespace notstd; + format("One: {}\n", s1.c_str()); +} + +// This is not std::format, so it shouldn't be fixed. +std::string not_std_format_wide(const std::string &s1) { + return notstd::format(L"One: {}\n", s1.c_str()); + + using namespace notstd; + format(L"One: {}\n", s1.c_str()); +} + +// We can't declare these earlier since they make the "using namespace std" +// tests ambiguous. +template +void print(const char *, Args &&...); +template +void print(const wchar_t *, Args &&...); +template +std::string format(const char *, Args &&...); +template +std::string format(const wchar_t *, Args &&...); + +// This isn't std::print, so it shouldn't be fixed. +void not_std_print2(const std::string &s1) { + print("One: {}\n", s1.c_str()); +} + +// This isn't std::print, so it shouldn't be fixed. +void not_std_print2_wide(const std::string &s1) { + print(L"One: {}\n", s1.c_str()); +} + +// This is not std::format, so it shouldn't be fixed. +std::string not_std_format2(const std::wstring &s1) { + return format("One: {}\n", s1.c_str()); +} + +// This is not std::format, so it shouldn't be fixed. +std::string not_std_format2_wide(const std::wstring &s1) { + return format(L"One: {}\n", s1.c_str()); +}