// RUN: %check_clang_tidy %s abseil-redundant-strcat-calls %t -- -- -isystem %clang_tidy_headers #include int strlen(const char *); namespace absl { class string_view { public: typedef std::char_traits traits_type; string_view(); string_view(const char *); string_view(const std::string &); string_view(const char *, int); string_view(string_view, int); template explicit operator std::basic_string() const; const char *data() const; int size() const; int length() const; }; bool operator==(string_view A, string_view B); struct AlphaNum { AlphaNum(int i); AlphaNum(double f); AlphaNum(const char *c_str); AlphaNum(const std::string &str); AlphaNum(const string_view &pc); private: AlphaNum(const AlphaNum &); AlphaNum &operator=(const AlphaNum &); }; std::string StrCat(); std::string StrCat(const AlphaNum &A); std::string StrCat(const AlphaNum &A, const AlphaNum &B); std::string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C); std::string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, const AlphaNum &D); // Support 5 or more arguments template std::string StrCat(const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, const AlphaNum &D, const AlphaNum &E, const AV &... args); void StrAppend(std::string *Dest, const AlphaNum &A); void StrAppend(std::string *Dest, const AlphaNum &A, const AlphaNum &B); void StrAppend(std::string *Dest, const AlphaNum &A, const AlphaNum &B, const AlphaNum &C); void StrAppend(std::string *Dest, const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, const AlphaNum &D); // Support 5 or more arguments template void StrAppend(std::string *Dest, const AlphaNum &A, const AlphaNum &B, const AlphaNum &C, const AlphaNum &D, const AlphaNum &E, const AV &... args); } // namespace absl using absl::AlphaNum; using absl::StrAppend; using absl::StrCat; void Positives() { std::string S = StrCat(1, StrCat("A", StrCat(1.1))); // CHECK-MESSAGES: [[@LINE-1]]:19: warning: multiple calls to 'absl::StrCat' can be flattened into a single call // CHECK-FIXES: string S = StrCat(1, "A", 1.1); S = StrCat(StrCat(StrCat(StrCat(StrCat(1))))); // CHECK-MESSAGES: [[@LINE-1]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call // CHECK-FIXES: S = StrCat(1); // TODO: should trigger. The issue here is that in the current // implementation we ignore any StrCat with StrCat ancestors. Therefore // inserting anything in between calls will disable triggering the deepest // ones. // s = StrCat(Identity(StrCat(StrCat(1, 2), StrCat(3, 4)))); StrAppend(&S, 001, StrCat(1, 2, "3"), StrCat("FOO")); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call // CHECK-FIXES: StrAppend(&S, 001, 1, 2, "3", "FOO"); StrAppend(&S, 001, StrCat(StrCat(1, 2), "3"), StrCat("FOO")); // CHECK-MESSAGES: [[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call // CHECK-FIXES: StrAppend(&S, 001, 1, 2, "3", "FOO"); // Too many args. Ignore for now. S = StrCat(1, 2, StrCat(3, 4, 5, 6, 7), 8, 9, 10, StrCat(11, 12, 13, 14, 15, 16, 17, 18), 19, 20, 21, 22, 23, 24, 25, 26, 27); // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: multiple calls to 'absl::StrCat' can be flattened into a single call StrAppend(&S, StrCat(1, 2, 3, 4, 5), StrCat(6, 7, 8, 9, 10)); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call // CHECK-FIXES: StrAppend(&S, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); StrCat(1, StrCat()); // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: multiple calls to 'absl::StrCat' can be flattened into a single call } void Negatives() { // One arg. It is used for conversion. Ignore. std::string S = StrCat(1); #define A_MACRO(x, y, z) StrCat(x, y, z) S = A_MACRO(1, 2, StrCat("A", "B")); }