1 //===--- FasterStringFindCheck.cpp - clang-tidy----------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "FasterStringFindCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/Support/raw_ostream.h"
16 using namespace clang::ast_matchers
;
18 namespace clang::tidy::performance
{
22 std::optional
<std::string
> makeCharacterLiteral(const StringLiteral
*Literal
) {
25 llvm::raw_string_ostream
OS(Result
);
26 Literal
->outputString(OS
);
28 // Now replace the " with '.
29 auto OpenPos
= Result
.find_first_of('"');
30 if (OpenPos
== std::string::npos
)
32 Result
[OpenPos
] = '\'';
34 auto ClosePos
= Result
.find_last_of('"');
35 if (ClosePos
== std::string::npos
)
37 Result
[ClosePos
] = '\'';
39 // "'" is OK, but ''' is not, so add a backslash
40 if ((ClosePos
- OpenPos
) == 2 && Result
[OpenPos
+ 1] == '\'')
41 Result
.replace(OpenPos
+ 1, 1, "\\'");
46 AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher
<Expr
>,
48 return hasType(qualType(anyOf(substTemplateTypeParmType(),
49 hasDescendant(substTemplateTypeParmType()))));
54 FasterStringFindCheck::FasterStringFindCheck(StringRef Name
,
55 ClangTidyContext
*Context
)
56 : ClangTidyCheck(Name
, Context
),
57 StringLikeClasses(utils::options::parseStringList(
58 Options
.get("StringLikeClasses",
59 "::std::basic_string;::std::basic_string_view"))) {}
61 void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
62 Options
.store(Opts
, "StringLikeClasses",
63 utils::options::serializeStringList(StringLikeClasses
));
66 void FasterStringFindCheck::registerMatchers(MatchFinder
*Finder
) {
67 const auto SingleChar
=
68 expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
69 const auto StringFindFunctions
=
70 hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
71 "find_last_of", "find_last_not_of");
75 callee(functionDecl(StringFindFunctions
).bind("func")),
76 anyOf(argumentCountIs(1), argumentCountIs(2)),
77 hasArgument(0, SingleChar
),
78 on(expr(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
79 recordDecl(hasAnyName(StringLikeClasses
)))))),
80 unless(hasSubstitutedType())))),
84 void FasterStringFindCheck::check(const MatchFinder::MatchResult
&Result
) {
85 const auto *Literal
= Result
.Nodes
.getNodeAs
<StringLiteral
>("literal");
86 const auto *FindFunc
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("func");
88 auto Replacement
= makeCharacterLiteral(Literal
);
92 diag(Literal
->getBeginLoc(), "%0 called with a string literal consisting of "
93 "a single character; consider using the more "
94 "effective overload accepting a character")
96 << FixItHint::CreateReplacement(
97 CharSourceRange::getTokenRange(Literal
->getBeginLoc(),
98 Literal
->getEndLoc()),
102 } // namespace clang::tidy::performance