blob: 0c3d249fd36b0bd303c1a59d34ffd475fbac7df2 [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),
Manuel Klimek7b9c1172017-08-02 13:13:11 +000077 on(expr(
78 hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
79 recordDecl(hasAnyName(SmallVector<StringRef, 4>(
80 StringLikeClasses.begin(), StringLikeClasses.end()))))))),
81 unless(hasSubstitutedType())))),
Haojian Wu8fd72962017-04-24 16:41:00 +000082 this);
Samuel Benzaquen51e15232016-02-12 19:28:14 +000083}
84
85void 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
Stephen Kelly43465bf2018-08-09 22:42:26 +000093 diag(Literal->getBeginLoc(), "%0 called with a string literal consisting of "
Samuel Benzaquen51e15232016-02-12 19:28:14 +000094 "a single character; consider using the more "
95 "effective overload accepting a character")
Stephen Kelly43465bf2018-08-09 22:42:26 +000096 << FindFunc
97 << FixItHint::CreateReplacement(
98 CharSourceRange::getTokenRange(Literal->getBeginLoc(),
Stephen Kellyc09197e2018-08-09 22:43:02 +000099 Literal->getEndLoc()),
Stephen Kelly43465bf2018-08-09 22:42:26 +0000100 *Replacement);
Samuel Benzaquen51e15232016-02-12 19:28:14 +0000101}
102
103} // namespace performance
104} // namespace tidy
105} // namespace clang