151e1523dSSamuel Benzaquen //===--- FasterStringFindCheck.cpp - clang-tidy----------------------------===//
251e1523dSSamuel Benzaquen //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
651e1523dSSamuel Benzaquen //
751e1523dSSamuel Benzaquen //===----------------------------------------------------------------------===//
851e1523dSSamuel Benzaquen
951e1523dSSamuel Benzaquen #include "FasterStringFindCheck.h"
10de1ec037SEtienne Bergeron #include "../utils/OptionsUtils.h"
1151e1523dSSamuel Benzaquen #include "clang/AST/ASTContext.h"
1251e1523dSSamuel Benzaquen #include "clang/ASTMatchers/ASTMatchFinder.h"
1351e1523dSSamuel Benzaquen #include "llvm/Support/raw_ostream.h"
1471f55735SKazu Hirata #include <optional>
1551e1523dSSamuel Benzaquen
1651e1523dSSamuel Benzaquen using namespace clang::ast_matchers;
1751e1523dSSamuel Benzaquen
187d2ea6c4SCarlos Galvez namespace clang::tidy::performance {
1951e1523dSSamuel Benzaquen
2051e1523dSSamuel Benzaquen namespace {
2151e1523dSSamuel Benzaquen
makeCharacterLiteral(const StringLiteral * Literal)22f71ffd3bSKazu Hirata std::optional<std::string> makeCharacterLiteral(const StringLiteral *Literal) {
2351e1523dSSamuel Benzaquen std::string Result;
2451e1523dSSamuel Benzaquen {
2551e1523dSSamuel Benzaquen llvm::raw_string_ostream OS(Result);
2651e1523dSSamuel Benzaquen Literal->outputString(OS);
2751e1523dSSamuel Benzaquen }
2851e1523dSSamuel Benzaquen // Now replace the " with '.
295b95d17dSFabian Wolff auto OpenPos = Result.find_first_of('"');
30*8ba103caSPiotr Zegar if (OpenPos == std::string::npos)
31cd8702efSKazu Hirata return std::nullopt;
325b95d17dSFabian Wolff Result[OpenPos] = '\'';
335b95d17dSFabian Wolff
345b95d17dSFabian Wolff auto ClosePos = Result.find_last_of('"');
35*8ba103caSPiotr Zegar if (ClosePos == std::string::npos)
36cd8702efSKazu Hirata return std::nullopt;
375b95d17dSFabian Wolff Result[ClosePos] = '\'';
385b95d17dSFabian Wolff
395b95d17dSFabian Wolff // "'" is OK, but ''' is not, so add a backslash
405b95d17dSFabian Wolff if ((ClosePos - OpenPos) == 2 && Result[OpenPos + 1] == '\'')
415b95d17dSFabian Wolff Result.replace(OpenPos + 1, 1, "\\'");
425b95d17dSFabian Wolff
4351e1523dSSamuel Benzaquen return Result;
4451e1523dSSamuel Benzaquen }
4551e1523dSSamuel Benzaquen
AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,hasSubstitutedType)4651e1523dSSamuel Benzaquen AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
4751e1523dSSamuel Benzaquen hasSubstitutedType) {
4851e1523dSSamuel Benzaquen return hasType(qualType(anyOf(substTemplateTypeParmType(),
4951e1523dSSamuel Benzaquen hasDescendant(substTemplateTypeParmType()))));
5051e1523dSSamuel Benzaquen }
5151e1523dSSamuel Benzaquen
5251e1523dSSamuel Benzaquen } // namespace
5351e1523dSSamuel Benzaquen
FasterStringFindCheck(StringRef Name,ClangTidyContext * Context)5451e1523dSSamuel Benzaquen FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
5551e1523dSSamuel Benzaquen ClangTidyContext *Context)
5651e1523dSSamuel Benzaquen : ClangTidyCheck(Name, Context),
57de1ec037SEtienne Bergeron StringLikeClasses(utils::options::parseStringList(
582efba0e8SNathan James Options.get("StringLikeClasses",
592efba0e8SNathan James "::std::basic_string;::std::basic_string_view"))) {}
6051e1523dSSamuel Benzaquen
storeOptions(ClangTidyOptions::OptionMap & Opts)6151e1523dSSamuel Benzaquen void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
6251e1523dSSamuel Benzaquen Options.store(Opts, "StringLikeClasses",
63de1ec037SEtienne Bergeron utils::options::serializeStringList(StringLikeClasses));
6451e1523dSSamuel Benzaquen }
6551e1523dSSamuel Benzaquen
registerMatchers(MatchFinder * Finder)6651e1523dSSamuel Benzaquen void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
6751e1523dSSamuel Benzaquen const auto SingleChar =
68e15ef2f6SEtienne Bergeron expr(ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal")));
6951e1523dSSamuel Benzaquen const auto StringFindFunctions =
708fd72968SHaojian Wu hasAnyName("find", "rfind", "find_first_of", "find_first_not_of",
718fd72968SHaojian Wu "find_last_of", "find_last_not_of");
7251e1523dSSamuel Benzaquen
7351e1523dSSamuel Benzaquen Finder->addMatcher(
7451e1523dSSamuel Benzaquen cxxMemberCallExpr(
7551e1523dSSamuel Benzaquen callee(functionDecl(StringFindFunctions).bind("func")),
7651e1523dSSamuel Benzaquen anyOf(argumentCountIs(1), argumentCountIs(2)),
7751e1523dSSamuel Benzaquen hasArgument(0, SingleChar),
7812cb5405SNathan James on(expr(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
7912cb5405SNathan James recordDecl(hasAnyName(StringLikeClasses)))))),
8051e1523dSSamuel Benzaquen unless(hasSubstitutedType())))),
8151e1523dSSamuel Benzaquen this);
8251e1523dSSamuel Benzaquen }
8351e1523dSSamuel Benzaquen
check(const MatchFinder::MatchResult & Result)8451e1523dSSamuel Benzaquen void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) {
8551e1523dSSamuel Benzaquen const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal");
8651e1523dSSamuel Benzaquen const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
8751e1523dSSamuel Benzaquen
88ab2d3ce4SAlexander Kornienko auto Replacement = makeCharacterLiteral(Literal);
8951e1523dSSamuel Benzaquen if (!Replacement)
9051e1523dSSamuel Benzaquen return;
9151e1523dSSamuel Benzaquen
9243465bf3SStephen Kelly diag(Literal->getBeginLoc(), "%0 called with a string literal consisting of "
9351e1523dSSamuel Benzaquen "a single character; consider using the more "
9451e1523dSSamuel Benzaquen "effective overload accepting a character")
9543465bf3SStephen Kelly << FindFunc
9643465bf3SStephen Kelly << FixItHint::CreateReplacement(
9743465bf3SStephen Kelly CharSourceRange::getTokenRange(Literal->getBeginLoc(),
98c09197e0SStephen Kelly Literal->getEndLoc()),
9951e1523dSSamuel Benzaquen *Replacement);
10051e1523dSSamuel Benzaquen }
10151e1523dSSamuel Benzaquen
1027d2ea6c4SCarlos Galvez } // namespace clang::tidy::performance
103