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) return llvm::None; 34 Result[pos] = '\''; 35 pos = Result.find_last_of('"'); 36 if (pos == Result.npos) return llvm::None; 37 Result[pos] = '\''; 38 return Result; 39 } 40 41 AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>, 42 hasSubstitutedType) { 43 return hasType(qualType(anyOf(substTemplateTypeParmType(), 44 hasDescendant(substTemplateTypeParmType())))); 45 } 46 47 } // namespace 48 49 FasterStringFindCheck::FasterStringFindCheck(StringRef Name, 50 ClangTidyContext *Context) 51 : ClangTidyCheck(Name, Context), 52 StringLikeClasses(utils::options::parseStringList( 53 Options.get("StringLikeClasses", "std::basic_string"))) { 54 } 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 68 const auto StringFindFunctions = 69 anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"), 70 hasName("find_first_not_of"), hasName("find_last_of"), 71 hasName("find_last_not_of")); 72 73 llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass; 74 75 for (const auto &ClassName : StringLikeClasses) { 76 const auto HasName = hasName(ClassName); 77 IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName; 78 } 79 80 if (IsStringClass) { 81 Finder->addMatcher( 82 cxxMemberCallExpr( 83 callee(functionDecl(StringFindFunctions).bind("func")), 84 anyOf(argumentCountIs(1), argumentCountIs(2)), 85 hasArgument(0, SingleChar), 86 on(expr(hasType(recordDecl(*IsStringClass)), 87 unless(hasSubstitutedType())))), 88 this); 89 } 90 } 91 92 void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { 93 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal"); 94 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func"); 95 96 auto Replacement = MakeCharacterLiteral(Literal); 97 if (!Replacement) 98 return; 99 100 diag(Literal->getLocStart(), "%0 called with a string literal consisting of " 101 "a single character; consider using the more " 102 "effective overload accepting a character") 103 << FindFunc << FixItHint::CreateReplacement( 104 CharSourceRange::getTokenRange(Literal->getLocStart(), 105 Literal->getLocEnd()), 106 *Replacement); 107 } 108 109 } // namespace performance 110 } // namespace tidy 111 } // namespace clang 112