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 if (!getLangOpts().CPlusPlus) 63 return; 64 65 const auto SingleChar = 66 expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"))); 67 const auto StringFindFunctions = 68 hasAnyName("find", "rfind", "find_first_of", "find_first_not_of", 69 "find_last_of", "find_last_not_of"); 70 71 Finder->addMatcher( 72 cxxMemberCallExpr( 73 callee(functionDecl(StringFindFunctions).bind("func")), 74 anyOf(argumentCountIs(1), argumentCountIs(2)), 75 hasArgument(0, SingleChar), 76 on(expr( 77 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( 78 recordDecl(hasAnyName(SmallVector<StringRef, 4>( 79 StringLikeClasses.begin(), StringLikeClasses.end()))))))), 80 unless(hasSubstitutedType())))), 81 this); 82 } 83 84 void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { 85 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal"); 86 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func"); 87 88 auto Replacement = MakeCharacterLiteral(Literal); 89 if (!Replacement) 90 return; 91 92 diag(Literal->getBeginLoc(), "%0 called with a string literal consisting of " 93 "a single character; consider using the more " 94 "effective overload accepting a character") 95 << FindFunc 96 << FixItHint::CreateReplacement( 97 CharSourceRange::getTokenRange(Literal->getBeginLoc(), 98 Literal->getEndLoc()), 99 *Replacement); 100 } 101 102 } // namespace performance 103 } // namespace tidy 104 } // namespace clang 105