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