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 the first redundant 'c_str()' call in parameters passed to + // std::print and std::format. + Finder->addMatcher( + traverse(TK_AsIs, + callExpr(anyOf(callee(functionDecl(hasName("::std::print"))), + callee(functionDecl(hasName("::std::format")))), + hasAnyArgument(materializeTemporaryExpr( + has(StringCStrCallExpr))))), + 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,12 @@ 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``. Note that only the first + such argument is currently reported so it may be necessary to run the + check multiple times to fix all of them. Removed checks ^^^^^^^^^^^^^^ Index: clang-tools-extra/docs/clang-tidy/checks/readability/redundant-string-cstr.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/readability/redundant-string-cstr.rst +++ clang-tools-extra/docs/clang-tidy/checks/readability/redundant-string-cstr.rst @@ -5,3 +5,7 @@ Finds unnecessary calls to ``std::string::c_str()`` and ``std::string::data()``. + +Only the first such call in an argument to ``std::print`` or +``std::format`` will be detected, so it may be necessary to run the check +more than once when applying fixes. 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 @@ -291,3 +291,59 @@ Foo.func2((Str.c_str())); } } // namespace PR45286 + +namespace std { + template + void print(const char *, Args &&...); + template + std::string format(const char *, Args &&...); +} + +namespace notstd { + template + void print(const char *, Args &&...); + template + std::string format(const char *, Args &&...); +} + +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); + + // Ideally we'd fix both the second and fourth parameters here, but that doesn't work. + std::print("One:{} Two:{} Three:{}\n", s1.c_str(), s2, s3.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}std::print("One:{} Two:{} Three:{}\n", s1, s2, s3.c_str()); +} + +// 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); +} + +// 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()); +} + +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:{}\n", s1.c_str(), s2, s3.c_str()); + // CHECK-MESSAGES: :[[@LINE-1]]:53: warning: redundant call to 'c_str' [readability-redundant-string-cstr] + // CHECK-FIXES: {{^ }}auto r2 = std::format("One:{} Two:{} Three:{}\n", s1, s2, s3.c_str()); +} + +// 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); +} + +// 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()); +}