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,14 @@ // directly. hasArgument(0, StringCStrCallExpr))), this); + + 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/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()); +}