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 | |
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 |
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. |
44 | void not_fmt_print(const std::string &s1) { |
45 | notfmt::print("One: {}\n" , s1.c_str()); |
46 | } |
47 | |
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 |
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 |
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> |
72 | void operator()(const char *fmt, Args &&...args) { |
73 | } |
74 | |
75 | template <typename... 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 | |
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 | |
111 | void logger2(const std::string &s1, const std::string &s2) { |
112 | BaseLogger LOGGER3; |
113 | |
114 | LOGGER3.Log(fmt: "%s\n" , args: s1.c_str(), args: 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(fmt: "%d %s\n" , args: 42, args: s1.c_str(), args: 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> |
129 | void operator()(const char *fmt, Args &&...args) { |
130 | } |
131 | |
132 | template <typename... Args> |
133 | void Log(const char *fmt, Args &&...args) { |
134 | } |
135 | }; |
136 | |
137 | void Log(const char *fmt, ...); |
138 | |
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(fmt: "%s\n" , args: s1.c_str()); |
145 | |
146 | // Free function not in StringParameterFunctions list |
147 | Log(fmt: "%s\n" , s1.c_str()); |
148 | } |
149 | |