1 // RUN: %check_clang_tidy %s readability-redundant-string-cstr %t -- \
2 // RUN: -config="{CheckOptions: \
3 // RUN: {readability-redundant-string-cstr.StringParameterFunctions: \
4 // RUN: '::fmt::format; ::fmt::print; ::BaseLogger::operator(); ::BaseLogger::Log'} \
5 // RUN: }" \
6 // RUN: -- -isystem %clang_tidy_headers
7 #include <string>
8
9 namespace fmt {
10 inline namespace v8 {
11 template<typename ...Args>
12 void print(const char *, Args &&...);
13 template<typename ...Args>
14 std::string format(const char *, Args &&...);
15 }
16 }
17
18 namespace notfmt {
19 inline namespace v8 {
20 template<typename ...Args>
21 void print(const char *, Args &&...);
22 template<typename ...Args>
23 std::string format(const char *, Args &&...);
24 }
25 }
26
fmt_print(const std::string & s1,const std::string & s2,const std::string & s3)27 void fmt_print(const std::string &s1, const std::string &s2, const std::string &s3) {
28 fmt::print("One:{}\n", s1.c_str());
29 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
30 // CHECK-FIXES: {{^ }}fmt::print("One:{}\n", s1);
31
32 fmt::print("One:{} Two:{} Three:{}\n", s1.c_str(), s2, s3.c_str());
33 // CHECK-MESSAGES: :[[@LINE-1]]:42: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
34 // CHECK-MESSAGES: :[[@LINE-2]]:58: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
35 // CHECK-FIXES: {{^ }}fmt::print("One:{} Two:{} Three:{}\n", s1, s2, s3);
36 }
37
38 // There's no c_str() call here, so it shouldn't be touched
fmt_print_no_cstr(const std::string & s1,const std::string & s2)39 void fmt_print_no_cstr(const std::string &s1, const std::string &s2) {
40 fmt::print("One: {}, Two: {}\n", s1, s2);
41 }
42
43 // This isn't fmt::print, so it shouldn't be fixed.
not_fmt_print(const std::string & s1)44 void not_fmt_print(const std::string &s1) {
45 notfmt::print("One: {}\n", s1.c_str());
46 }
47
fmt_format(const std::string & s1,const std::string & s2,const std::string & s3)48 void fmt_format(const std::string &s1, const std::string &s2, const std::string &s3) {
49 auto r1 = fmt::format("One:{}\n", s1.c_str());
50 // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
51 // CHECK-FIXES: {{^ }}auto r1 = fmt::format("One:{}\n", s1);
52
53 auto r2 = fmt::format("One:{} Two:{} Three:{}\n", s1.c_str(), s2, s3.c_str());
54 // CHECK-MESSAGES: :[[@LINE-1]]:53: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
55 // CHECK-MESSAGES: :[[@LINE-2]]:69: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
56 // CHECK-FIXES: {{^ }}auto r2 = fmt::format("One:{} Two:{} Three:{}\n", s1, s2, s3);
57 }
58
59 // There's are c_str() calls here, so it shouldn't be touched
fmt_format_no_cstr(const std::string & s1,const std::string & s2)60 void fmt_format_no_cstr(const std::string &s1, const std::string &s2) {
61 fmt::format("One: {}, Two: {}\n", s1, s2);
62 }
63
64 // This is not fmt::format, so it shouldn't be fixed
not_fmt_format(const std::string & s1)65 std::string not_fmt_format(const std::string &s1) {
66 return notfmt::format("One: {}\n", s1.c_str());
67 }
68
69 class BaseLogger {
70 public:
71 template <typename... Args>
operator ()(const char * fmt,Args &&...args)72 void operator()(const char *fmt, Args &&...args) {
73 }
74
75 template <typename... Args>
Log(const char * fmt,Args &&...args)76 void Log(const char *fmt, Args &&...args) {
77 }
78 };
79
80 class DerivedLogger : public BaseLogger {};
81 class DoubleDerivedLogger : public DerivedLogger {};
82 typedef DerivedLogger TypedefDerivedLogger;
83
logger1(const std::string & s1,const std::string & s2,const std::string & s3)84 void logger1(const std::string &s1, const std::string &s2, const std::string &s3) {
85 BaseLogger LOGGER;
86
87 LOGGER("%s\n", s1.c_str(), s2, s3.c_str());
88 // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
89 // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
90 // CHECK-FIXES: {{^ }}LOGGER("%s\n", s1, s2, s3);
91
92 DerivedLogger LOGGER2;
93 LOGGER2("%d %s\n", 42, s1.c_str(), s2.c_str(), s3);
94 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
95 // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
96 // CHECK-FIXES: {{^ }}LOGGER2("%d %s\n", 42, s1, s2, s3);
97
98 DoubleDerivedLogger LOGGERD;
99 LOGGERD("%d %s\n", 42, s1.c_str(), s2, s3.c_str());
100 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
101 // CHECK-MESSAGES: :[[@LINE-2]]:42: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
102 // CHECK-FIXES: {{^ }}LOGGERD("%d %s\n", 42, s1, s2, s3);
103
104 TypedefDerivedLogger LOGGERT;
105 LOGGERT("%d %s\n", 42, s1.c_str(), s2, s3.c_str());
106 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
107 // CHECK-MESSAGES: :[[@LINE-2]]:42: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
108 // CHECK-FIXES: {{^ }}LOGGERT("%d %s\n", 42, s1, s2, s3);
109 }
110
logger2(const std::string & s1,const std::string & s2)111 void logger2(const std::string &s1, const std::string &s2) {
112 BaseLogger LOGGER3;
113
114 LOGGER3.Log("%s\n", s1.c_str(), s2.c_str());
115 // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
116 // CHECK-MESSAGES: :[[@LINE-2]]:35: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
117 // CHECK-FIXES: {{^ }}LOGGER3.Log("%s\n", s1, s2);
118
119 DerivedLogger LOGGER4;
120 LOGGER4.Log("%d %s\n", 42, s1.c_str(), s2.c_str());
121 // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
122 // CHECK-MESSAGES: :[[@LINE-2]]:42: warning: redundant call to 'c_str' [readability-redundant-string-cstr]
123 // CHECK-FIXES: {{^ }}LOGGER4.Log("%d %s\n", 42, s1, s2);
124 }
125
126 class NotLogger {
127 public:
128 template <typename... Args>
operator ()(const char * fmt,Args &&...args)129 void operator()(const char *fmt, Args &&...args) {
130 }
131
132 template <typename... Args>
Log(const char * fmt,Args &&...args)133 void Log(const char *fmt, Args &&...args) {
134 }
135 };
136
137 void Log(const char *fmt, ...);
138
logger3(const std::string & s1)139 void logger3(const std::string &s1)
140 {
141 // Not BaseLogger or something derived from it
142 NotLogger LOGGER;
143 LOGGER("%s\n", s1.c_str());
144 LOGGER.Log("%s\n", s1.c_str());
145
146 // Free function not in StringParameterFunctions list
147 Log("%s\n", s1.c_str());
148 }
149