1dcbf57d1SAlexander Kornienko //===--- InefficientStringConcatenationCheck.cpp - clang-tidy--------------===//
2dcbf57d1SAlexander Kornienko //
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
6dcbf57d1SAlexander Kornienko //
7dcbf57d1SAlexander Kornienko //===----------------------------------------------------------------------===//
8dcbf57d1SAlexander Kornienko
9dcbf57d1SAlexander Kornienko #include "InefficientStringConcatenationCheck.h"
10dcbf57d1SAlexander Kornienko #include "clang/AST/ASTContext.h"
11dcbf57d1SAlexander Kornienko #include "clang/ASTMatchers/ASTMatchFinder.h"
12dcbf57d1SAlexander Kornienko
13dcbf57d1SAlexander Kornienko using namespace clang::ast_matchers;
14dcbf57d1SAlexander Kornienko
15*7d2ea6c4SCarlos Galvez namespace clang::tidy::performance {
16dcbf57d1SAlexander Kornienko
storeOptions(ClangTidyOptions::OptionMap & Opts)17dcbf57d1SAlexander Kornienko void InefficientStringConcatenationCheck::storeOptions(
18dcbf57d1SAlexander Kornienko ClangTidyOptions::OptionMap &Opts) {
19dcbf57d1SAlexander Kornienko Options.store(Opts, "StrictMode", StrictMode);
20dcbf57d1SAlexander Kornienko }
21dcbf57d1SAlexander Kornienko
InefficientStringConcatenationCheck(StringRef Name,ClangTidyContext * Context)22dcbf57d1SAlexander Kornienko InefficientStringConcatenationCheck::InefficientStringConcatenationCheck(
23dcbf57d1SAlexander Kornienko StringRef Name, ClangTidyContext *Context)
240055d97dSAlexander Kornienko : ClangTidyCheck(Name, Context),
25672207c3SNathan James StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {}
26dcbf57d1SAlexander Kornienko
registerMatchers(MatchFinder * Finder)27dcbf57d1SAlexander Kornienko void InefficientStringConcatenationCheck::registerMatchers(
28dcbf57d1SAlexander Kornienko MatchFinder *Finder) {
29dcbf57d1SAlexander Kornienko const auto BasicStringType =
307b9c117bSManuel Klimek hasType(qualType(hasUnqualifiedDesugaredType(recordType(
317b9c117bSManuel Klimek hasDeclaration(cxxRecordDecl(hasName("::std::basic_string")))))));
32dcbf57d1SAlexander Kornienko
33dcbf57d1SAlexander Kornienko const auto BasicStringPlusOperator = cxxOperatorCallExpr(
34dcbf57d1SAlexander Kornienko hasOverloadedOperatorName("+"),
35dcbf57d1SAlexander Kornienko hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))));
36dcbf57d1SAlexander Kornienko
37dcbf57d1SAlexander Kornienko const auto PlusOperator =
38dcbf57d1SAlexander Kornienko cxxOperatorCallExpr(
39dcbf57d1SAlexander Kornienko hasOverloadedOperatorName("+"),
40dcbf57d1SAlexander Kornienko hasAnyArgument(ignoringImpCasts(declRefExpr(BasicStringType))),
41dcbf57d1SAlexander Kornienko hasDescendant(BasicStringPlusOperator))
42dcbf57d1SAlexander Kornienko .bind("plusOperator");
43dcbf57d1SAlexander Kornienko
44dcbf57d1SAlexander Kornienko const auto AssignOperator = cxxOperatorCallExpr(
45dcbf57d1SAlexander Kornienko hasOverloadedOperatorName("="),
46dcbf57d1SAlexander Kornienko hasArgument(0, declRefExpr(BasicStringType,
47dcbf57d1SAlexander Kornienko hasDeclaration(decl().bind("lhsStrT")))
48dcbf57d1SAlexander Kornienko .bind("lhsStr")),
49dcbf57d1SAlexander Kornienko hasArgument(1, stmt(hasDescendant(declRefExpr(
50dcbf57d1SAlexander Kornienko hasDeclaration(decl(equalsBoundNode("lhsStrT"))))))),
51dcbf57d1SAlexander Kornienko hasDescendant(BasicStringPlusOperator));
52dcbf57d1SAlexander Kornienko
53dcbf57d1SAlexander Kornienko if (StrictMode) {
54dcbf57d1SAlexander Kornienko Finder->addMatcher(cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator)),
55dcbf57d1SAlexander Kornienko this);
56dcbf57d1SAlexander Kornienko } else {
57dcbf57d1SAlexander Kornienko Finder->addMatcher(
58dcbf57d1SAlexander Kornienko cxxOperatorCallExpr(anyOf(AssignOperator, PlusOperator),
59dcbf57d1SAlexander Kornienko hasAncestor(stmt(anyOf(cxxForRangeStmt(),
60dcbf57d1SAlexander Kornienko whileStmt(), forStmt())))),
61dcbf57d1SAlexander Kornienko this);
62dcbf57d1SAlexander Kornienko }
63dcbf57d1SAlexander Kornienko }
64dcbf57d1SAlexander Kornienko
check(const MatchFinder::MatchResult & Result)65dcbf57d1SAlexander Kornienko void InefficientStringConcatenationCheck::check(
66dcbf57d1SAlexander Kornienko const MatchFinder::MatchResult &Result) {
67dcbf57d1SAlexander Kornienko const auto *LhsStr = Result.Nodes.getNodeAs<DeclRefExpr>("lhsStr");
68dcbf57d1SAlexander Kornienko const auto *PlusOperator =
69dcbf57d1SAlexander Kornienko Result.Nodes.getNodeAs<CXXOperatorCallExpr>("plusOperator");
70ab2d3ce4SAlexander Kornienko const char *DiagMsg =
71dcbf57d1SAlexander Kornienko "string concatenation results in allocation of unnecessary temporary "
72dcbf57d1SAlexander Kornienko "strings; consider using 'operator+=' or 'string::append()' instead";
73dcbf57d1SAlexander Kornienko
74dcbf57d1SAlexander Kornienko if (LhsStr)
75dcbf57d1SAlexander Kornienko diag(LhsStr->getExprLoc(), DiagMsg);
76dcbf57d1SAlexander Kornienko else if (PlusOperator)
77dcbf57d1SAlexander Kornienko diag(PlusOperator->getExprLoc(), DiagMsg);
78dcbf57d1SAlexander Kornienko }
79dcbf57d1SAlexander Kornienko
80*7d2ea6c4SCarlos Galvez } // namespace clang::tidy::performance
81