summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp102
1 files changed, 102 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp b/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp
new file mode 100644
index 00000000000..25b9d17e833
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp
@@ -0,0 +1,102 @@
+//===--- StrCatAppendCheck.cpp - clang-tidy--------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StrCatAppendCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+namespace {
+// Skips any combination of temporary materialization, temporary binding and
+// implicit casting.
+AST_MATCHER_P(Stmt, IgnoringTemporaries, ast_matchers::internal::Matcher<Stmt>,
+ InnerMatcher) {
+ const Stmt *E = &Node;
+ while (true) {
+ if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E))
+ E = MTE->getTemporary();
+ if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(E))
+ E = BTE->getSubExpr();
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E))
+ E = ICE->getSubExpr();
+ else
+ break;
+ }
+
+ return InnerMatcher.matches(*E, Finder, Builder);
+}
+
+} // namespace
+
+// TODO: str += StrCat(...)
+// str.append(StrCat(...))
+
+void StrCatAppendCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ const auto StrCat = functionDecl(hasName("::absl::StrCat"));
+ // The arguments of absl::StrCat are implicitly converted to AlphaNum. This
+ // matches to the arguments because of that behavior.
+ const auto AlphaNum = IgnoringTemporaries(cxxConstructExpr(
+ argumentCountIs(1), hasType(cxxRecordDecl(hasName("::absl::AlphaNum"))),
+ hasArgument(0, ignoringImpCasts(declRefExpr(to(equalsBoundNode("LHS")),
+ expr().bind("Arg0"))))));
+
+ const auto HasAnotherReferenceToLhs =
+ callExpr(hasAnyArgument(expr(hasDescendant(declRefExpr(
+ to(equalsBoundNode("LHS")), unless(equalsBoundNode("Arg0")))))));
+
+ // Now look for calls to operator= with an object on the LHS and a call to
+ // StrCat on the RHS. The first argument of the StrCat call should be the same
+ // as the LHS. Ignore calls from template instantiations.
+ Finder->addMatcher(
+ cxxOperatorCallExpr(
+ unless(isInTemplateInstantiation()), hasOverloadedOperatorName("="),
+ hasArgument(0, declRefExpr(to(decl().bind("LHS")))),
+ hasArgument(1, IgnoringTemporaries(
+ callExpr(callee(StrCat), hasArgument(0, AlphaNum),
+ unless(HasAnotherReferenceToLhs))
+ .bind("Call"))))
+ .bind("Op"),
+ this);
+}
+
+void StrCatAppendCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Op = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("Op");
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("Call");
+ assert(Op != nullptr && Call != nullptr && "Matcher does not work as expected");
+
+ // Handles the case 'x = absl::StrCat(x)', which has no effect.
+ if (Call->getNumArgs() == 1) {
+ diag(Op->getBeginLoc(), "call to 'absl::StrCat' has no effect");
+ return;
+ }
+
+ // Emit a warning and emit fixits to go from
+ // x = absl::StrCat(x, ...)
+ // to
+ // absl::StrAppend(&x, ...)
+ diag(Op->getBeginLoc(),
+ "call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a "
+ "string to avoid a performance penalty")
+ << FixItHint::CreateReplacement(
+ CharSourceRange::getTokenRange(Op->getBeginLoc(),
+ Call->getCallee()->getEndLoc()),
+ "StrAppend")
+ << FixItHint::CreateInsertion(Call->getArg(0)->getBeginLoc(), "&");
+}
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
OpenPOWER on IntegriCloud