blob: ea43d352ca76f9a0aae5cb7a54947a02ce73b197 [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"
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
16using namespace clang::ast_matchers;
17
18namespace clang {
19namespace tidy {
20namespace performance {
21
22namespace {
23
24static const char StringLikeClassesDelimiter[] = ";";
25
26std::vector<std::string> ParseClasses(StringRef Option) {
27 SmallVector<StringRef, 4> Classes;
28 Option.split(Classes, StringLikeClassesDelimiter);
29 std::vector<std::string> Result;
30 for (StringRef &Class : Classes) {
31 Class = Class.trim();
32 if (!Class.empty())
33 Result.push_back(Class);
34 }
35 return Result;
36}
37
38llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) {
39 std::string Result;
40 {
41 llvm::raw_string_ostream OS(Result);
42 Literal->outputString(OS);
43 }
44 // Now replace the " with '.
45 auto pos = Result.find_first_of('"');
46 if (pos == Result.npos) return llvm::None;
47 Result[pos] = '\'';
48 pos = Result.find_last_of('"');
49 if (pos == Result.npos) return llvm::None;
50 Result[pos] = '\'';
51 return Result;
52}
53
54AST_MATCHER(StringLiteral, lengthIsOne) { return Node.getLength() == 1; }
55
56AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
57 hasSubstitutedType) {
58 return hasType(qualType(anyOf(substTemplateTypeParmType(),
59 hasDescendant(substTemplateTypeParmType()))));
60}
61
62} // namespace
63
64FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
65 ClangTidyContext *Context)
66 : ClangTidyCheck(Name, Context),
67 StringLikeClasses(
68 ParseClasses(Options.get("StringLikeClasses", "std::basic_string"))) {
69}
70
71void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
72 Options.store(Opts, "StringLikeClasses",
73 llvm::join(StringLikeClasses.begin(), StringLikeClasses.end(),
74 StringLikeClassesDelimiter));
75}
76
77void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
78 if (!getLangOpts().CPlusPlus)
79 return;
80
81 const auto SingleChar =
82 expr(ignoringParenCasts(stringLiteral(lengthIsOne()).bind("literal")));
83
84 const auto StringFindFunctions =
85 anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"),
86 hasName("find_first_not_of"), hasName("find_last_of"),
87 hasName("find_last_not_of"));
88
89 llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass;
90
91 for (const auto &ClassName : StringLikeClasses) {
92 const auto HasName = hasName(ClassName);
93 IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName;
94 }
95
96 if (IsStringClass) {
97 Finder->addMatcher(
98 cxxMemberCallExpr(
99 callee(functionDecl(StringFindFunctions).bind("func")),
100 anyOf(argumentCountIs(1), argumentCountIs(2)),
101 hasArgument(0, SingleChar),
102 on(expr(hasType(recordDecl(*IsStringClass)),
103 unless(hasSubstitutedType())))),
104 this);
105 }
106}
107
108void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
109 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
110 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
111
112 auto Replacement = MakeCharacterLiteral(Literal);
113 if (!Replacement)
114 return;
115
116 diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
117 "a single character; consider using the more "
118 "effective overload accepting a character")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000119 << FindFunc << FixItHint::CreateReplacement(
120 CharSourceRange::getTokenRange(Literal->getLocStart(),
121 Literal->getLocEnd()),
122 *Replacement);
Samuel Benzaquen51e15232016-02-12 19:28:14 +0000123}
124
125} // namespace performance
126} // namespace tidy
127} // namespace clang