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