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