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