blob: 29dac1bc829af309092545630a2091212e9d4786 [file] [log] [blame]
Samuel Benzaquen51e15232016-02-12 19:28:14 +00001//===--- 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"
Etienne Bergeronde1ec032016-05-10 15:31:15 +000011#include "../utils/OptionsUtils.h"
Samuel Benzaquen51e15232016-02-12 19:28:14 +000012#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
17using namespace clang::ast_matchers;
18
19namespace clang {
20namespace tidy {
21namespace performance {
22
23namespace {
24
Samuel Benzaquen51e15232016-02-12 19:28:14 +000025llvm::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('"');
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000033 if (pos == Result.npos)
34 return llvm::None;
Samuel Benzaquen51e15232016-02-12 19:28:14 +000035 Result[pos] = '\'';
36 pos = Result.find_last_of('"');
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000037 if (pos == Result.npos)
38 return llvm::None;
Samuel Benzaquen51e15232016-02-12 19:28:14 +000039 Result[pos] = '\'';
40 return Result;
41}
42
Samuel Benzaquen51e15232016-02-12 19:28:14 +000043AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
44 hasSubstitutedType) {
45 return hasType(qualType(anyOf(substTemplateTypeParmType(),
46 hasDescendant(substTemplateTypeParmType()))));
47}
48
49} // namespace
50
51FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
52 ClangTidyContext *Context)
53 : ClangTidyCheck(Name, Context),
Etienne Bergeronde1ec032016-05-10 15:31:15 +000054 StringLikeClasses(utils::options::parseStringList(
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000055 Options.get("StringLikeClasses", "std::basic_string"))) {}
Samuel Benzaquen51e15232016-02-12 19:28:14 +000056
57void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
58 Options.store(Opts, "StringLikeClasses",
Etienne Bergeronde1ec032016-05-10 15:31:15 +000059 utils::options::serializeStringList(StringLikeClasses));
Samuel Benzaquen51e15232016-02-12 19:28:14 +000060}
61
62void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
63 if (!getLangOpts().CPlusPlus)
64 return;
65
66 const auto SingleChar =
Etienne Bergerone15ef2f2016-05-17 19:36:09 +000067 expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
Samuel Benzaquen51e15232016-02-12 19:28:14 +000068 const auto StringFindFunctions =
Haojian Wu8fd72962017-04-24 16:41:00 +000069 hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
70 "find_last_of", "find_last_not_of");
Samuel Benzaquen51e15232016-02-12 19:28:14 +000071
Haojian Wu8fd72962017-04-24 16:41:00 +000072 Finder->addMatcher(
73 cxxMemberCallExpr(
74 callee(functionDecl(StringFindFunctions).bind("func")),
75 anyOf(argumentCountIs(1), argumentCountIs(2)),
76 hasArgument(0, SingleChar),
77 on(expr(hasType(recordDecl(hasAnyName(SmallVector<StringRef, 4>(
78 StringLikeClasses.begin(), StringLikeClasses.end())))),
79 unless(hasSubstitutedType())))),
80 this);
Samuel Benzaquen51e15232016-02-12 19:28:14 +000081}
82
83void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
84 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
85 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
86
87 auto Replacement = MakeCharacterLiteral(Literal);
88 if (!Replacement)
89 return;
90
91 diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
92 "a single character; consider using the more "
93 "effective overload accepting a character")
Benjamin Kramera62e2232016-04-07 14:55:25 +000094 << FindFunc << FixItHint::CreateReplacement(
95 CharSourceRange::getTokenRange(Literal->getLocStart(),
96 Literal->getLocEnd()),
97 *Replacement);
Samuel Benzaquen51e15232016-02-12 19:28:14 +000098}
99
100} // namespace performance
101} // namespace tidy
102} // namespace clang