Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -29,6 +29,7 @@ StringLiteralWithEmbeddedNulCheck.cpp SuspiciousMissingCommaCheck.cpp SuspiciousSemicolonCheck.cpp + SuspiciousStringCompareCheck.cpp SwappedArgumentsCheck.cpp ThrowByValueCatchByReferenceCheck.cpp UndelegatedConstructor.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -37,6 +37,7 @@ #include "StringLiteralWithEmbeddedNulCheck.h" #include "SuspiciousMissingCommaCheck.h" #include "SuspiciousSemicolonCheck.h" +#include "SuspiciousStringCompareCheck.h" #include "SwappedArgumentsCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UndelegatedConstructor.h" @@ -105,6 +106,8 @@ "misc-suspicious-missing-comma"); CheckFactories.registerCheck( "misc-suspicious-semicolon"); + CheckFactories.registerCheck( + "misc-suspicious-string-compare"); CheckFactories.registerCheck( "misc-swapped-arguments"); CheckFactories.registerCheck( Index: clang-tidy/misc/SuspiciousStringCompareCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/SuspiciousStringCompareCheck.h @@ -0,0 +1,40 @@ +//===--- 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); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const bool WarnOnImplicitComparison; + const bool WarnOnLogicalNotComparison; + const std::string StringCompareLikeFunctions; +}; + +} // 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,221 @@ +//===--- 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 { + +AST_MATCHER(BinaryOperator, isComparisonOperator) { + return Node.isComparisonOp(); +} + +static const char *KnownStringCompareFunctions = "__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;" + "lstrcmp;" + "lstrcmpi;" + "memcmp;" + "memicmp;" + "strcasecmp;" + "strcmp;" + "strcmpi;" + "stricmp;" + "strncasecmp;" + "strncmp;" + "strnicmp;" + "wcscasecmp;" + "wcscmp;" + "wcsicmp;" + "wcsncmp;" + "wcsnicmp;" + "wmemcmp;"; + +static const char StringCompareLikeFunctionsDelimiter[] = ";"; + +static void ParseFunctionNames(StringRef Option, + std::vector *Result) { + SmallVector Functions; + Option.split(Functions, StringCompareLikeFunctionsDelimiter); + for (StringRef &Function : Functions) { + Function = Function.trim(); + if (!Function.empty()) + Result->push_back(Function); + } +} + +SuspiciousStringCompareCheck::SuspiciousStringCompareCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnOnImplicitComparison(Options.get("WarnOnImplicitComparison", 1)), + WarnOnLogicalNotComparison(Options.get("WarnOnLogicalNotComparison", 1)), + StringCompareLikeFunctions( + Options.get("StringCompareLikeFunctions", "")) {} + +void SuspiciousStringCompareCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WarnOnImplicitComparison", WarnOnImplicitComparison); + Options.store(Opts, "WarnOnLogicalNotComparison", WarnOnLogicalNotComparison); + Options.store(Opts, "StringCompareLikeFunctions", StringCompareLikeFunctions); +} + +void SuspiciousStringCompareCheck::registerMatchers(MatchFinder *Finder) { + // Match relational operators. + const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName("!")); + const auto ComparisonBinaryOperator = binaryOperator(isComparisonOperator()); + const auto ComparisonOperator = + expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator)); + + // Add the list of known string compare-like functions and add user-defined + // functions. + std::vector FuntionNames; + ParseFunctionNames(KnownStringCompareFunctions, &FuntionNames); + ParseFunctionNames(StringCompareLikeFunctions, &FuntionNames); + const auto FunctionCompareDecl = + functionDecl(hasAnyName(std::vector(FuntionNames.begin(), + FuntionNames.end()))) + .bind("decl"); + + // Match a call to a string compare functions. + const auto StringCompareCallExpr = + callExpr(hasDeclaration(FunctionCompareDecl)).bind("call"); + + if (WarnOnImplicitComparison) { + // 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(ComparisonOperator)), + 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 (see WarnOnLogicalNotComparison) + Finder->addMatcher( + implicitCastExpr(hasType(isInteger()), + hasSourceExpression(StringCompareCallExpr), + unless(hasParent(ComparisonOperator))) + .bind("missing-comparison"), + this); + + // Detect suspicious cast to an inconsistant type. + Finder->addMatcher( + implicitCastExpr(unless(hasType(isInteger())), + hasSourceExpression(StringCompareCallExpr)) + .bind("invalid-conversion"), + this); + } + + if (WarnOnLogicalNotComparison) { + // Detect suspicious calls to string compared with '!' operator: + // 'if (!strcmp())' -> 'if (strcmp() == 0)' + Finder->addMatcher(unaryOperator(hasOperatorName("!"), + hasUnaryOperand(ignoringParenImpCasts( + StringCompareCallExpr))) + .bind("logical-not-comparison"), + 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(isComparisonOperator(), + hasEitherOperand(StringCompareCallExpr), + hasEitherOperand(InvalidLiteral)) + .bind("invalid-comparison"), + this); +} + +void SuspiciousStringCompareCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Decl = Result.Nodes.getNodeAs("decl"); + const auto *Call = Result.Nodes.getNodeAs("call"); + assert(Decl != nullptr && Call != nullptr); + + if (Result.Nodes.getNodeAs("missing-comparison")) { + 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 << FixItHint::CreateInsertion(EndLoc, " != 0"); + } + + if (const auto *E = Result.Nodes.getNodeAs("logical-not-comparison")) { + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + Call->getRParenLoc(), 0, Result.Context->getSourceManager(), + Result.Context->getLangOpts()); + SourceLocation NotLoc = E->getLocStart(); + + diag(Call->getLocStart(), + "function %0 is compared using logical not operator") + << Decl << FixItHint::CreateRemoval( + CharSourceRange::getTokenRange(NotLoc, NotLoc)) + << FixItHint::CreateInsertion(EndLoc, " == 0"); + } + + if (Result.Nodes.getNodeAs("invalid-comparison")) { + diag(Call->getLocStart(), + "function %0 is compared to a suspicious constant") + << Decl; + } + + if (Result.Nodes.getNodeAs("invalid-conversion")) { + diag(Call->getLocStart(), "function %0 has suspicious implicit cast") + << Decl; + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -73,6 +73,7 @@ misc-string-literal-with-embedded-nul 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: docs/clang-tidy/checks/misc-suspicious-string-compare.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-suspicious-string-compare.rst @@ -0,0 +1,40 @@ +.. 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. + +.. code:: c++ + + if (strcmp(...)) // Implicitly compare to zero + if (!strcmp(...)) // Won't warn + if (strcmp(...) != 0) // Won't warn + + +Checks that compare function results (i,e, ``strcmp``) are compared to valid +constant. The resulting value is + +.. code:: + + < 0 when lower than, + > 0 when greater than, + == 0 when equals. + +A common mistake is to compare the result to '1' or '-1'. + +.. code:: c++ + + 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 +a wrong context. + +.. code:: c++ + + if (strcmp(...) < 0.) // Incorrect usage of the returned value. Index: test/clang-tidy/misc-suspicious-string-compare.c =================================================================== --- /dev/null +++ test/clang-tidy/misc-suspicious-string-compare.c @@ -0,0 +1,66 @@ +// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \ +// RUN: {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \ +// RUN: -- -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") != 0 || + strcmp(A, "b")) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result + // 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 + + if (strcmp(A, "a") == -1) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant + + if (strcmp(A, "a") < '0') + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant + + if (strcmp(A, "a") < 0.) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast + + if (!strcmp(A, "a")) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:8: warning: function 'strcmp' is compared using logical not operator + // CHECK-FIXES: if (strcmp(A, "a") == 0) +} + +void test_structure_patterns() { + if (strcmp(A, "a")) {} + // CHECK-MESSAGES: [[@LINE-1]]:7: warning: function 'strcmp' is called without explicitly comparing result + // 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 + // 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 + // 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") == strcmp(A, "b")) return 0; + return 1; +} Index: test/clang-tidy/misc-suspicious-string-compare.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-suspicious-string-compare.cpp @@ -0,0 +1,299 @@ +// RUN: %check_clang_tidy %s misc-suspicious-string-compare %t -- \ +// RUN: -config='{CheckOptions: \ +// RUN: [{key: misc-suspicious-string-compare.WarnOnImplicitComparison, value: 1}, \ +// RUN: {key: misc-suspicious-string-compare.WarnOnLogicalNotComparison, value: 1}]}' \ +// RUN: -- + +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") == 0 || + strcmp(A, "b")) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is called without explicitly comparing result + // 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 + + if (strcmp(A, "a") == -1) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant + + if (strcmp(A, "a") == true) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant + + if (strcmp(A, "a") < '0') + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' is compared to a suspicious constant + + if (strcmp(A, "a") < 0.) + return 0; + // CHECK-MESSAGES: [[@LINE-2]]:7: warning: function 'strcmp' has suspicious implicit cast +} + +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; + + 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // 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 + // CHECK-FIXES: _mbsnbicmp_l(U, V, 1, locale) != 0) + + return 1; +}