Thank you for the detailed review.

I think I fixed all of the issues you mentioned. In case I forgot something I 
will include it in the fix in the next version of the patch.


http://reviews.llvm.org/D6925

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/ContainerSizeEmpty.cpp
  clang-tidy/readability/ContainerSizeEmpty.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  test/clang-tidy/readibility-container-size-empty.cpp

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
Index: clang-tidy/readability/CMakeLists.txt
===================================================================
--- clang-tidy/readability/CMakeLists.txt
+++ clang-tidy/readability/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_library(clangTidyReadabilityModule
   BracesAroundStatementsCheck.cpp
+  ContainerSizeEmpty.cpp
   FunctionSize.cpp
   NamespaceCommentCheck.cpp
   ReadabilityTidyModule.cpp
Index: clang-tidy/readability/ContainerSizeEmpty.cpp
===================================================================
--- clang-tidy/readability/ContainerSizeEmpty.cpp
+++ clang-tidy/readability/ContainerSizeEmpty.cpp
@@ -0,0 +1,174 @@
+//===--- ContainerSizeEmpty.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 "ContainerSizeEmpty.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
+
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace {
+const llvm::StringSet<> containerNames = [] {
+  llvm::StringSet<> RetVal;
+  RetVal.insert("std::vector");
+  RetVal.insert("std::list");
+  RetVal.insert("std::array");
+  RetVal.insert("std::deque");
+  RetVal.insert("std::forward_list");
+  RetVal.insert("std::set");
+  RetVal.insert("std::map");
+  RetVal.insert("std::multiset");
+  RetVal.insert("std::multimap");
+  RetVal.insert("std::unordered_set");
+  RetVal.insert("std::unordered_map");
+  RetVal.insert("std::unordered_multiset");
+  RetVal.insert("std::unordered_multimap");
+  RetVal.insert("std::stack");
+  RetVal.insert("std::queue");
+  RetVal.insert("std::priority_queue");
+  return RetVal;
+}();
+
+bool isContainer(llvm::StringRef className) {
+  return containerNames.find(className) != containerNames.end();
+}
+}
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER_P(QualType, unqualifiedType, internal::Matcher<Type>,
+              InnerMatcher) {
+  return InnerMatcher.matches(*Node, Finder, Builder);
+}
+
+AST_MATCHER(Type, isBoolType) { return Node.isBooleanType(); }
+
+AST_MATCHER(NamedDecl, stlContainer) {
+  return isContainer(Node.getQualifiedNameAsString());
+}
+}
+}
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+ContainerSizeEmptyCheck::ContainerSizeEmptyCheck(StringRef Name,
+                                                 ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context) {}
+
+void ContainerSizeEmptyCheck::registerMatchers(MatchFinder *Finder) {
+  const auto wrong_use = anyOf(
+      hasParent(
+          binaryOperator(
+              anyOf(has(integerLiteral(equals(0))),
+                    allOf(anyOf(hasOperatorName("<"), hasOperatorName(">="),
+                                hasOperatorName(">"), hasOperatorName("<=")),
+                          anyOf(hasRHS(integerLiteral(equals(1))),
+                                hasLHS(integerLiteral(equals(1)))))))
+              .bind("SizeBinaryOp")),
+      hasParent(implicitCastExpr(
+          hasImplicitDestinationType(unqualifiedType(isBoolType())),
+          anyOf(
+              hasParent(unaryOperator(hasOperatorName("!")).bind("NegOnSize")),
+              anything()))),
+      hasParent(
+          explicitCastExpr(hasDestinationType(unqualifiedType(isBoolType())))));
+
+  Finder->addMatcher(
+      memberCallExpr(
+          on(expr(anyOf(hasType(namedDecl(stlContainer())),
+                        hasType(qualType(pointsTo(namedDecl(stlContainer())))),
+                        hasType(qualType(references(
+                            namedDecl(stlContainer())))))).bind("STLObject")),
+          callee(methodDecl(hasName("size"))), wrong_use).bind("SizeCallExpr"),
+      this);
+}
+
+void ContainerSizeEmptyCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto MemberCall =
+      Result.Nodes.getNodeAs<CXXMemberCallExpr>("SizeCallExpr");
+  const auto BinaryOp = Result.Nodes.getNodeAs<BinaryOperator>("SizeBinaryOp");
+  const auto E = Result.Nodes.getNodeAs<Expr>("STLObject");
+  FixItHint Hint;
+  std::string ReplacementText =
+      Lexer::getSourceText(CharSourceRange::getTokenRange(E->getSourceRange()),
+                           *Result.SourceManager, LangOptions());
+  if (E->getType()->isPointerType())
+    ReplacementText += "->empty()";
+  else
+    ReplacementText += ".empty()";
+
+  if (BinaryOp) { // Determine the correct transformation.
+    bool Negation = false;
+    const bool ContainerIsLHS = !llvm::isa<IntegerLiteral>(BinaryOp->getLHS());
+    const auto OpCode = BinaryOp->getOpcode();
+    uint64_t Value = 0;
+    if (ContainerIsLHS) {
+      if (const auto Literal =
+              llvm::dyn_cast<IntegerLiteral>(BinaryOp->getRHS()))
+        Value = Literal->getValue().getLimitedValue();
+      else
+        return;
+    } else {
+      Value = llvm::dyn_cast<IntegerLiteral>(BinaryOp->getLHS())
+                  ->getValue()
+                  .getLimitedValue();
+    }
+
+    // Constant that is not handled.
+    if (Value > 1)
+      return;
+
+    // Always true, no warnings for that.
+    if ((OpCode == BinaryOperatorKind::BO_GE && Value == 0 && ContainerIsLHS) ||
+        (OpCode == BinaryOperatorKind::BO_LE && Value == 0 && !ContainerIsLHS))
+      return;
+
+    if (OpCode == BinaryOperatorKind::BO_NE && Value == 0)
+      Negation = true;
+    if ((OpCode == BinaryOperatorKind::BO_GT ||
+         OpCode == BinaryOperatorKind::BO_GE) &&
+        ContainerIsLHS)
+      Negation = true;
+    if ((OpCode == BinaryOperatorKind::BO_LT ||
+         OpCode == BinaryOperatorKind::BO_LE) &&
+        !ContainerIsLHS)
+      Negation = true;
+
+    if (Negation)
+      ReplacementText = "!" + ReplacementText;
+    Hint = FixItHint::CreateReplacement(BinaryOp->getSourceRange(),
+                                        ReplacementText);
+
+  } else {
+    // If there is a conversion above the size call to bool, it is safe to just
+    // replace size with empty.
+    const auto UO = Result.Nodes.getNodeAs<UnaryOperator>("NegOnSize");
+    if (UO)
+      Hint =
+          FixItHint::CreateReplacement(UO->getSourceRange(), ReplacementText);
+    else
+      Hint = FixItHint::CreateReplacement(MemberCall->getSourceRange(),
+                                          "!" + ReplacementText);
+  }
+  diag(MemberCall->getLocStart(),
+       "The 'empty' method should be used to check for emptiness instead "
+       "of 'size'.")
+      << Hint;
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/readability/ContainerSizeEmpty.h
===================================================================
--- clang-tidy/readability/ContainerSizeEmpty.h
+++ clang-tidy/readability/ContainerSizeEmpty.h
@@ -0,0 +1,39 @@
+//===--- ContainerSizeEmpty.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_READABILITY_CONTAINERSIZEEMPTY_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTY_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+/// \brief Checks whether a call to the \c size() method can be replaced with a
+/// call to \c empty().
+///
+/// The emptiness of a container should be checked using the \c empty() method
+/// instead of the \c size() method. It is not guaranteed that size is a
+/// constant-time function, and it is generally more efficient and also shows
+/// clearer intent to use empty. Furthermore some containers may implement the
+/// empty method but not implement the size method. Using empty whenever
+/// possible makes it easier to switch to another container in the future.
+class ContainerSizeEmptyCheck : public ClangTidyCheck {
+public:
+  ContainerSizeEmptyCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_CONTAINERSIZEEMPTY_H
Index: clang-tidy/readability/ReadabilityTidyModule.cpp
===================================================================
--- clang-tidy/readability/ReadabilityTidyModule.cpp
+++ clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -11,6 +11,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "BracesAroundStatementsCheck.h"
+#include "ContainerSizeEmpty.h"
 #include "FunctionSize.h"
 #include "RedundantSmartptrGet.h"
 
@@ -23,6 +24,8 @@
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<BracesAroundStatementsCheck>(
         "readability-braces-around-statements");
+    CheckFactories.registerCheck<ContainerSizeEmptyCheck>(
+        "readability-container-size-empty");
     CheckFactories.registerCheck<FunctionSizeCheck>(
         "readability-function-size");
     CheckFactories.registerCheck<RedundantSmartptrGet>(
Index: test/clang-tidy/readibility-container-size-empty.cpp
===================================================================
--- test/clang-tidy/readibility-container-size-empty.cpp
+++ test/clang-tidy/readibility-container-size-empty.cpp
@@ -0,0 +1,107 @@
+// RUN: $(dirname %s)/check_clang_tidy.sh %s readability-container-size-empty %t
+// REQUIRES: shell
+
+namespace std {
+template<typename T>
+struct vector {
+  vector() {}
+  int size() const {}
+  bool empty() const {}
+};
+}
+
+int main()
+{
+	std::vector<int> vect;
+	if(vect.size() == 0); 
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used to check for emptiness instead of 'size'. [readability-container-size-empty]
+  // CHECK-MESSAGES: if(vect.size() == 0);
+  // CHECK-FIXES: vect.empty()
+	if(vect.size() != 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect.size() != 0);
+  // CHECK-FIXES: !vect.empty()
+	if(0 == vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(0 == vect.size());
+  // CHECK-FIXES: vect.empty()
+	if(0 != vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(0 != vect.size());
+  // CHECK-FIXES: !vect.empty()
+	if(vect.size() > 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect.size() > 0);
+  // CHECK-FIXES: !vect.empty()
+	if(0 < vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(0 < vect.size());
+  // CHECK-FIXES: !vect.empty()
+	if(vect.size() < 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect.size() < 1);
+  // CHECK-FIXES: vect.empty()
+	if(1 > vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(1 > vect.size());
+  // CHECK-FIXES: vect.empty()
+	if(vect.size() >= 1);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect.size() >= 1);
+  // CHECK-FIXES: !vect.empty()
+	if(1 <= vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(1 <= vect.size());
+  // CHECK-FIXES: !vect.empty()
+	if(!vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(!vect.size());
+  // CHECK-FIXES: vect.empty()
+	if(vect.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect.size());
+  // CHECK-FIXES: !vect.empty()
+
+	if(vect.empty()); 
+	
+	const std::vector<int> vect2;
+	if(vect2.size() != 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect2.size() != 0)
+  // CHECK-FIXES: !vect2.empty()
+
+	std::vector<int> *vect3 = new std::vector<int>();
+	if(vect3->size() == 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect3->size() == 0)
+  // CHECK-FIXES: vect3->empty()
+
+  delete vect3;
+	
+  const std::vector<int> &vect4 = vect2;
+	if(vect4.size() == 0);
+  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if(vect4.size() == 0)
+  // CHECK-FIXES: vect4.empty()
+}
+
+#define CHECKSIZE(x) if (x.size()) 
+
+template<typename T>
+void f() {
+  std::vector<T> v;
+  if (v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: if (v.size()) {}
+  // CHECK-FIXES: !v.empty()
+  CHECKSIZE(v); 
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: The 'empty' method should be used
+  // CHECK-MESSAGES: CHECKSIZE(v);
+}
+
+void g() {
+  f<int>();
+  f<double>();
+  f<char*>();
+}
+
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to