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::RewriteRuleWith;
34
AST_MATCHER(Type,isCharType)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::RewriteRuleWith<std::string>
makeRewriteRule(ArrayRef<StringRef> StringLikeClassNames,StringRef AbseilStringsMatchHeader)43 makeRewriteRule(ArrayRef<StringRef> StringLikeClassNames,
44 StringRef AbseilStringsMatchHeader) {
45 auto StringLikeClass = cxxRecordDecl(hasAnyName(StringLikeClassNames));
46 auto StringType =
47 hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass)));
48 auto CharStarType =
49 hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter())));
50 auto CharType = hasUnqualifiedDesugaredType(isCharType());
51 auto StringNpos = declRefExpr(
52 to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass))));
53 auto StringFind = cxxMemberCallExpr(
54 callee(cxxMethodDecl(
55 hasName("find"), parameterCountIs(2),
56 hasParameter(
57 0, parmVarDecl(anyOf(hasType(StringType), hasType(CharStarType),
58 hasType(CharType)))))),
59 on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")),
60 anyOf(hasArgument(1, integerLiteral(equals(0))),
61 hasArgument(1, cxxDefaultArgExpr())),
62 onImplicitObjectArgument(expr().bind("string_being_searched")));
63
64 RewriteRuleWith<std::string> Rule = applyFirst(
65 {makeRule(
66 binaryOperator(hasOperatorName("=="),
67 hasOperands(ignoringParenImpCasts(StringNpos),
68 ignoringParenImpCasts(StringFind))),
69 {changeTo(cat("!absl::StrContains(", node("string_being_searched"),
70 ", ", node("parameter_to_find"), ")")),
71 addInclude(AbseilStringsMatchHeader)},
72 cat("use !absl::StrContains instead of find() == npos")),
73 makeRule(
74 binaryOperator(hasOperatorName("!="),
75 hasOperands(ignoringParenImpCasts(StringNpos),
76 ignoringParenImpCasts(StringFind))),
77 {changeTo(cat("absl::StrContains(", node("string_being_searched"),
78 ", ", node("parameter_to_find"), ")")),
79 addInclude(AbseilStringsMatchHeader)},
80 cat("use absl::StrContains instead "
81 "of find() != npos"))});
82 return Rule;
83 }
84
StringFindStrContainsCheck(StringRef Name,ClangTidyContext * Context)85 StringFindStrContainsCheck::StringFindStrContainsCheck(
86 StringRef Name, ClangTidyContext *Context)
87 : TransformerClangTidyCheck(Name, Context),
88 StringLikeClassesOption(utils::options::parseStringList(
89 Options.get("StringLikeClasses", DefaultStringLikeClasses))),
90 AbseilStringsMatchHeaderOption(Options.get(
91 "AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {
92 setRule(
93 makeRewriteRule(StringLikeClassesOption, AbseilStringsMatchHeaderOption));
94 }
95
isLanguageVersionSupported(const LangOptions & LangOpts) const96 bool StringFindStrContainsCheck::isLanguageVersionSupported(
97 const LangOptions &LangOpts) const {
98 return LangOpts.CPlusPlus11;
99 }
100
storeOptions(ClangTidyOptions::OptionMap & Opts)101 void StringFindStrContainsCheck::storeOptions(
102 ClangTidyOptions::OptionMap &Opts) {
103 TransformerClangTidyCheck::storeOptions(Opts);
104 Options.store(Opts, "StringLikeClasses",
105 utils::options::serializeStringList(StringLikeClassesOption));
106 Options.store(Opts, "AbseilStringsMatchHeader",
107 AbseilStringsMatchHeaderOption);
108 }
109
110 } // namespace abseil
111 } // namespace tidy
112 } // namespace clang
113