xref: /llvm-project/clang-tools-extra/clang-tidy/abseil/StringFindStrContainsCheck.cpp (revision 1fdb3e36ff379e5b3b05a00d49b6081435df727a)
17cfdff7bSTom Lokovic //===--- StringFindStrContainsCheck.cc - clang-tidy------------------------===//
27cfdff7bSTom Lokovic //
37cfdff7bSTom Lokovic // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47cfdff7bSTom Lokovic // See https://llvm.org/LICENSE.txt for license information.
57cfdff7bSTom Lokovic // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67cfdff7bSTom Lokovic //
77cfdff7bSTom Lokovic //===----------------------------------------------------------------------===//
87cfdff7bSTom Lokovic 
97cfdff7bSTom Lokovic #include "StringFindStrContainsCheck.h"
107cfdff7bSTom Lokovic 
117cfdff7bSTom Lokovic #include "../utils/OptionsUtils.h"
127cfdff7bSTom Lokovic #include "clang/AST/ASTContext.h"
137cfdff7bSTom Lokovic #include "clang/ASTMatchers/ASTMatchers.h"
147cfdff7bSTom Lokovic #include "clang/Frontend/CompilerInstance.h"
157cfdff7bSTom Lokovic #include "clang/Tooling/Transformer/RewriteRule.h"
167cfdff7bSTom Lokovic #include "clang/Tooling/Transformer/Stencil.h"
177cfdff7bSTom Lokovic 
1823063296SNathan James // FixItHint - Hint to check documentation script to mark this check as
1923063296SNathan James // providing a FixIt.
2023063296SNathan James 
217cfdff7bSTom Lokovic using namespace clang::ast_matchers;
227cfdff7bSTom Lokovic 
237cfdff7bSTom Lokovic namespace clang {
247cfdff7bSTom Lokovic namespace tidy {
257cfdff7bSTom Lokovic namespace abseil {
267cfdff7bSTom Lokovic 
279eb2284fSYitzhak Mandelbaum using ::clang::transformer::addInclude;
287cfdff7bSTom Lokovic using ::clang::transformer::applyFirst;
297cfdff7bSTom Lokovic using ::clang::transformer::cat;
309eb2284fSYitzhak Mandelbaum using ::clang::transformer::changeTo;
317cfdff7bSTom Lokovic using ::clang::transformer::makeRule;
327cfdff7bSTom Lokovic using ::clang::transformer::node;
33fdff677aSYitzhak Mandelbaum using ::clang::transformer::RewriteRule;
347cfdff7bSTom Lokovic 
3516622d53SChris Kennelly AST_MATCHER(Type, isCharType) { return Node.isCharType(); }
3616622d53SChris Kennelly 
377cfdff7bSTom Lokovic static const char DefaultStringLikeClasses[] = "::std::basic_string;"
387cfdff7bSTom Lokovic                                                "::std::basic_string_view;"
397cfdff7bSTom Lokovic                                                "::absl::string_view";
407cfdff7bSTom Lokovic static const char DefaultAbseilStringsMatchHeader[] = "absl/strings/match.h";
417cfdff7bSTom Lokovic 
429eb2284fSYitzhak Mandelbaum static transformer::RewriteRule
439eb2284fSYitzhak Mandelbaum makeRewriteRule(const std::vector<std::string> &StringLikeClassNames,
449eb2284fSYitzhak Mandelbaum                 StringRef AbseilStringsMatchHeader) {
457cfdff7bSTom Lokovic   auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>(
467cfdff7bSTom Lokovic       StringLikeClassNames.begin(), StringLikeClassNames.end())));
477cfdff7bSTom Lokovic   auto StringType =
487cfdff7bSTom Lokovic       hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass)));
497cfdff7bSTom Lokovic   auto CharStarType =
507cfdff7bSTom Lokovic       hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter())));
5116622d53SChris Kennelly   auto CharType = hasUnqualifiedDesugaredType(isCharType());
527cfdff7bSTom Lokovic   auto StringNpos = declRefExpr(
537cfdff7bSTom Lokovic       to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass))));
547cfdff7bSTom Lokovic   auto StringFind = cxxMemberCallExpr(
557cfdff7bSTom Lokovic       callee(cxxMethodDecl(
56*1fdb3e36STom Lokovic           hasName("find"), parameterCountIs(2),
5716622d53SChris Kennelly           hasParameter(
5816622d53SChris Kennelly               0, parmVarDecl(anyOf(hasType(StringType), hasType(CharStarType),
5916622d53SChris Kennelly                                    hasType(CharType)))))),
607cfdff7bSTom Lokovic       on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")),
617cfdff7bSTom Lokovic       anyOf(hasArgument(1, integerLiteral(equals(0))),
627cfdff7bSTom Lokovic             hasArgument(1, cxxDefaultArgExpr())),
637cfdff7bSTom Lokovic       onImplicitObjectArgument(expr().bind("string_being_searched")));
647cfdff7bSTom Lokovic 
65ab2d3ce4SAlexander Kornienko   RewriteRule Rule = applyFirst(
669eb2284fSYitzhak Mandelbaum       {makeRule(
679eb2284fSYitzhak Mandelbaum            binaryOperator(hasOperatorName("=="),
687cfdff7bSTom Lokovic                           hasOperands(ignoringParenImpCasts(StringNpos),
697cfdff7bSTom Lokovic                                       ignoringParenImpCasts(StringFind))),
709eb2284fSYitzhak Mandelbaum            {changeTo(cat("!absl::StrContains(", node("string_being_searched"),
717cfdff7bSTom Lokovic                          ", ", node("parameter_to_find"), ")")),
729eb2284fSYitzhak Mandelbaum             addInclude(AbseilStringsMatchHeader)},
737cfdff7bSTom Lokovic            cat("use !absl::StrContains instead of find() == npos")),
749eb2284fSYitzhak Mandelbaum        makeRule(
759eb2284fSYitzhak Mandelbaum            binaryOperator(hasOperatorName("!="),
767cfdff7bSTom Lokovic                           hasOperands(ignoringParenImpCasts(StringNpos),
777cfdff7bSTom Lokovic                                       ignoringParenImpCasts(StringFind))),
789eb2284fSYitzhak Mandelbaum            {changeTo(cat("absl::StrContains(", node("string_being_searched"),
797cfdff7bSTom Lokovic                          ", ", node("parameter_to_find"), ")")),
809eb2284fSYitzhak Mandelbaum             addInclude(AbseilStringsMatchHeader)},
819eb2284fSYitzhak Mandelbaum            cat("use absl::StrContains instead "
829eb2284fSYitzhak Mandelbaum                "of find() != npos"))});
83ab2d3ce4SAlexander Kornienko   return Rule;
847cfdff7bSTom Lokovic }
857cfdff7bSTom Lokovic 
867cfdff7bSTom Lokovic StringFindStrContainsCheck::StringFindStrContainsCheck(
877cfdff7bSTom Lokovic     StringRef Name, ClangTidyContext *Context)
889eb2284fSYitzhak Mandelbaum     : TransformerClangTidyCheck(Name, Context),
897cfdff7bSTom Lokovic       StringLikeClassesOption(utils::options::parseStringList(
907cfdff7bSTom Lokovic           Options.get("StringLikeClasses", DefaultStringLikeClasses))),
917cfdff7bSTom Lokovic       AbseilStringsMatchHeaderOption(Options.get(
929eb2284fSYitzhak Mandelbaum           "AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {
939eb2284fSYitzhak Mandelbaum   setRule(
949eb2284fSYitzhak Mandelbaum       makeRewriteRule(StringLikeClassesOption, AbseilStringsMatchHeaderOption));
959eb2284fSYitzhak Mandelbaum }
967cfdff7bSTom Lokovic 
977cfdff7bSTom Lokovic bool StringFindStrContainsCheck::isLanguageVersionSupported(
987cfdff7bSTom Lokovic     const LangOptions &LangOpts) const {
997cfdff7bSTom Lokovic   return LangOpts.CPlusPlus11;
1007cfdff7bSTom Lokovic }
1017cfdff7bSTom Lokovic 
1027cfdff7bSTom Lokovic void StringFindStrContainsCheck::storeOptions(
1037cfdff7bSTom Lokovic     ClangTidyOptions::OptionMap &Opts) {
1047cfdff7bSTom Lokovic   TransformerClangTidyCheck::storeOptions(Opts);
1057cfdff7bSTom Lokovic   Options.store(Opts, "StringLikeClasses",
1067cfdff7bSTom Lokovic                 utils::options::serializeStringList(StringLikeClassesOption));
1077cfdff7bSTom Lokovic   Options.store(Opts, "AbseilStringsMatchHeader",
1087cfdff7bSTom Lokovic                 AbseilStringsMatchHeaderOption);
1097cfdff7bSTom Lokovic }
1107cfdff7bSTom Lokovic 
1117cfdff7bSTom Lokovic } // namespace abseil
1127cfdff7bSTom Lokovic } // namespace tidy
1137cfdff7bSTom Lokovic } // namespace clang
114