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 using namespace clang::ast_matchers; 19 20 namespace clang { 21 namespace tidy { 22 namespace abseil { 23 24 using ::clang::transformer::applyFirst; 25 using ::clang::transformer::cat; 26 using ::clang::transformer::change; 27 using ::clang::transformer::makeRule; 28 using ::clang::transformer::node; 29 30 static const char DefaultStringLikeClasses[] = "::std::basic_string;" 31 "::std::basic_string_view;" 32 "::absl::string_view"; 33 static const char DefaultAbseilStringsMatchHeader[] = "absl/strings/match.h"; 34 35 static llvm::Optional<transformer::RewriteRule> 36 MakeRule(const LangOptions &LangOpts, 37 const ClangTidyCheck::OptionsView &Options) { 38 // Parse options. 39 // 40 // FIXME(tdl-g): These options are being parsed redundantly with the 41 // constructor because TransformerClangTidyCheck forces us to provide MakeRule 42 // before "this" is fully constructed, but StoreOptions requires us to store 43 // the parsed options in "this". We need to fix TransformerClangTidyCheck and 44 // then we can clean this up. 45 const std::vector<std::string> StringLikeClassNames = 46 utils::options::parseStringList( 47 Options.get("StringLikeClasses", DefaultStringLikeClasses)); 48 const std::string AbseilStringsMatchHeader = 49 Options.get("AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader); 50 51 auto StringLikeClass = cxxRecordDecl(hasAnyName(SmallVector<StringRef, 4>( 52 StringLikeClassNames.begin(), StringLikeClassNames.end()))); 53 auto StringType = 54 hasUnqualifiedDesugaredType(recordType(hasDeclaration(StringLikeClass))); 55 auto CharStarType = 56 hasUnqualifiedDesugaredType(pointerType(pointee(isAnyCharacter()))); 57 auto StringNpos = declRefExpr( 58 to(varDecl(hasName("npos"), hasDeclContext(StringLikeClass)))); 59 auto StringFind = cxxMemberCallExpr( 60 callee(cxxMethodDecl( 61 hasName("find"), 62 hasParameter(0, parmVarDecl(anyOf(hasType(StringType), 63 hasType(CharStarType)))))), 64 on(hasType(StringType)), hasArgument(0, expr().bind("parameter_to_find")), 65 anyOf(hasArgument(1, integerLiteral(equals(0))), 66 hasArgument(1, cxxDefaultArgExpr())), 67 onImplicitObjectArgument(expr().bind("string_being_searched"))); 68 69 tooling::RewriteRule rule = applyFirst( 70 {makeRule(binaryOperator(hasOperatorName("=="), 71 hasOperands(ignoringParenImpCasts(StringNpos), 72 ignoringParenImpCasts(StringFind))), 73 change(cat("!absl::StrContains(", node("string_being_searched"), 74 ", ", node("parameter_to_find"), ")")), 75 cat("use !absl::StrContains instead of find() == npos")), 76 makeRule(binaryOperator(hasOperatorName("!="), 77 hasOperands(ignoringParenImpCasts(StringNpos), 78 ignoringParenImpCasts(StringFind))), 79 change(cat("absl::StrContains(", node("string_being_searched"), 80 ", ", node("parameter_to_find"), ")")), 81 cat("use absl::StrContains instead of find() != npos"))}); 82 addInclude(rule, AbseilStringsMatchHeader); 83 return rule; 84 } 85 86 StringFindStrContainsCheck::StringFindStrContainsCheck( 87 StringRef Name, ClangTidyContext *Context) 88 : TransformerClangTidyCheck(&MakeRule, Name, Context), 89 StringLikeClassesOption(utils::options::parseStringList( 90 Options.get("StringLikeClasses", DefaultStringLikeClasses))), 91 AbseilStringsMatchHeaderOption(Options.get( 92 "AbseilStringsMatchHeader", DefaultAbseilStringsMatchHeader)) {} 93 94 bool StringFindStrContainsCheck::isLanguageVersionSupported( 95 const LangOptions &LangOpts) const { 96 return LangOpts.CPlusPlus11; 97 } 98 99 void StringFindStrContainsCheck::storeOptions( 100 ClangTidyOptions::OptionMap &Opts) { 101 TransformerClangTidyCheck::storeOptions(Opts); 102 Options.store(Opts, "StringLikeClasses", 103 utils::options::serializeStringList(StringLikeClassesOption)); 104 Options.store(Opts, "AbseilStringsMatchHeader", 105 AbseilStringsMatchHeaderOption); 106 } 107 108 } // namespace abseil 109 } // namespace tidy 110 } // namespace clang 111