[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