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