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