[clang-tools-extra] r287107 - [clang-tidy] New check to prefer transparent functors to non-transparent ones.

Gabor Horvath via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 16 06:42:11 PST 2016


Author: xazax
Date: Wed Nov 16 08:42:10 2016
New Revision: 287107

URL: http://llvm.org/viewvc/llvm-project?rev=287107&view=rev
Log:
[clang-tidy] New check to prefer transparent functors to non-transparent ones.

Added:
    clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
    clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.h
    clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-transparent-functors.rst
    clang-tools-extra/trunk/test/clang-tidy/modernize-use-transparent-functors.cpp
Modified:
    clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt
    clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst

Modified: clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt?rev=287107&r1=287106&r2=287107&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/clang-tidy/modernize/CMakeLists.txt Wed Nov 16 08:42:10 2016
@@ -21,6 +21,7 @@ add_clang_library(clangTidyModernizeModu
   UseEqualsDeleteCheck.cpp
   UseNullptrCheck.cpp
   UseOverrideCheck.cpp
+  UseTransparentFunctorsCheck.cpp
   UseUsingCheck.cpp
 
   LINK_LIBS

Modified: clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp?rev=287107&r1=287106&r2=287107&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/modernize/ModernizeTidyModule.cpp Wed Nov 16 08:42:10 2016
@@ -27,6 +27,7 @@
 #include "UseEqualsDeleteCheck.h"
 #include "UseNullptrCheck.h"
 #include "UseOverrideCheck.h"
+#include "UseTransparentFunctorsCheck.h"
 #include "UseUsingCheck.h"
 
 using namespace clang::ast_matchers;
@@ -61,6 +62,8 @@ public:
         "modernize-use-equals-delete");
     CheckFactories.registerCheck<UseNullptrCheck>("modernize-use-nullptr");
     CheckFactories.registerCheck<UseOverrideCheck>("modernize-use-override");
+    CheckFactories.registerCheck<UseTransparentFunctorsCheck>(
+        "modernize-use-transparent-functors");
     CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
   }
 

