summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/abseil
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/abseil')
-rw-r--r--clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp3
-rw-r--r--clang-tools-extra/clang-tidy/abseil/CMakeLists.txt1
-rw-r--r--clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp140
-rw-r--r--clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.h39
4 files changed, 183 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp
index e70184bfb3d..e7e0a2bdc3c 100644
--- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp
@@ -13,6 +13,7 @@
#include "DurationDivisionCheck.h"
#include "FasterStrsplitDelimiterCheck.h"
#include "NoNamespaceCheck.h"
+#include "RedundantStrcatCallsCheck.h"
#include "StringFindStartswithCheck.h"
#include "StrCatAppendCheck.h"
@@ -28,6 +29,8 @@ public:
CheckFactories.registerCheck<FasterStrsplitDelimiterCheck>(
"abseil-faster-strsplit-delimiter");
CheckFactories.registerCheck<NoNamespaceCheck>("abseil-no-namespace");
+ CheckFactories.registerCheck<RedundantStrcatCallsCheck>(
+ "abseil-redundant-strcat-calls");
CheckFactories.registerCheck<StringFindStartswithCheck>(
"abseil-string-find-startswith");
CheckFactories.registerCheck<StrCatAppendCheck>(
diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
index 97932013a6f..f36c0d979ec 100644
--- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt
@@ -5,6 +5,7 @@ add_clang_library(clangTidyAbseilModule
DurationDivisionCheck.cpp
FasterStrsplitDelimiterCheck.cpp
NoNamespaceCheck.cpp
+ RedundantStrcatCallsCheck.cpp
StringFindStartswithCheck.cpp
StrCatAppendCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp
new file mode 100644
index 00000000000..19806685f89
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.cpp
@@ -0,0 +1,140 @@
+//===--- RedundantStrcatCallsCheck.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 "RedundantStrcatCallsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+// TODO: Features to add to the check:
+// - Make it work if num_args > 26.
+// - Remove empty literal string arguments.
+// - Collapse consecutive literal string arguments into one (remove the ,).
+// - Replace StrCat(a + b) -> StrCat(a, b) if a or b are strings.
+// - Make it work in macros if the outer and inner StrCats are both in the
+// argument.
+
+void RedundantStrcatCallsCheck::registerMatchers(MatchFinder* Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+ const auto CallToStrcat =
+ callExpr(callee(functionDecl(hasName("::absl::StrCat"))));
+ const auto CallToStrappend =
+ callExpr(callee(functionDecl(hasName("::absl::StrAppend"))));
+ // Do not match StrCat() calls that are descendants of other StrCat calls.
+ // Those are handled on the ancestor call.
+ const auto CallToEither = callExpr(
+ callee(functionDecl(hasAnyName("::absl::StrCat", "::absl::StrAppend"))));
+ Finder->addMatcher(
+ callExpr(CallToStrcat, unless(hasAncestor(CallToEither))).bind("StrCat"),
+ this);
+ Finder->addMatcher(CallToStrappend.bind("StrAppend"), this);
+}
+
+namespace {
+
+struct StrCatCheckResult {
+ int NumCalls = 0;
+ std::vector<FixItHint> Hints;
+};
+
+void RemoveCallLeaveArgs(const CallExpr* Call, StrCatCheckResult* CheckResult) {
+ // Remove 'Foo('
+ CheckResult->Hints.push_back(
+ FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+ Call->getBeginLoc(), Call->getArg(0)->getBeginLoc())));
+ // Remove the ')'
+ CheckResult->Hints.push_back(
+ FixItHint::CreateRemoval(CharSourceRange::getCharRange(
+ Call->getRParenLoc(), Call->getEndLoc().getLocWithOffset(1))));
+}
+
+const clang::CallExpr* ProcessArgument(const Expr* Arg,
+ const MatchFinder::MatchResult& Result,
+ StrCatCheckResult* CheckResult) {
+ const auto IsAlphanum = hasDeclaration(cxxMethodDecl(hasName("AlphaNum")));
+ static const auto* const Strcat = new auto(hasName("::absl::StrCat"));
+ const auto IsStrcat = cxxBindTemporaryExpr(
+ has(callExpr(callee(functionDecl(*Strcat))).bind("StrCat")));
+ if (const auto* SubStrcatCall = selectFirst<const CallExpr>(
+ "StrCat",
+ match(stmt(anyOf(
+ cxxConstructExpr(IsAlphanum, hasArgument(0, IsStrcat)),
+ IsStrcat)),
+ *Arg->IgnoreParenImpCasts(), *Result.Context))) {
+ RemoveCallLeaveArgs(SubStrcatCall, CheckResult);
+ return SubStrcatCall;
+ }
+ return nullptr;
+}
+
+StrCatCheckResult ProcessCall(const CallExpr* RootCall, bool IsAppend,
+ const MatchFinder::MatchResult& Result) {
+ StrCatCheckResult CheckResult;
+ std::deque<const CallExpr*> CallsToProcess = {RootCall};
+
+ while (!CallsToProcess.empty()) {
+ ++CheckResult.NumCalls;
+
+ const CallExpr* CallExpr = CallsToProcess.front();
+ CallsToProcess.pop_front();
+
+ int StartArg = CallExpr == RootCall && IsAppend;
+ for (const auto *Arg : CallExpr->arguments()) {
+ if (StartArg-- > 0)
+ continue;
+ if (const clang::CallExpr* Sub =
+ ProcessArgument(Arg, Result, &CheckResult)) {
+ CallsToProcess.push_back(Sub);
+ }
+ }
+ }
+ return CheckResult;
+}
+} // namespace
+
+void RedundantStrcatCallsCheck::check(const MatchFinder::MatchResult& Result) {
+ bool IsAppend;
+
+ const CallExpr* RootCall;
+ if ((RootCall = Result.Nodes.getNodeAs<CallExpr>("StrCat")))
+ IsAppend = false;
+ else if ((RootCall = Result.Nodes.getNodeAs<CallExpr>("StrAppend")))
+ IsAppend = true;
+ else
+ return;
+
+ if (RootCall->getBeginLoc().isMacroID()) {
+ // Ignore calls within macros.
+ // In many cases the outer StrCat part of the macro and the inner StrCat is
+ // a macro argument. Removing the inner StrCat() converts one macro
+ // argument into many.
+ return;
+ }
+
+ const StrCatCheckResult CheckResult =
+ ProcessCall(RootCall, IsAppend, Result);
+ if (CheckResult.NumCalls == 1) {
+ // Just one call, so nothing to fix.
+ return;
+ }
+
+ diag(RootCall->getBeginLoc(),
+ "multiple calls to 'absl::StrCat' can be flattened into a single call")
+ << CheckResult.Hints;
+}
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
diff --git a/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.h b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.h
new file mode 100644
index 00000000000..ede03547960
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/abseil/RedundantStrcatCallsCheck.h
@@ -0,0 +1,39 @@
+//===--- RedundantStrcatCallsCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+/// Flags redundant calls to absl::StrCat when the result is being passed to
+/// another call of absl::StrCat/absl::StrAppend. Also suggests a fix to
+/// collapse the calls.
+/// Example:
+/// StrCat(1, StrCat(2, 3)) ==> StrCat(1, 2, 3)
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-redundant-strcat-calls.html
+class RedundantStrcatCallsCheck : public ClangTidyCheck {
+public:
+ RedundantStrcatCallsCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_REDUNDANTSTRCATCALLSCHECK_H
OpenPOWER on IntegriCloud