[clang-tidy] Add new checker for comparison with runtime string functions.
Summary: This checker is validating suspicious usage of string compare functions. Example: ``` if (strcmp(...)) // Implicitly compare to zero if (!strcmp(...)) // Won't warn if (strcmp(...) != 0) // Won't warn ``` This patch was checked over large amount of code. There is three checks: [*] Implicit comparator to zero (coding-style, many warnings found), [*] Suspicious implicit cast to non-integral (bugs!?, almost none found), [*] Comparison to suspicious constant (bugs!?, found two cases), Example: [[https://github.com/kylepjohnson/sigma/blob/master/sigma/native-installers/debian/dependencies/files/opt/sigma/E/HEURISTICS/che_to_precgen.c | https://github.com/kylepjohnson/sigma/blob/master/sigma/native-installers/debian/dependencies/files/opt/sigma/E/HEURISTICS/che_to_precgen.c]] ``` else if(strcmp(id, "select") == 0) { array->array[i].key1 = 25; } else if(strcmp(id, "sk") == 28) // BUG!? { array->array[i].key1 = 20; } ``` Reviewers: alexfh Subscribers: Eugene.Zelenko, cfe-commits Differential Revision: http://reviews.llvm.org/D18703 llvm-svn: 267009
This commit is contained in:
parent
1a299eab32
commit
bae829ede5
|
@ -29,6 +29,7 @@ add_clang_library(clangTidyMiscModule
|
||||||
StringLiteralWithEmbeddedNulCheck.cpp
|
StringLiteralWithEmbeddedNulCheck.cpp
|
||||||
SuspiciousMissingCommaCheck.cpp
|
SuspiciousMissingCommaCheck.cpp
|
||||||
SuspiciousSemicolonCheck.cpp
|
SuspiciousSemicolonCheck.cpp
|
||||||
|
SuspiciousStringCompareCheck.cpp
|
||||||
SwappedArgumentsCheck.cpp
|
SwappedArgumentsCheck.cpp
|
||||||
ThrowByValueCatchByReferenceCheck.cpp
|
ThrowByValueCatchByReferenceCheck.cpp
|
||||||
UndelegatedConstructor.cpp
|
UndelegatedConstructor.cpp
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "StringLiteralWithEmbeddedNulCheck.h"
|
#include "StringLiteralWithEmbeddedNulCheck.h"
|
||||||
#include "SuspiciousMissingCommaCheck.h"
|
#include "SuspiciousMissingCommaCheck.h"
|
||||||
#include "SuspiciousSemicolonCheck.h"
|
#include "SuspiciousSemicolonCheck.h"
|
||||||
|
#include "SuspiciousStringCompareCheck.h"
|
||||||
#include "SwappedArgumentsCheck.h"
|
#include "SwappedArgumentsCheck.h"
|
||||||
#include "ThrowByValueCatchByReferenceCheck.h"
|
#include "ThrowByValueCatchByReferenceCheck.h"
|
||||||
#include "UndelegatedConstructor.h"
|
#include "UndelegatedConstructor.h"
|
||||||
|
@ -106,6 +107,8 @@ public:
|
||||||
"misc-suspicious-missing-comma");
|
"misc-suspicious-missing-comma");
|
||||||
CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
|
CheckFactories.registerCheck<SuspiciousSemicolonCheck>(
|
||||||
"misc-suspicious-semicolon");
|
"misc-suspicious-semicolon");
|
||||||
|
CheckFactories.registerCheck<SuspiciousStringCompareCheck>(
|
||||||
|
"misc-suspicious-string-compare");
|
||||||
CheckFactories.registerCheck<SwappedArgumentsCheck>(
|
CheckFactories.registerCheck<SwappedArgumentsCheck>(
|
||||||
"misc-swapped-arguments");
|
"misc-swapped-arguments");
|
||||||
CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
|
CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr 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<std::string> *Result) {
|
||||||
|
SmallVector<StringRef, 4> 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<std::string> FunctionNames;
|
||||||
|
ParseFunctionNames(KnownStringCompareFunctions, &FunctionNames);
|
||||||
|
ParseFunctionNames(StringCompareLikeFunctions, &FunctionNames);
|
||||||
|
const auto FunctionCompareDecl =
|
||||||
|
functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
|
||||||
|
FunctionNames.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<FunctionDecl>("decl");
|
||||||
|
const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
|
||||||
|
assert(Decl != nullptr && Call != nullptr);
|
||||||
|
|
||||||
|
if (Result.Nodes.getNodeAs<Stmt>("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<Expr>("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<Stmt>("invalid-comparison")) {
|
||||||
|
diag(Call->getLocStart(),
|
||||||
|
"function %0 is compared to a suspicious constant")
|
||||||
|
<< Decl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Result.Nodes.getNodeAs<Stmt>("invalid-conversion")) {
|
||||||
|
diag(Call->getLocStart(), "function %0 has suspicious implicit cast")
|
||||||
|
<< Decl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace misc
|
||||||
|
} // namespace tidy
|
||||||
|
} // namespace clang
|
|
@ -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
|
|
@ -73,6 +73,7 @@ Clang-Tidy Checks
|
||||||
misc-string-literal-with-embedded-nul
|
misc-string-literal-with-embedded-nul
|
||||||
misc-suspicious-missing-comma
|
misc-suspicious-missing-comma
|
||||||
misc-suspicious-semicolon
|
misc-suspicious-semicolon
|
||||||
|
misc-suspicious-string-compare
|
||||||
misc-swapped-arguments
|
misc-swapped-arguments
|
||||||
misc-throw-by-value-catch-by-reference
|
misc-throw-by-value-catch-by-reference
|
||||||
misc-undelegated-constructor
|
misc-undelegated-constructor
|
||||||
|
|
|
@ -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.
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue