blob: 893458f49cd87c398647d1e197a85dde7a096c09 [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('"');
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
41AST_MATCHER(StringLiteral, lengthIsOne) { return Node.getLength() == 1; }
42
43AST_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(
55 Options.get("StringLikeClasses", "std::basic_string"))) {
Samuel Benzaquen51e15232016-02-12 19:28:14 +000056}
57
58void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
59 Options.store(Opts, "StringLikeClasses",
Etienne Bergeronde1ec032016-05-10 15:31:15 +000060 utils::options::serializeStringList(StringLikeClasses));
Samuel Benzaquen51e15232016-02-12 19:28:14 +000061}
62
63void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
64 if (!getLangOpts().CPlusPlus)
65 return;
66
67 const auto SingleChar =
68 expr(ignoringParenCasts(stringLiteral(lengthIsOne()).bind("literal")));
69
70 const auto StringFindFunctions =
71 anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"),
72 hasName("find_first_not_of"), hasName("find_last_of"),
73 hasName("find_last_not_of"));
74
75 llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass;
76
77 for (const auto &ClassName : StringLikeClasses) {
78 const auto HasName = hasName(ClassName);
79 IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName;
80 }
81
82 if (IsStringClass) {
83 Finder->addMatcher(
84 cxxMemberCallExpr(
85 callee(functionDecl(StringFindFunctions).bind("func")),
86 anyOf(argumentCountIs(1), argumentCountIs(2)),
87 hasArgument(0, SingleChar),
88 on(expr(hasType(recordDecl(*IsStringClass)),
89 unless(hasSubstitutedType())))),
90 this);
91 }
92}
93
94void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
95 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
96 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
97
98 auto Replacement = MakeCharacterLiteral(Literal);
99 if (!Replacement)
100 return;
101
102 diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
103 "a single character; consider using the more "
104 "effective overload accepting a character")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000105 << FindFunc << FixItHint::CreateReplacement(
106 CharSourceRange::getTokenRange(Literal->getLocStart(),
107 Literal->getLocEnd()),
108 *Replacement);
Samuel Benzaquen51e15232016-02-12 19:28:14 +0000109}
110
111} // namespace performance
112} // namespace tidy
113} // namespace clang