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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits