diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/abseil')
4 files changed, 142 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp index 178a7a610a0..e70184bfb3d 100644 --- a/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/abseil/AbseilTidyModule.cpp @@ -14,6 +14,7 @@ #include "FasterStrsplitDelimiterCheck.h" #include "NoNamespaceCheck.h" #include "StringFindStartswithCheck.h" +#include "StrCatAppendCheck.h" namespace clang { namespace tidy { @@ -29,6 +30,8 @@ public: CheckFactories.registerCheck<NoNamespaceCheck>("abseil-no-namespace"); CheckFactories.registerCheck<StringFindStartswithCheck>( "abseil-string-find-startswith"); + CheckFactories.registerCheck<StrCatAppendCheck>( + "abseil-str-cat-append"); } }; diff --git a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt index 601dceb12c2..97932013a6f 100644 --- a/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/abseil/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyAbseilModule FasterStrsplitDelimiterCheck.cpp NoNamespaceCheck.cpp StringFindStartswithCheck.cpp + StrCatAppendCheck.cpp LINK_LIBS clangAST 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 diff --git a/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.h b/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.h new file mode 100644 index 00000000000..eaa750daf32 --- /dev/null +++ b/clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.h @@ -0,0 +1,36 @@ +//===--- StrCatAppendCheck.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_STRCATAPPENDCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_STRCATAPPENDCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Flags uses of absl::StrCat to append to a string. Suggests absl::StrAppend +/// should be used instead. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-str-cat-append.html +class StrCatAppendCheck : public ClangTidyCheck { +public: + StrCatAppendCheck(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_STRCATAPPENDCHECK_H |