[clang-tools-extra] r340915 - Introduce the abseil-str-cat-append check.
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 29 04:17:31 PDT 2018
Author: aaronballman
Date: Wed Aug 29 04:17:31 2018
New Revision: 340915
URL: http://llvm.org/viewvc/llvm-project?rev=340915&view=rev
Log:
Introduce the abseil-str-cat-append check.
This flags uses of absl::StrCat when absl::StrAppend should be used instead. Patch by Hugo Gonzalez and Benjamin Kramer.
Added:
clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.cpp
clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.h
clang-tools-extra/trunk/docs/clang-tidy/checks/abseil-str-cat-append.rst
clang-tools-extra/trunk/test/clang-tidy/abseil-str-cat-append.cpp
Modified:
clang-tools-extra/trunk/clang-tidy/abseil/AbseilTidyModule.cpp
clang-tools-extra/trunk/clang-tidy/abseil/CMakeLists.txt
clang-tools-extra/trunk/docs/ReleaseNotes.rst
clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
Modified: clang-tools-extra/trunk/clang-tidy/abseil/AbseilTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/abseil/AbseilTidyModule.cpp?rev=340915&r1=340914&r2=340915&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/abseil/AbseilTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/abseil/AbseilTidyModule.cpp Wed Aug 29 04:17:31 2018
@@ -14,6 +14,7 @@
#include "FasterStrsplitDelimiterCheck.h"
#include "NoNamespaceCheck.h"
#include "StringFindStartswithCheck.h"
+#include "StrCatAppendCheck.h"
namespace clang {
namespace tidy {
@@ -29,6 +30,8 @@ public:
CheckFactories.registerCheck<NoNamespaceCheck>("abseil-no-namespace");
CheckFactories.registerCheck<StringFindStartswithCheck>(
"abseil-string-find-startswith");
+ CheckFactories.registerCheck<StrCatAppendCheck>(
+ "abseil-str-cat-append");
}
};
Modified: clang-tools-extra/trunk/clang-tidy/abseil/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/abseil/CMakeLists.txt?rev=340915&r1=340914&r2=340915&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/abseil/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/abseil/CMakeLists.txt Wed Aug 29 04:17:31 2018
@@ -6,6 +6,7 @@ add_clang_library(clangTidyAbseilModule
FasterStrsplitDelimiterCheck.cpp
NoNamespaceCheck.cpp
StringFindStartswithCheck.cpp
+ StrCatAppendCheck.cpp
LINK_LIBS
clangAST
Added: clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.cpp?rev=340915&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.cpp Wed Aug 29 04:17:31 2018
@@ -0,0 +1,102 @@
+//===--- StrCatAppendCheck.cpp - clang-tidy--------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StrCatAppendCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+namespace {
+// Skips any combination of temporary materialization, temporary binding and
+// implicit casting.
+AST_MATCHER_P(Stmt, IgnoringTemporaries, ast_matchers::internal::Matcher<Stmt>,
+ InnerMatcher) {
+ const Stmt *E = &Node;
+ while (true) {
+ if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
+ E = MTE->getTemporary();
+ if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
+ E = BTE->getSubExpr();
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E))
+ E = ICE->getSubExpr();
+ else
+ break;
+ }
+
+ return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+} // namespace
+
+// TODO: str += StrCat(...)
+// str.append(StrCat(...))
+
+void StrCatAppendCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ const auto StrCat = functionDecl(hasName("::absl::StrCat"));
+ // The arguments of absl::StrCat are implicitly converted to AlphaNum. This
+ // matches to the arguments because of that behavior.
+ const auto AlphaNum = IgnoringTemporaries(cxxConstructExpr(
+ argumentCountIs(1), hasType(cxxRecordDecl(hasName("::absl::AlphaNum"))),
+ hasArgument(0, ignoringImpCasts(declRefExpr(to(equalsBoundNode("LHS")),
+ expr().bind("Arg0"))))));
+
+ const auto HasAnotherReferenceToLhs =
+ callExpr(hasAnyArgument(expr(hasDescendant(declRefExpr(
+ to(equalsBoundNode("LHS")), unless(equalsBoundNode("Arg0")))))));
+
+ // Now look for calls to operator= with an object on the LHS and a call to
+ // StrCat on the RHS. The first argument of the StrCat call should be the same
+ // as the LHS. Ignore calls from template instantiations.
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ unless(isInTemplateInstantiation()), hasOverloadedOperatorName("="),
+ hasArgument(0, declRefExpr(to(decl().bind("LHS")))),
+ hasArgument(1, IgnoringTemporaries(
+ callExpr(callee(StrCat), hasArgument(0, AlphaNum),
+ unless(HasAnotherReferenceToLhs))
+ .bind("Call"))))
+ .bind("Op"),
+ this);
+}
+
+void StrCatAppendCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Op = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("Op");
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("Call");
+ assert(Op != nullptr && Call != nullptr && "Matcher does not work as expected");
+
+ // Handles the case 'x = absl::StrCat(x)', which has no effect.
+ if (Call->getNumArgs() == 1) {
+ diag(Op->getBeginLoc(), "call to 'absl::StrCat' has no effect");
+ return;
+ }
+
+ // Emit a warning and emit fixits to go from
+ // x = absl::StrCat(x, ...)
+ // to
+ // absl::StrAppend(&x, ...)
+ diag(Op->getBeginLoc(),
+ "call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a "
+ "string to avoid a performance penalty")
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Op->getBeginLoc(),
+ Call->getCallee()->getEndLoc()),
+ "StrAppend")
+ << FixItHint::CreateInsertion(Call->getArg(0)->getBeginLoc(), "&");
+}
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
Added: clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.h?rev=340915&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/abseil/StrCatAppendCheck.h Wed Aug 29 04:17:31 2018
@@ -0,0 +1,36 @@
+//===--- StrCatAppendCheck.h - clang-tidy------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+/// Flags uses of absl::StrCat to append to a string. Suggests absl::StrAppend
+/// should be used instead.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-str-cat-append.html
+class StrCatAppendCheck : public ClangTidyCheck {
+public:
+ StrCatAppendCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H
Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=340915&r1=340914&r2=340915&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Wed Aug 29 04:17:31 2018
@@ -76,6 +76,12 @@ Improvements to clang-tidy
Ensures code does not open ``namespace absl`` as that violates Abseil's
compatibility guidelines.
+- New :doc:`abseil-str-cat-append
+ <clang-tidy/checks/abseil-str-cat-append>` check.
+
+ Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests
+ ``absl::StrAppend()`` should be used instead.
+
- New :doc:`readability-magic-numbers
<clang-tidy/checks/readability-magic-numbers>` check.
Added: clang-tools-extra/trunk/docs/clang-tidy/checks/abseil-str-cat-append.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/abseil-str-cat-append.rst?rev=340915&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/abseil-str-cat-append.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/abseil-str-cat-append.rst Wed Aug 29 04:17:31 2018
@@ -0,0 +1,17 @@
+.. title:: clang-tidy - abseil-str-cat-append
+
+abseil-str-cat-append
+=====================
+
+Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests
+``absl::StrAppend()`` should be used instead.
+
+The extra calls cause unnecessary temporary strings to be constructed. Removing
+them makes the code smaller and faster.
+
+.. code-block:: c++
+
+ a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead.
+
+Does not diagnose cases where ``abls::StrCat()`` is used as a template
+argument for a functor.
Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst?rev=340915&r1=340914&r2=340915&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Wed Aug 29 04:17:31 2018
@@ -8,6 +8,7 @@ Clang-Tidy Checks
abseil-faster-strsplit-delimiter
abseil-no-namespace
abseil-string-find-startswith
+ abseil-str-cat-append
android-cloexec-accept
android-cloexec-accept4
android-cloexec-creat
Added: clang-tools-extra/trunk/test/clang-tidy/abseil-str-cat-append.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/abseil-str-cat-append.cpp?rev=340915&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/abseil-str-cat-append.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/abseil-str-cat-append.cpp Wed Aug 29 04:17:31 2018
@@ -0,0 +1,129 @@
+// RUN: %check_clang_tidy %s abseil-str-cat-append %t -- -- -I%S -std=c++11
+
+typedef unsigned __INT16_TYPE__ char16;
+typedef unsigned __INT32_TYPE__ char32;
+typedef __SIZE_TYPE__ size;
+
+namespace std {
+template <typename T>
+class allocator {};
+template <typename T>
+class char_traits {};
+template <typename C, typename T, typename A>
+struct basic_string {
+ typedef basic_string<C, T, A> _Type;
+ basic_string();
+ basic_string(const C* p, const A& a = A());
+
+ const C* c_str() const;
+ const C* data() const;
+
+ _Type& append(const C* s);
+ _Type& append(const C* s, size n);
+ _Type& assign(const C* s);
+ _Type& assign(const C* s, size n);
+
+ int compare(const _Type&) const;
+ int compare(const C* s) const;
+ int compare(size pos, size len, const _Type&) const;
+ int compare(size pos, size len, const C* s) const;
+
+ size find(const _Type& str, size pos = 0) const;
+ size find(const C* s, size pos = 0) const;
+ size find(const C* s, size pos, size n) const;
+
+ _Type& insert(size pos, const _Type& str);
+ _Type& insert(size pos, const C* s);
+ _Type& insert(size pos, const C* s, size n);
+
+ _Type& operator+=(const _Type& str);
+ _Type& operator+=(const C* s);
+ _Type& operator=(const _Type& str);
+ _Type& operator=(const C* s);
+};
+
+typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string;
+typedef basic_string<wchar_t, std::char_traits<wchar_t>,
+ std::allocator<wchar_t>>
+ wstring;
+typedef basic_string<char16, std::char_traits<char16>, std::allocator<char16>>
+ u16string;
+typedef basic_string<char32, std::char_traits<char32>, std::allocator<char32>>
+ u32string;
+} // namespace std
+
+std::string operator+(const std::string&, const std::string&);
+std::string operator+(const std::string&, const char*);
+std::string operator+(const char*, const std::string&);
+
+bool operator==(const std::string&, const std::string&);
+bool operator==(const std::string&, const char*);
+bool operator==(const char*, const std::string&);
+
+namespace llvm {
+struct StringRef {
+ StringRef(const char* p);
+ StringRef(const std::string&);
+};
+} // namespace llvm
+
+namespace absl {
+
+struct AlphaNum {
+ AlphaNum(int i);
+ AlphaNum(double f);
+ AlphaNum(const char* c_str);
+ AlphaNum(const std::string& str);
+
+ private:
+ AlphaNum(const AlphaNum&);
+ AlphaNum& operator=(const AlphaNum&);
+};
+
+std::string StrCat(const AlphaNum& A);
+std::string StrCat(const AlphaNum& A, const AlphaNum& B);
+
+template <typename A>
+void Foo(A& a) {
+ a = StrCat(a);
+}
+
+void Bar() {
+ std::string A, B;
+ Foo<std::string>(A);
+
+ std::string C = StrCat(A);
+ A = StrCat(A);
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call to 'absl::StrCat' has no effect
+ A = StrCat(A, B);
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty
+// CHECK-FIXES: {{^}} StrAppend(&A, B);
+ B = StrCat(A, B);
+
+#define M(X) X = StrCat(X, A)
+ M(B);
+// CHECK-MESSAGES: [[@LINE-1]]:5: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty
+// CHECK-FIXES: #define M(X) X = StrCat(X, A)
+}
+
+void Regression_SelfAppend() {
+ std::string A;
+ A = StrCat(A, A);
+}
+
+} // namespace absl
+
+void OutsideAbsl() {
+ std::string A, B;
+ A = absl::StrCat(A, B);
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty
+// CHECK-FIXES: {{^}} StrAppend(&A, B);
+}
+
+void OutisdeUsingAbsl() {
+ std::string A, B;
+ using absl::StrCat;
+ A = StrCat(A, B);
+// CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty
+// CHECK-FIXES: {{^}} StrAppend(&A, B);
+}
More information about the cfe-commits
mailing list