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