-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce the abseil-str-cat-append check.
This flags uses of absl::StrCat when absl::StrAppend should be used instead. Patch by Hugo Gonzalez and Benjamin Kramer. llvm-svn: 340915
- Loading branch information
1 parent
8b4ffe6
commit a22d24a
Showing
8 changed files
with
295 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
102 changes: 102 additions & 0 deletions
102
clang-tools-extra/clang-tidy/abseil/StrCatAppendCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
clang-tools-extra/docs/clang-tidy/checks/abseil-str-cat-append.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| .. title:: clang-tidy - abseil-str-cat-append | ||
|
|
||
| abseil-str-cat-append | ||
| ===================== | ||
|
|
||
| Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests | ||
| ``absl::StrAppend()`` should be used instead. | ||
|
|
||
| The extra calls cause unnecessary temporary strings to be constructed. Removing | ||
| them makes the code smaller and faster. | ||
|
|
||
| .. code-block:: c++ | ||
|
|
||
| a = absl::StrCat(a, b); // Use absl::StrAppend(&a, b) instead. | ||
|
|
||
| Does not diagnose cases where ``abls::StrCat()`` is used as a template | ||
| argument for a functor. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
clang-tools-extra/test/clang-tidy/abseil-str-cat-append.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| // RUN: %check_clang_tidy %s abseil-str-cat-append %t -- -- -I%S -std=c++11 | ||
|
|
||
| typedef unsigned __INT16_TYPE__ char16; | ||
| typedef unsigned __INT32_TYPE__ char32; | ||
| typedef __SIZE_TYPE__ size; | ||
|
|
||
| namespace std { | ||
| template <typename T> | ||
| class allocator {}; | ||
| template <typename T> | ||
| class char_traits {}; | ||
| template <typename C, typename T, typename A> | ||
| struct basic_string { | ||
| typedef basic_string<C, T, A> _Type; | ||
| basic_string(); | ||
| basic_string(const C* p, const A& a = A()); | ||
|
|
||
| const C* c_str() const; | ||
| const C* data() const; | ||
|
|
||
| _Type& append(const C* s); | ||
| _Type& append(const C* s, size n); | ||
| _Type& assign(const C* s); | ||
| _Type& assign(const C* s, size n); | ||
|
|
||
| int compare(const _Type&) const; | ||
| int compare(const C* s) const; | ||
| int compare(size pos, size len, const _Type&) const; | ||
| int compare(size pos, size len, const C* s) const; | ||
|
|
||
| size find(const _Type& str, size pos = 0) const; | ||
| size find(const C* s, size pos = 0) const; | ||
| size find(const C* s, size pos, size n) const; | ||
|
|
||
| _Type& insert(size pos, const _Type& str); | ||
| _Type& insert(size pos, const C* s); | ||
| _Type& insert(size pos, const C* s, size n); | ||
|
|
||
| _Type& operator+=(const _Type& str); | ||
| _Type& operator+=(const C* s); | ||
| _Type& operator=(const _Type& str); | ||
| _Type& operator=(const C* s); | ||
| }; | ||
|
|
||
| typedef basic_string<char, std::char_traits<char>, std::allocator<char>> string; | ||
| typedef basic_string<wchar_t, std::char_traits<wchar_t>, | ||
| std::allocator<wchar_t>> | ||
| wstring; | ||
| typedef basic_string<char16, std::char_traits<char16>, std::allocator<char16>> | ||
| u16string; | ||
| typedef basic_string<char32, std::char_traits<char32>, std::allocator<char32>> | ||
| u32string; | ||
| } // namespace std | ||
|
|
||
| std::string operator+(const std::string&, const std::string&); | ||
| std::string operator+(const std::string&, const char*); | ||
| std::string operator+(const char*, const std::string&); | ||
|
|
||
| bool operator==(const std::string&, const std::string&); | ||
| bool operator==(const std::string&, const char*); | ||
| bool operator==(const char*, const std::string&); | ||
|
|
||
| namespace llvm { | ||
| struct StringRef { | ||
| StringRef(const char* p); | ||
| StringRef(const std::string&); | ||
| }; | ||
| } // namespace llvm | ||
|
|
||
| namespace absl { | ||
|
|
||
| struct AlphaNum { | ||
| AlphaNum(int i); | ||
| AlphaNum(double f); | ||
| AlphaNum(const char* c_str); | ||
| AlphaNum(const std::string& str); | ||
|
|
||
| private: | ||
| AlphaNum(const AlphaNum&); | ||
| AlphaNum& operator=(const AlphaNum&); | ||
| }; | ||
|
|
||
| std::string StrCat(const AlphaNum& A); | ||
| std::string StrCat(const AlphaNum& A, const AlphaNum& B); | ||
|
|
||
| template <typename A> | ||
| void Foo(A& a) { | ||
| a = StrCat(a); | ||
| } | ||
|
|
||
| void Bar() { | ||
| std::string A, B; | ||
| Foo<std::string>(A); | ||
|
|
||
| std::string C = StrCat(A); | ||
| A = StrCat(A); | ||
| // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call to 'absl::StrCat' has no effect | ||
| A = StrCat(A, B); | ||
| // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty | ||
| // CHECK-FIXES: {{^}} StrAppend(&A, B); | ||
| B = StrCat(A, B); | ||
|
|
||
| #define M(X) X = StrCat(X, A) | ||
| M(B); | ||
| // CHECK-MESSAGES: [[@LINE-1]]:5: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty | ||
| // CHECK-FIXES: #define M(X) X = StrCat(X, A) | ||
| } | ||
|
|
||
| void Regression_SelfAppend() { | ||
| std::string A; | ||
| A = StrCat(A, A); | ||
| } | ||
|
|
||
| } // namespace absl | ||
|
|
||
| void OutsideAbsl() { | ||
| std::string A, B; | ||
| A = absl::StrCat(A, B); | ||
| // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty | ||
| // CHECK-FIXES: {{^}} StrAppend(&A, B); | ||
| } | ||
|
|
||
| void OutisdeUsingAbsl() { | ||
| std::string A, B; | ||
| using absl::StrCat; | ||
| A = StrCat(A, B); | ||
| // CHECK-MESSAGES: [[@LINE-1]]:3: warning: call 'absl::StrAppend' instead of 'absl::StrCat' when appending to a string to avoid a performance penalty | ||
| // CHECK-FIXES: {{^}} StrAppend(&A, B); | ||
| } |