11 #include "../utils/Matchers.h" 12 #include "../utils/OptionsUtils.h" 13 #include "clang/AST/ASTContext.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 #include "clang/Lex/Lexer.h" 26 "__builtin_strcasecmp;" 28 "__builtin_strncasecmp;" 70 SuspiciousStringCompareCheck::SuspiciousStringCompareCheck(
73 WarnOnImplicitComparison(Options.get(
"WarnOnImplicitComparison", 1)),
74 WarnOnLogicalNotComparison(Options.get(
"WarnOnLogicalNotComparison", 0)),
75 StringCompareLikeFunctions(
76 Options.get(
"StringCompareLikeFunctions",
"")) {}
80 Options.
store(Opts,
"WarnOnImplicitComparison", WarnOnImplicitComparison);
81 Options.
store(Opts,
"WarnOnLogicalNotComparison", WarnOnLogicalNotComparison);
82 Options.
store(Opts,
"StringCompareLikeFunctions", StringCompareLikeFunctions);
87 const auto ComparisonUnaryOperator = unaryOperator(hasOperatorName(
"!"));
88 const auto ComparisonBinaryOperator =
89 binaryOperator(matchers::isComparisonOperator());
90 const auto ComparisonOperator =
91 expr(anyOf(ComparisonUnaryOperator, ComparisonBinaryOperator));
96 (llvm::Twine(KnownStringCompareFunctions) + StringCompareLikeFunctions)
100 const auto FunctionCompareDecl =
101 functionDecl(hasAnyName(std::vector<StringRef>(FunctionNames.begin(),
102 FunctionNames.end())))
104 const auto DirectStringCompareCallExpr =
105 callExpr(hasDeclaration(FunctionCompareDecl)).bind(
"call");
106 const auto MacroStringCompareCallExpr = conditionalOperator(anyOf(
107 hasTrueExpression(ignoringParenImpCasts(DirectStringCompareCallExpr)),
108 hasFalseExpression(ignoringParenImpCasts(DirectStringCompareCallExpr))));
110 const auto StringCompareCallExpr = ignoringParenImpCasts(
111 anyOf(DirectStringCompareCallExpr, MacroStringCompareCallExpr));
113 if (WarnOnImplicitComparison) {
117 stmt(anyOf(ifStmt(hasCondition(StringCompareCallExpr)),
118 whileStmt(hasCondition(StringCompareCallExpr)),
119 doStmt(hasCondition(StringCompareCallExpr)),
120 forStmt(hasCondition(StringCompareCallExpr)),
122 anyOf(hasOperatorName(
"&&"), hasOperatorName(
"||")),
123 hasEitherOperand(StringCompareCallExpr))))
124 .bind(
"missing-comparison"),
128 if (WarnOnLogicalNotComparison) {
131 Finder->addMatcher(unaryOperator(hasOperatorName(
"!"),
132 hasUnaryOperand(ignoringParenImpCasts(
133 StringCompareCallExpr)))
134 .bind(
"logical-not-comparison"),
140 implicitCastExpr(unless(hasType(isInteger())),
141 hasSourceExpression(StringCompareCallExpr))
142 .bind(
"invalid-conversion"),
148 unless(anyOf(matchers::isComparisonOperator(), hasOperatorName(
"&&"),
149 hasOperatorName(
"||"), hasOperatorName(
"="))),
150 hasEitherOperand(StringCompareCallExpr))
151 .bind(
"suspicious-operator"),
155 const auto InvalidLiteral = ignoringParenImpCasts(
156 anyOf(integerLiteral(unless(equals(0))),
158 hasOperatorName(
"-"),
159 has(ignoringParenImpCasts(integerLiteral(unless(equals(0)))))),
160 characterLiteral(), cxxBoolLiteral()));
162 Finder->addMatcher(binaryOperator(matchers::isComparisonOperator(),
163 hasEitherOperand(StringCompareCallExpr),
164 hasEitherOperand(InvalidLiteral))
165 .bind(
"invalid-comparison"),
170 const MatchFinder::MatchResult &Result) {
171 const auto *Decl = Result.Nodes.getNodeAs<FunctionDecl>(
"decl");
172 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"call");
173 assert(Decl !=
nullptr && Call !=
nullptr);
175 if (Result.Nodes.getNodeAs<Stmt>(
"missing-comparison")) {
176 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
177 Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
180 diag(Call->getLocStart(),
181 "function %0 is called without explicitly comparing result")
182 << Decl << FixItHint::CreateInsertion(EndLoc,
" != 0");
185 if (
const auto *E = Result.Nodes.getNodeAs<Expr>(
"logical-not-comparison")) {
186 SourceLocation EndLoc = Lexer::getLocForEndOfToken(
187 Call->getRParenLoc(), 0, Result.Context->getSourceManager(),
189 SourceLocation NotLoc = E->getLocStart();
191 diag(Call->getLocStart(),
192 "function %0 is compared using logical not operator")
193 << Decl << FixItHint::CreateRemoval(
194 CharSourceRange::getTokenRange(NotLoc, NotLoc))
195 << FixItHint::CreateInsertion(EndLoc,
" == 0");
198 if (Result.Nodes.getNodeAs<Stmt>(
"invalid-comparison")) {
199 diag(Call->getLocStart(),
200 "function %0 is compared to a suspicious constant")
204 if (
const auto *BinOp =
205 Result.Nodes.getNodeAs<BinaryOperator>(
"suspicious-operator")) {
206 diag(Call->getLocStart(),
"results of function %0 used by operator '%1'")
207 << Decl << BinOp->getOpcodeStr();
210 if (Result.Nodes.getNodeAs<Stmt>(
"invalid-conversion")) {
211 diag(Call->getLocStart(),
"function %0 has suspicious implicit cast")
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
LangOptions getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
std::map< std::string, std::string > OptionMap
static const char KnownStringCompareFunctions[]
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.