1 //===--- StringFindStrContainsCheck.cc - clang-tidy------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "StringFindStrContainsCheck.h"
10 
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Tooling/Transformer/RewriteRule.h"
16 #include "clang/Tooling/Transformer/Stencil.h"
17 
18 // FixItHint - Hint to check documentation script to mark this check as
19 // providing a FixIt.
20 
21 using namespace clang::ast_matchers;
22 
23 namespace clang {
24 namespace tidy {
25 namespace abseil {
26 
27 using ::clang::transformer::addInclude;
28 using ::clang::transformer::applyFirst;
29 using ::clang::transformer::cat;
30 using ::clang::transformer::changeTo;
31 using ::clang::transformer::makeRule;
32 using ::clang::transformer::node;
33 using ::clang::transformer::RewriteRule;
34 
35 AST_MATCHER(Type, isCharType) { return Node.isCharType(); }
36 
37 static const char DefaultStringLikeClasses[] = "::std::basic_string;"
38                                                "::std::basic_string_view;"
39                                                "::absl::string_view";
40 static const char DefaultAbseilStringsMatchHeader[] = "absl/strings/match.h";
41 
42 static transformer::RewriteRule
43 makeRewriteRule(const std::vector<std::string> &StringLikeClassNames,
44                 StringRef AbseilStringsMatchHeader) {
45   auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>(
46       StringLikeClassNames.begin(), StringLikeClassNames.end())));
47   auto StringType =
48       hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass)));
49   auto CharStarType =
50       hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter())));
51   auto CharType = hasUnqualifiedDesugaredType(isCharType());
52   auto StringNpos = declRefExpr(
53       to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass))));
54   auto StringFind = cxxMemberCallExpr(
55       callee(cxxMethodDecl(
56           hasName("find"), parameterCountIs(2),
57           hasParameter(
58               0, parmVarDecl(anyOf(hasType(StringType), hasType(CharStarType),
59                                    hasType(CharType)))))),
60       on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")),
61       anyOf(hasArgument(1, integerLiteral(equals(0))),
62             hasArgument(1, cxxDefaultArgExpr())),
63       onImplicitObjectArgument(expr().bind("string_being_searched")));
64 
65   RewriteRule Rule = applyFirst(
66       {makeRule(
67            binaryOperator(hasOperatorName("=="),
68                           hasOperands(ignoringParenImpCasts(StringNpos),
69                                       ignoringParenImpCasts(StringFind))),
70            {changeTo(cat("!absl::StrContains(", node("string_being_searched"),
71                          ", ", node("parameter_to_find"), ")")),
72             addInclude(AbseilStringsMatchHeader)},
73            cat("use !absl::StrContains instead of find() == npos")),
74        makeRule(
75            binaryOperator(hasOperatorName("!="),
76                           hasOperands(ignoringParenImpCasts(StringNpos),
77                                       ignoringParenImpCasts(StringFind))),
78            {changeTo(cat("absl::StrContains(", node("string_being_searched"),
79                          ", ", node("parameter_to_find"), ")")),
80             addInclude(AbseilStringsMatchHeader)},
81            cat("use absl::StrContains instead "
82                "of find() != npos"))});
83   return Rule;
84 }
85 
86 StringFindStrContainsCheck::StringFindStrContainsCheck(
87     StringRef Name, ClangTidyContext *Context)
88     : TransformerClangTidyCheck(Name, Context),
89       StringLikeClassesOption(utils::options::parseStringList(
90           Options.get("StringLikeClasses", DefaultStringLikeClasses))),
91       AbseilStringsMatchHeaderOption(Options.get(
92           "AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {
93   setRule(
94       makeRewriteRule(StringLikeClassesOption, AbseilStringsMatchHeaderOption));
95 }
96 
97 bool StringFindStrContainsCheck::isLanguageVersionSupported(
98     const LangOptions &LangOpts) const {
99   return LangOpts.CPlusPlus11;
100 }
101 
102 void StringFindStrContainsCheck::storeOptions(
103     ClangTidyOptions::OptionMap &Opts) {
104   TransformerClangTidyCheck::storeOptions(Opts);
105   Options.store(Opts, "StringLikeClasses",
106                 utils::options::serializeStringList(StringLikeClassesOption));
107   Options.store(Opts, "AbseilStringsMatchHeader",
108                 AbseilStringsMatchHeaderOption);
109 }
110 
111 } // namespace abseil
112 } // namespace tidy
113 } // namespace clang
114