Added: clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp?rev=287107&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp (added)
+++ clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp Wed Nov 16 08:42:10 2016
@@ -0,0 +1,131 @@
+//===--- UseTransparentFunctorsCheck.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 "UseTransparentFunctorsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {}
+
+void UseTransparentFunctorsCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "SafeMode", SafeMode ? 1 : 0);
+}
+
+void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus14)
+    return;
+
+  const auto TransparentFunctors =
+      classTemplateSpecializationDecl(
+          unless(hasAnyTemplateArgument(refersToType(voidType()))),
+          hasAnyName("::std::plus", "::std::minus", "::std::multiplies",
+                     "::std::divides", "::std::modulus", "::std::negate",
+                     "::std::equal_to", "::std::not_equal_to", "::std::greater",
+                     "::std::less", "::std::greater_equal", "::std::less_equal",
+                     "::std::logical_and", "::std::logical_or",
+                     "::std::logical_not", "::std::bit_and", "::std::bit_or",
+                     "::std::bit_xor", "::std::bit_not"))
+          .bind("FunctorClass");
+
+  // Non-transparent functor mentioned as a template parameter. FIXIT.
+  Finder->addMatcher(
+      loc(qualType(
+              unless(elaboratedType()),
+              hasDeclaration(classTemplateSpecializationDecl(
+                  unless(hasAnyTemplateArgument(templateArgument(refersToType(
+                      qualType(pointsTo(qualType(isAnyCharacter()))))))),
+                  hasAnyTemplateArgument(
+                      templateArgument(refersToType(qualType(hasDeclaration(
+                                           TransparentFunctors))))
+                          .bind("Functor"))))))
+          .bind("FunctorParentLoc"),
+      this);
+
+  if (SafeMode)
+    return;
+
+  // Non-transparent functor constructed. No FIXIT. There is no easy way
+  // to rule out the problematic char* vs string case.
+  Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl(
+                                          ofClass(TransparentFunctors))),
+                                      unless(isInTemplateInstantiation()))
+                         .bind("FuncInst"),
+                     this);
+}
+
+static const StringRef Message = "prefer transparent functors '%0'";
+
+template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) {
+  T Result;
+  while (Result.isNull() && !Loc.isNull()) {
+    Result = Loc.getAs<T>();
+    Loc = Loc.getNextTypeLoc();
+  }
+  return Result;
+}
+
+void UseTransparentFunctorsCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *FuncClass =
+      Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass");
+  if (const auto *FuncInst =
+          Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) {
+    diag(FuncInst->getLocStart(), Message)
+          << (FuncClass->getName() + "<>").str();
+    return;
+  }
+
+  const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor");
+  const auto FunctorParentLoc =
+      Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc")
+          ->getAs<TemplateSpecializationTypeLoc>();
+
+  if (!FunctorParentLoc)
+    return;
+
+  unsigned ArgNum = 0;
+  const auto *FunctorParentType =
+      FunctorParentLoc.getType()->castAs<TemplateSpecializationType>();
+  for (; ArgNum < FunctorParentType->getNumArgs(); ++ArgNum) {
+    const TemplateArgument &Arg = FunctorParentType->getArg(ArgNum);
+    if (Arg.getKind() != TemplateArgument::Type)
+      continue;
+    QualType ParentArgType = Arg.getAsType();
+    if (ParentArgType->isRecordType() &&
+        ParentArgType->getAsCXXRecordDecl() ==
+            Functor->getAsType()->getAsCXXRecordDecl())
+      break;
+  }
+  // Functor is a default template argument.
+  if (ArgNum == FunctorParentType->getNumArgs())
+    return;
+  TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum);
+  auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>(
+      FunctorLoc.getTypeSourceInfo()->getTypeLoc());
+  if (FunctorTypeLoc.isNull())
+    return;
+
+  SourceLocation ReportLoc = FunctorLoc.getLocation();
+  diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str()
+                           << FixItHint::CreateRemoval(
+                                  FunctorTypeLoc.getArgLoc(0).getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang

Added: clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.h?rev=287107&view=auto
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.h (added)
+++ clang-tools-extra/trunk/clang-tidy/modernize/UseTransparentFunctorsCheck.h Wed Nov 16 08:42:10 2016
@@ -0,0 +1,37 @@
+//===--- UseTransparentFunctorsCheck.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_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Prefer using transparent functors to non-transparent ones.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-transparent-functors.html
+class UseTransparentFunctorsCheck : public ClangTidyCheck {
+public:
+  UseTransparentFunctorsCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+private:
+  const bool SafeMode;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_TRANSPARENT_FUNCTORS_H

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=287107&r1=287106&r2=287107&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Wed Nov 16 08:42:10 2016
@@ -96,6 +96,11 @@ Improvements to clang-tidy
 
   Adds ``= delete`` to unimplemented private special member functions.
 
+- New `modernize-use-transparent-functors
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-transparent-functors.html>`_ check
+
+  Replaces uses of non-transparent functors with transparent ones where applicable.
+
 - New `mpi-buffer-deref
   <http://clang.llvm.org/extra/clang-tidy/checks/mpi-buffer-deref.html>`_ check
 

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=287107&r1=287106&r2=287107&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst Wed Nov 16 08:42:10 2016
@@ -114,6 +114,7 @@ Clang-Tidy Checks
    modernize-use-equals-delete
    modernize-use-nullptr
    modernize-use-override
+   modernize-use-transparent-functors
    modernize-use-using
    mpi-buffer-deref
    mpi-type-mismatch

Added: clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-transparent-functors.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-transparent-functors.rst?rev=287107&view=auto
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-transparent-functors.rst (added)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/modernize-use-transparent-functors.rst Wed Nov 16 08:42:10 2016
@@ -0,0 +1,39 @@
+.. title:: clang-tidy - modernize-use-transparent-functors
+
+modernize-use-transparent-functors
+==================================
+
+Prefer transparent functors to non-transparent ones. When using transparent
+functors, the type does not need to be repeated. The code is easier to read,
+maintain and less prone to errors. It is not possible to introduce unwanted
+conversions.
+
+  .. code-block:: c++
+
+    // Non-transparent functor  
+    std::map<int, std::string, std::greater<int>> s;
+
+    // Transparent functor.
+    std::map<int, std::string, std::greater<>> s;
+
+    // Non-transparent functor
+    using MyFunctor = std::less<MyType>;
+
+It is not always a safe transformation though. The following case will be 
+untouched to preserve the semantics.
+
+  .. code-block:: c++
+
+    // Non-transparent functor  
+    std::map<const char *, std::string, std::greater<std::string>> s;
+
+Options
+-------
+
+.. option:: SafeMode
+
+  If the option is set to non-zero, the check will not diagnose cases where
+  using a transparent functor cannot be guaranteed to produce identical results
+  as the original code. The default value for this option is `0`.
+
+This check requires using C++14 or higher to run.

Added: clang-tools-extra/trunk/test/clang-tidy/modernize-use-transparent-functors.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/modernize-use-transparent-functors.cpp?rev=287107&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/modernize-use-transparent-functors.cpp (added)
+++ clang-tools-extra/trunk/test/clang-tidy/modernize-use-transparent-functors.cpp Wed Nov 16 08:42:10 2016
@@ -0,0 +1,107 @@
+// RUN: %check_clang_tidy %s modernize-use-transparent-functors %t -- -- -std=c++14
+
+namespace std {
+template<class T>
+struct remove_reference;
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &t);
+
+template <class T>
+constexpr T &&forward(typename std::remove_reference<T>::type &&t);
+
+template <typename T = void>
+struct plus {
+  constexpr T operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct plus<void> {
+  template <typename T, typename U>
+  constexpr auto operator()(T &&Lhs, U &&Rhs) const -> 
+    decltype(forward<T>(Lhs) + forward<U>(Rhs));
+};
+
+template <typename T = void>
+struct less {
+  constexpr bool operator()(const T &Lhs, const T &Rhs) const;
+};
+
+template <>
+struct less<void> {
+  template <typename T, typename U>
+  constexpr bool operator()(T &&Lhs, U &&Rhs) const;
+};
+
+template <typename T = void>
+struct logical_not {
+  constexpr bool operator()(const T &Arg) const;
+};
+
+template <>
+struct logical_not<void> {
+  template <typename T>
+  constexpr bool operator()(T &&Arg) const;
+};
+
+template <typename T>
+class allocator;
+
+template <
+    class Key,
+    class Compare = std::less<>,
+    class Allocator = std::allocator<Key>>
+class set {};
+
+template <
+    class Key,
+    class Compare = std::less<Key>,
+    class Allocator = std::allocator<Key>>
+class set2 {};
+
+template <class InputIt, class UnaryPredicate>
+InputIt find_if(InputIt first, InputIt last,
+                UnaryPredicate p);
+
+template <class RandomIt, class Compare>
+void sort(RandomIt first, RandomIt last, Compare comp);
+
+class iterator {};
+class string {};
+}
+
+int main() {
+  using std::set;
+  using std::less;
+  std::set<int, std::less<int>> s;
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer transparent functors 'less<>' [modernize-use-transparent-functors]
+  // CHECK-FIXES: {{^}}  std::set<int, std::less<>> s;{{$}}
+  set<int, std::less<int>> s2;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  set<int, std::less<>> s2;{{$}}
+  set<int, less<int>> s3;
+  // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  set<int, less<>> s3;{{$}}
+  std::set<int, std::less<>> s4;
+  std::set<char *, std::less<std::string>> s5;
+  std::set<set<int, less<int>>, std::less<>> s6;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  std::set<set<int, less<>>, std::less<>> s6;{{$}}
+  std::iterator begin, end;
+  sort(begin, end, std::less<int>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: prefer transparent functors
+  std::sort(begin, end, std::less<>());
+  find_if(begin, end, std::logical_not<bool>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+  std::find_if(begin, end, std::logical_not<>());
+  using my_set = std::set<int, std::less<int>>;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: prefer transparent functors
+  // CHECK-FIXES: {{^}}  using my_set = std::set<int, std::less<>>;{{$}}
+  using my_set2 = std::set<char*, std::less<std::string>>;
+  using my_less = std::less<std::string>;
+  find_if(begin, end, my_less());
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: prefer transparent functors
+  std::set2<int> control;
+}
+
+




More information about the cfe-commits mailing list