1 // RUN: %check_clang_tidy %s abseil-redundant-strcat-calls %t
2
3 int strlen(const char *);
4
5 // Here we mimic the hierarchy of ::string.
6 // We need to do so because we are matching on the fully qualified name of the
7 // methods.
8 struct __sso_string_base {};
9 namespace __gnu_cxx {
10 template <typename A, typename B, typename C, typename D = __sso_string_base>
11 class __versa_string {
12 public:
13 const char *c_str() const;
14 const char *data() const;
15 int size() const;
16 int capacity() const;
17 int length() const;
18 bool empty() const;
19 char &operator[](int);
20 void clear();
21 void resize(int);
22 int compare(const __versa_string &) const;
23 };
24 } // namespace __gnu_cxx
25
26 namespace std {
27 template <typename T>
28 class char_traits {};
29 template <typename T>
30 class allocator {};
31 } // namespace std
32
33 template <typename A, typename B = std::char_traits<A>,
34 typename C = std::allocator<A>>
35 class basic_string : public __gnu_cxx::__versa_string<A, B, C> {
36 public:
37 basic_string();
38 basic_string(const basic_string &);
39 basic_string(const char *, C = C());
40 basic_string(const char *, int, C = C());
41 basic_string(const basic_string &, int, int, C = C());
42 ~basic_string();
43
44 basic_string &operator+=(const basic_string &);
45 };
46
47 template <typename A, typename B, typename C>
48 basic_string<A, B, C> operator+(const basic_string<A, B, C> &,
49 const basic_string<A, B, C> &);
50 template <typename A, typename B, typename C>
51 basic_string<A, B, C> operator+(const basic_string<A, B, C> &, const char *);
52
53 typedef basic_string<char> string;
54
55 bool operator==(const string &, const string &);
56 bool operator==(const string &, const char *);
57 bool operator==(const char *, const string &);
58
59 bool operator!=(const string &, const string &);
60 bool operator<(const string &, const string &);
61 bool operator>(const string &, const string &);
62 bool operator<=(const string &, const string &);
63 bool operator>=(const string &, const string &);
64
65 namespace std {
66 template <typename _CharT, typename _Traits = char_traits<_CharT>,
67 typename _Alloc = allocator<_CharT>>
68 class basic_string;
69
70 template <typename _CharT, typename _Traits, typename _Alloc>
71 class basic_string {
72 public:
73 basic_string();
74 basic_string(const basic_string &);
75 basic_string(const char *, const _Alloc & = _Alloc());
76 basic_string(const char *, int, const _Alloc & = _Alloc());
77 basic_string(const basic_string &, int, int, const _Alloc & = _Alloc());
78 ~basic_string();
79
80 basic_string &operator+=(const basic_string &);
81
82 unsigned size() const;
83 unsigned length() const;
84 bool empty() const;
85 };
86
87 typedef basic_string<char> string;
88 } // namespace std
89
90 namespace absl {
91
92 class string_view {
93 public:
94 typedef std::char_traits<char> traits_type;
95
96 string_view();
97 string_view(const char *);
98 string_view(const string &);
99 string_view(const char *, int);
100 string_view(string_view, int);
101
102 template <typename A>
103 explicit operator ::basic_string<char, traits_type, A>() const;
104
105 const char *data() const;
106 int size() const;
107 int length() const;
108 };
109
110 bool operator==(string_view A, string_view B);
111
112 struct AlphaNum {
113 AlphaNum(int i);
114 AlphaNum(double f);
115 AlphaNum(const char *c_str);
116 AlphaNum(const string &str);
117 AlphaNum(const string_view &pc);
118
119 private:
120 AlphaNum(const AlphaNum &);
121 AlphaNum &operator=(const AlphaNum &);
122 };
123
124 string StrCat();
125 string StrCat(const AlphaNum &A);
126 string StrCat(const AlphaNum &A, const AlphaNum &B);
127 string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C);
128 string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C,
129 const AlphaNum &D);
130
131 // Support 5 or more arguments
132 template <typename... AV>
133 string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C,
134 const AlphaNum &D, const AlphaNum &E, const AV &... args);
135
136 void StrAppend(string *Dest, const AlphaNum &A);
137 void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B);
138 void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B,
139 const AlphaNum &C);
140 void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B,
141 const AlphaNum &C, const AlphaNum &D);
142
143 // Support 5 or more arguments
144 template <typename... AV>
145 void StrAppend(string *Dest, const AlphaNum &A, const AlphaNum &B,
146 const AlphaNum &C, const AlphaNum &D, const AlphaNum &E,
147 const AV &... args);
148
149 } // namespace absl
150
151 using absl::AlphaNum;
152 using absl::StrAppend;
153 using absl::StrCat;
154
Positives()155 void Positives() {
156 string S = StrCat(1, StrCat("A", StrCat(1.1)));
157 // CHECK-MESSAGES: [[@LINE-1]]:14: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
158 // CHECK-FIXES: string S = StrCat(1, "A", 1.1);
159
160 S = StrCat(StrCat(StrCat(StrCat(StrCat(1)))));
161 // CHECK-MESSAGES: [[@LINE-1]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
162 // CHECK-FIXES: S = StrCat(1);
163
164 // TODO: should trigger. The issue here is that in the current
165 // implementation we ignore any StrCat with StrCat ancestors. Therefore
166 // inserting anything in between calls will disable triggering the deepest
167 // ones.
168 // s = StrCat(Identity(StrCat(StrCat(1, 2), StrCat(3, 4))));
169
170 StrAppend(&S, 001, StrCat(1, 2, "3"), StrCat("FOO"));
171 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
172 // CHECK-FIXES: StrAppend(&S, 001, 1, 2, "3", "FOO");
173
174 StrAppend(&S, 001, StrCat(StrCat(1, 2), "3"), StrCat("FOO"));
175 // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
176 // CHECK-FIXES: StrAppend(&S, 001, 1, 2, "3", "FOO");
177
178 // Too many args. Ignore for now.
179 S = StrCat(1, 2, StrCat(3, 4, 5, 6, 7), 8, 9, 10,
180 StrCat(11, 12, 13, 14, 15, 16, 17, 18), 19, 20, 21, 22, 23, 24, 25,
181 26, 27);
182 // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
183 StrAppend(&S, StrCat(1, 2, 3, 4, 5), StrCat(6, 7, 8, 9, 10));
184 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
185 // CHECK-FIXES: StrAppend(&S, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
186
187 StrCat(1, StrCat());
188 // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call
189 }
190
Negatives()191 void Negatives() {
192 // One arg. It is used for conversion. Ignore.
193 string S = StrCat(1);
194
195 #define A_MACRO(x, y, z) StrCat(x, y, z)
196 S = A_MACRO(1, 2, StrCat("A", "B"));
197 }
198