blob: a8a9f662ee1991281bdad5e086529ecd09c64a44 [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
69 const auto StringFindFunctions =
70 anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"),
71 hasName("find_first_not_of"), hasName("find_last_of"),
72 hasName("find_last_not_of"));
73
74 llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass;
75
76 for (const auto &ClassName : StringLikeClasses) {
77 const auto HasName = hasName(ClassName);
78 IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName;
79 }
80
81 if (IsStringClass) {
82 Finder->addMatcher(
83 cxxMemberCallExpr(
84 callee(functionDecl(StringFindFunctions).bind("func")),
85 anyOf(argumentCountIs(1), argumentCountIs(2)),
86 hasArgument(0, SingleChar),
87 on(expr(hasType(recordDecl(*IsStringClass)),
88 unless(hasSubstitutedType())))),
89 this);
90 }
91}
92
93void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
94 const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
95 const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
96
97 auto Replacement = MakeCharacterLiteral(Literal);
98 if (!Replacement)
99 return;
100
101 diag(Literal->getLocStart(), "%0 called with a string literal consisting of "
102 "a single character; consider using the more "
103 "effective overload accepting a character")
Benjamin Kramera62e2232016-04-07 14:55:25 +0000104 << FindFunc << FixItHint::CreateReplacement(
105 CharSourceRange::getTokenRange(Literal->getLocStart(),
106 Literal->getLocEnd()),
107 *Replacement);
Samuel Benzaquen51e15232016-02-12 19:28:14 +0000108}
109
110} // namespace performance
111} // namespace tidy
112} // namespace clang