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