etienneb updated this revision to Diff 52420.
etienneb added a comment.

Fix support for C.
Adding test for C99 code.


http://reviews.llvm.org/D18703

Files:
  clang-tidy/misc/CMakeLists.txt
  clang-tidy/misc/MiscTidyModule.cpp
  clang-tidy/misc/SuspiciousStringCompareCheck.cpp
  clang-tidy/misc/SuspiciousStringCompareCheck.h
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/misc-suspicious-string-compare.rst
  test/clang-tidy/misc-suspicious-string-compare.c
  test/clang-tidy/misc-suspicious-string-compare.cpp

Index: test/clang-tidy/misc-suspicious-string-compare.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-suspicious-string-compare.cpp
@@ -0,0 +1,298 @@
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t
+
+typedef __SIZE_TYPE__ size;
+
+struct locale_t {
+  void* dummy;
+} locale;
+
+static const char A[] = "abc";
+static const unsigned char U[] = "abc";
+static const unsigned char V[] = "xyz";
+static const wchar_t W[] = L"abc";
+
+int memcmp(const void *, const void *, size);
+int wmemcmp(const wchar_t *, const wchar_t *, size);
+int memicmp(const void *, const void *, size);
+int _memicmp(const void *, const void *, size);
+int _memicmp_l(const void *, const void *, size, locale_t);
+
+int strcmp(const char *, const char *);
+int strncmp(const char *, const char *, size);
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size);
+int stricmp(const char *, const char *);
+int strcmpi(const char *, const char *);
+int strnicmp(const char *, const char *, size);
+int _stricmp(const char *, const char * );
+int _strnicmp(const char *, const char *, size);
+int _stricmp_l(const char *, const char *, locale_t);
+int _strnicmp_l(const char *, const char *, size, locale_t);
+
+int wcscmp(const wchar_t *, const wchar_t *);
+int wcsncmp(const wchar_t *, const wchar_t *, size);
+int wcscasecmp(const wchar_t *, const wchar_t *);
+int wcsicmp(const wchar_t *, const wchar_t *);
+int wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp(const wchar_t *, const wchar_t *);
+int _wcsnicmp(const wchar_t *, const wchar_t *, size);
+int _wcsicmp_l(const wchar_t *, const wchar_t *, locale_t);
+int _wcsnicmp_l(const wchar_t *, const wchar_t *, size, locale_t);
+
+int _mbscmp(const unsigned char *, const unsigned char *);
+int _mbsncmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbcmp(const unsigned char *, const unsigned char *, size);
+int _mbsnbicmp(const unsigned char *, const unsigned char *, size);
+int _mbsicmp(const unsigned char *, const unsigned char *);
+int _mbsnicmp(const unsigned char *, const unsigned char *, size);
+int _mbscmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsncmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsicmp_l(const unsigned char *, const unsigned char *, locale_t);
+int _mbsnicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbcmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+int _mbsnbicmp_l(const unsigned char *, const unsigned char *, size, locale_t);
+
+int test_warning_patterns() {
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+  if (!strcmp(A, "a") ||
+      strcmp(A, "b"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strcmp(A, "b") != 0)
+
+  if (strcmp(A, "a") == 1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") == -1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") == true)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") < '0')
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") < 0.)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast [misc-suspicious-string-compare]
+}
+
+int test_valid_patterns() {
+  // The following cases are valid.
+  if (strcmp(A, "a") < 0)
+    return 0;
+  if (strcmp(A, "a") == 0)
+    return 0;
+  if (strcmp(A, "a") <= 0)
+    return 0;
+
+  if (wcscmp(W, L"a") < 0)
+    return 0;
+  if (wcscmp(W, L"a") == 0)
+    return 0;
+  if (wcscmp(W, L"a") <= 0)
+    return 0;
+
+  if (!strcmp(A, "a"))
+    return 0;
+
+  return 1;
+}
+
+int test_implicit_compare_with_functions() {
+
+  if (memcmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: memcmp(A, "a", 1) != 0)
+
+  if (wmemcmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wmemcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wmemcmp(W, L"a", 1) != 0)
+
+  if (memicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'memicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: memicmp(A, "a", 1) != 0)
+
+  if (_memicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _memicmp(A, "a", 1) != 0)
+
+  if (_memicmp_l(A, "a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_memicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _memicmp_l(A, "a", 1, locale) != 0)
+
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strcmp(A, "a") != 0)
+
+  if (strncmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strncmp(A, "a", 1) != 0)
+
+  if (strcasecmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcasecmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strcasecmp(A, "a") != 0)
+
+  if (strncasecmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strncasecmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strncasecmp(A, "a", 1) != 0)
+
+  if (stricmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'stricmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: stricmp(A, "a") != 0)
+
+  if (strcmpi(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmpi' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strcmpi(A, "a") != 0)
+
+  if (_stricmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _stricmp(A, "a") != 0)
+
+  if (strnicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strnicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strnicmp(A, "a", 1) != 0)
+
+  if (_strnicmp(A, "a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _strnicmp(A, "a", 1) != 0)
+
+  if (_stricmp_l(A, "a", locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_stricmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _stricmp_l(A, "a", locale) != 0)
+
+  if (_strnicmp_l(A, "a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_strnicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _strnicmp_l(A, "a", 1, locale) != 0)
+
+  if (wcscmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wcscmp(W, L"a") != 0)
+
+  if (wcsncmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsncmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wcsncmp(W, L"a", 1) != 0)
+
+  if (wcscasecmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcscasecmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wcscasecmp(W, L"a") != 0)
+
+  if (wcsicmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wcsicmp(W, L"a") != 0)
+
+  if (_wcsicmp(W, L"a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _wcsicmp(W, L"a") != 0)
+
+  if (_wcsicmp_l(W, L"a", locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _wcsicmp_l(W, L"a", locale) != 0)
+
+  if (wcsnicmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'wcsnicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: wcsnicmp(W, L"a", 1) != 0)
+
+  if (_wcsnicmp(W, L"a", 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _wcsnicmp(W, L"a", 1) != 0)
+
+  if (_wcsnicmp_l(W, L"a", 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_wcsnicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _wcsnicmp_l(W, L"a", 1, locale) != 0)
+
+  if (_mbscmp(U, V))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbscmp(U, V) != 0)
+
+  if (_mbsncmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsncmp(U, V, 1) != 0)
+
+  if (_mbsnbcmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnbcmp(U, V, 1) != 0)
+
+  if (_mbsnbicmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnbicmp(U, V, 1) != 0)
+
+  if (_mbsicmp(U, V))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsicmp(U, V) != 0)
+
+  if (_mbsnicmp(U, V, 1))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnicmp(U, V, 1) != 0)
+
+  if (_mbscmp_l(U, V, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbscmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbscmp_l(U, V, locale) != 0)
+
+  if (_mbsncmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsncmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsncmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsicmp_l(U, V, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsicmp_l(U, V, locale) != 0)
+
+  if (_mbsnicmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnicmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsnbcmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbcmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnbcmp_l(U, V, 1, locale) != 0)
+
+  if (_mbsnbicmp_l(U, V, 1, locale))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function '_mbsnbicmp_l' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: _mbsnbicmp_l(U, V, 1, locale) != 0)
+
+  return 1;
+}
\ No newline at end of file
Index: test/clang-tidy/misc-suspicious-string-compare.c
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-suspicious-string-compare.c
@@ -0,0 +1,58 @@
+// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- -- -std=c99
+
+static const char A[] = "abc";
+
+int strcmp(const char *, const char *);
+
+int test_warning_patterns() {
+  if (strcmp(A, "a"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: if (strcmp(A, "a") != 0)
+
+  if (!strcmp(A, "a") ||
+      strcmp(A, "b"))
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: strcmp(A, "b") != 0)
+
+  if (strcmp(A, "a") == 1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") == -1)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") < '0')
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant [misc-suspicious-string-compare]
+
+  if (strcmp(A, "a") < 0.)
+    return 0;
+  // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast [misc-suspicious-string-compare]
+}
+
+void test_structure_patterns() {
+  if (strcmp(A, "a")) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:7: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: if (strcmp(A, "a") != 0) {}
+
+  while (strcmp(A, "a")) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:10: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: while (strcmp(A, "a") != 0) {}
+
+  for (;strcmp(A, "a");) {}
+  // CHECK-MESSAGES: [[@LINE-1]]:9: warning: function 'strcmp' is called without explicitly comparing result [misc-suspicious-string-compare]
+  // CHECK-FIXES: for (;strcmp(A, "a") != 0;) {}
+}
+
+int test_valid_patterns() {
+  // The following cases are valid.
+  if (strcmp(A, "a") < 0) return 0;
+  if (strcmp(A, "a") == 0) return 0;
+  if (strcmp(A, "a") <= 0) return 0;
+  if (!strcmp(A, "a")) return 0;
+  if (strcmp(A, "a") == strcmp(A, "b")) return 0;
+  return 1;
+}
\ No newline at end of file
Index: docs/clang-tidy/checks/misc-suspicious-string-compare.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-suspicious-string-compare.rst
@@ -0,0 +1,34 @@
+.. title:: clang-tidy - misc-suspicious-string-compare
+
+misc-suspicious-string-compare
+==============================
+
+Find suspicious usage of runtime string comparison functions.
+This check is valid in C and C++.
+
+Checks for calls with implicit comparator and proposed to explicitly add it.
+
+Example:
+  if (strcmp(...))       // Implicitly compare to zero
+  if (!strcmp(...))      // Won't warn
+  if (strcmp(...) != 0)  // Won't warn
+
+
+Checks that compare function (i,e, ``strcmp``) are compare to valid constant.
+A common mistake is to compare them to '1' or '-1'. The resulting value is
+
+    < 0    when lower than,
+    > 0    when greater than,
+   == 0    when equals.
+
+Example:
+  if (strcmp(...) == -1)  // Incorrect usage of the returned value.
+
+
+Additionally, the check warns if the results value is implicitly cast to a
+*suspicious* non-integer type. It's happening when the returned value is used in
+wrong context.
+
+Example: 
+  if (strcmp(...) < 0.)  // Incorrect usage of the returned value.
+
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -68,6 +68,7 @@
    misc-string-integer-assignment
    misc-suspicious-missing-comma
    misc-suspicious-semicolon
+   misc-suspicious-string-compare
    misc-swapped-arguments
    misc-throw-by-value-catch-by-reference
    misc-undelegated-constructor
Index: clang-tidy/misc/SuspiciousStringCompareCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/SuspiciousStringCompareCheck.h
@@ -0,0 +1,35 @@
+//===--- SuspiciousStringCompareCheck.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_MISC_SUSPICIOUS_STRING_COMPARE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Find suspicious calls to string compare functions.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-suspicious-string-compare.html
+class SuspiciousStringCompareCheck : public ClangTidyCheck {
+public:
+  SuspiciousStringCompareCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SUSPICIOUS_STRING_COMPARE_H
Index: clang-tidy/misc/SuspiciousStringCompareCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/SuspiciousStringCompareCheck.cpp
@@ -0,0 +1,163 @@
+//===--- SuspiciousStringCompareCheck.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 "SuspiciousStringCompareCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) {
+  // Match relationnal operators.
+  const auto RelationalUnaryOperator = unaryOperator(hasOperatorName("!"));
+  const auto RelationalBinaryOperator =
+      binaryOperator(
+          anyOf(hasOperatorName("=="),
+                hasOperatorName("!="),
+                hasOperatorName("<"),
+                hasOperatorName("<="),
+                hasOperatorName(">"),
+                hasOperatorName(">=")));
+  const auto RelationalOperator =
+      expr(anyOf(RelationalUnaryOperator, RelationalBinaryOperator));
+
+  // Match a call to a string compare functions.
+  const auto StringCompareCallExpr =
+      callExpr(hasDeclaration(functionDecl(
+          hasAnyName("__builtin_memcmp",
+                     "__builtin_strcasecmp",
+                     "__builtin_strcmp",
+                     "__builtin_strncasecmp",
+                     "__builtin_strncmp",
+                     "_mbscmp",
+                     "_mbscmp_l",
+                     "_mbsicmp",
+                     "_mbsicmp_l",
+                     "_mbsnbcmp",
+                     "_mbsnbcmp_l",
+                     "_mbsnbicmp",
+                     "_mbsnbicmp_l",
+                     "_mbsncmp",
+                     "_mbsncmp_l",
+                     "_mbsnicmp",
+                     "_mbsnicmp_l",
+                     "_memicmp",
+                     "_memicmp_l",
+                     "_stricmp",
+                     "_stricmp_l",
+                     "_strnicmp",
+                     "_strnicmp_l",
+                     "_wcsicmp",
+                     "_wcsicmp_l",
+                     "_wcsnicmp",
+                     "_wcsnicmp_l",
+                     "memcmp",
+                     "memicmp",
+                     "strcasecmp",
+                     "strcmp",
+                     "strcmpi",
+                     "stricmp",
+                     "strncasecmp",
+                     "strncmp",
+                     "strnicmp",
+                     "wcscasecmp",
+                     "wcscmp",
+                     "wcsicmp",
+                     "wcsncmp",
+                     "wcsnicmp",
+                     "wmemcmp")).bind("decl"))).bind("call");
+
+  // Detect suspicious calls to string compare (missing comparator) [only C]:
+  //     'if (strcmp())'  ->  'if (strcmp() != 0)'
+  Finder->addMatcher(
+  	  stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
+  	  	         whileStmt(hasCondition(StringCompareCallExpr)),
+  	  	         doStmt(hasCondition(StringCompareCallExpr)),
+  	  	         forStmt(hasCondition(StringCompareCallExpr))))
+          .bind("missing-comparison"),
+      this);
+
+  Finder->addMatcher(
+  	  expr(StringCompareCallExpr,
+  	       unless(hasParent(RelationalOperator)),
+  	       unless(hasParent(implicitCastExpr())))
+          .bind("missing-comparison"),
+      this);
+
+  // Detect suspicious calls to string compare with implicit comparison:
+  //     'if (strcmp())'  ->  'if (strcmp() != 0)'
+  //     'if (!strcmp())'  is considered valid.
+  Finder->addMatcher(
+      implicitCastExpr(hasType(isInteger()),
+                       hasSourceExpression(StringCompareCallExpr),
+                       unless(hasParent(RelationalOperator)))
+          .bind("missing-comparison"),
+      this);
+
+  // Detect suspicious cast to an inconsistant type.
+  Finder->addMatcher(
+      implicitCastExpr(unless(hasType(isInteger())),
+                       hasSourceExpression(StringCompareCallExpr))
+          .bind("invalid-conversion"),
+      this);
+
+  // Detect suspicious calls to string compare functions: 'strcmp() == -1'.
+  const auto InvalidLiteral =
+      ignoringParenImpCasts(
+          anyOf(integerLiteral(unless(equals(0))),
+                unaryOperator(hasOperatorName("-"),
+                              has(integerLiteral(unless(equals(0))))),
+                characterLiteral(),
+                cxxBoolLiteral()));
+
+  Finder->addMatcher(
+      binaryOperator(RelationalBinaryOperator,
+                     hasEitherOperand(StringCompareCallExpr),
+                     hasEitherOperand(InvalidLiteral))
+          .bind("invalid-comparison"),
+      this);
+}
+
+void SuspiciousStringCompareCheck::check(
+	const MatchFinder::MatchResult &Result) {
+  const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+  assert(Decl != nullptr && Call != nullptr);
+
+  if (Result.Nodes.getNodeAs<Stmt>("missing-comparison") != nullptr) {
+    SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+        Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
+        Result.Context->getLangOpts());
+
+    diag(Call->getLocStart(),
+         "function '%0' is called without explicitly comparing result")
+        << Decl->getName()
+        << FixItHint::CreateInsertion(EndLoc, " != 0");
+  }
+
+  if (Result.Nodes.getNodeAs<Stmt>("invalid-comparison") != nullptr) {
+    diag(Call->getLocStart(),
+         "function '%0' is compared to a suspicious constant")
+        << Decl->getName();
+  }
+
+  if (Result.Nodes.getNodeAs<Stmt>("invalid-conversion") != nullptr) {
+    diag(Call->getLocStart(),
+         "function '%0' has suspicious implicit cast")
+        << Decl->getName();
+  }}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -33,6 +33,7 @@
 #include "StringIntegerAssignmentCheck.h"
 #include "SuspiciousMissingCommaCheck.h"
 #include "SuspiciousSemicolonCheck.h"
+#include "SuspiciousStringCompareCheck.h"
 #include "SwappedArgumentsCheck.h"
 #include "ThrowByValueCatchByReferenceCheck.h"
 #include "UndelegatedConstructor.h"
@@ -93,6 +94,8 @@
         "misc-suspicious-missing-comma");
     CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
         "misc-suspicious-semicolon");
+    CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
+        "misc-suspicious-string-compare");
     CheckFactories.registerCheck<SwappedArgumentsCheck>(
         "misc-swapped-arguments");
     CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -25,6 +25,7 @@
   StringIntegerAssignmentCheck.cpp
   SuspiciousMissingCommaCheck.cpp
   SuspiciousSemicolonCheck.cpp
+  SuspiciousStringCompareCheck.cpp
   SwappedArgumentsCheck.cpp
   ThrowByValueCatchByReferenceCheck.cpp
   UndelegatedConstructor.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to