1 //===--- FasterStringFindCheck.cpp - 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 "FasterStringFindCheck.h" 10 #include "../utils/OptionsUtils.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "llvm/ADT/Optional.h" 14 #include "llvm/Support/raw_ostream.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace performance { 21 22 namespace { 23 24 llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) { 25 std::string Result; 26 { 27 llvm::raw_string_ostream OS(Result); 28 Literal->outputString(OS); 29 } 30 // Now replace the " with '. 31 auto pos = Result.find_first_of('"'); 32 if (pos == Result.npos) 33 return llvm::None; 34 Result[pos] = '\''; 35 pos = Result.find_last_of('"'); 36 if (pos == Result.npos) 37 return llvm::None; 38 Result[pos] = '\''; 39 return Result; 40 } 41 42 AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>, 43 hasSubstitutedType) { 44 return hasType(qualType(anyOf(substTemplateTypeParmType(), 45 hasDescendant(substTemplateTypeParmType())))); 46 } 47 48 } // namespace 49 50 FasterStringFindCheck::FasterStringFindCheck(StringRef Name, 51 ClangTidyContext *Context) 52 : ClangTidyCheck(Name, Context), 53 StringLikeClasses(utils::options::parseStringList( 54 Options.get("StringLikeClasses", "std::basic_string"))) {} 55 56 void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 57 Options.store(Opts, "StringLikeClasses", 58 utils::options::serializeStringList(StringLikeClasses)); 59 } 60 61 void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) { 62 const auto SingleChar = 63 expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"))); 64 const auto StringFindFunctions = 65 hasAnyName("find", "rfind", "find_first_of", "find_first_not_of", 66 "find_last_of", "find_last_not_of"); 67 68 Finder->addMatcher( 69 cxxMemberCallExpr( 70 callee(functionDecl(StringFindFunctions).bind("func")), 71 anyOf(argumentCountIs(1), argumentCountIs(2)), 72 hasArgument(0, SingleChar), 73 on(expr( 74 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( 75 recordDecl(hasAnyName(SmallVector<StringRef, 4>( 76 StringLikeClasses.begin(), StringLikeClasses.end()))))))), 77 unless(hasSubstitutedType())))), 78 this); 79 } 80 81 void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { 82 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal"); 83 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func"); 84 85 auto Replacement = MakeCharacterLiteral(Literal); 86 if (!Replacement) 87 return; 88 89 diag(Literal->getBeginLoc(), "%0 called with a string literal consisting of " 90 "a single character; consider using the more " 91 "effective overload accepting a character") 92 << FindFunc 93 << FixItHint::CreateReplacement( 94 CharSourceRange::getTokenRange(Literal->getBeginLoc(), 95 Literal->getEndLoc()), 96 *Replacement); 97 } 98 99 } // namespace performance 100 } // namespace tidy 101 } // namespace clang 102