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