-
Notifications
You must be signed in to change notification settings - Fork 12.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clang-tidy] Add check performance-faster-string-find
Summary: Add check performance-faster-string-find. It replaces single character string literals to character literals in calls to string::find and friends. Reviewers: alexfh Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D16152 llvm-svn: 260712
- llvmorg-20-init
- llvmorg-19.1.7
- llvmorg-19.1.6
- llvmorg-19.1.5
- llvmorg-19.1.4
- llvmorg-19.1.3
- llvmorg-19.1.2
- llvmorg-19.1.1
- llvmorg-19.1.0
- llvmorg-19.1.0-rc4
- llvmorg-19.1.0-rc3
- llvmorg-19.1.0-rc2
- llvmorg-19.1.0-rc1
- llvmorg-19-init
- llvmorg-18.1.8
- llvmorg-18.1.7
- llvmorg-18.1.6
- llvmorg-18.1.5
- llvmorg-18.1.4
- llvmorg-18.1.3
- llvmorg-18.1.2
- llvmorg-18.1.1
- llvmorg-18.1.0
- llvmorg-18.1.0-rc4
- llvmorg-18.1.0-rc3
- llvmorg-18.1.0-rc2
- llvmorg-18.1.0-rc1
- llvmorg-18-init
- llvmorg-17.0.6
- llvmorg-17.0.5
- llvmorg-17.0.4
- llvmorg-17.0.3
- llvmorg-17.0.2
- llvmorg-17.0.1
- llvmorg-17.0.0
- llvmorg-17.0.0-rc4
- llvmorg-17.0.0-rc3
- llvmorg-17.0.0-rc2
- llvmorg-17.0.0-rc1
- llvmorg-17-init
- llvmorg-16.0.6
- llvmorg-16.0.5
- llvmorg-16.0.4
- llvmorg-16.0.3
- llvmorg-16.0.2
- llvmorg-16.0.1
- llvmorg-16.0.0
- llvmorg-16.0.0-rc4
- llvmorg-16.0.0-rc3
- llvmorg-16.0.0-rc2
- llvmorg-16.0.0-rc1
- llvmorg-16-init
- llvmorg-15.0.7
- llvmorg-15.0.6
- llvmorg-15.0.5
- llvmorg-15.0.4
- llvmorg-15.0.3
- llvmorg-15.0.2
- llvmorg-15.0.1
- llvmorg-15.0.0
- llvmorg-15.0.0-rc3
- llvmorg-15.0.0-rc2
- llvmorg-15.0.0-rc1
- llvmorg-15-init
- llvmorg-14.0.6
- llvmorg-14.0.5
- llvmorg-14.0.4
- llvmorg-14.0.3
- llvmorg-14.0.2
- llvmorg-14.0.1
- llvmorg-14.0.0
- llvmorg-14.0.0-rc4
- llvmorg-14.0.0-rc3
- llvmorg-14.0.0-rc2
- llvmorg-14.0.0-rc1
- llvmorg-14-init
- llvmorg-13.0.1
- llvmorg-13.0.1-rc3
- llvmorg-13.0.1-rc2
- llvmorg-13.0.1-rc1
- llvmorg-13.0.0
- llvmorg-13.0.0-rc4
- llvmorg-13.0.0-rc3
- llvmorg-13.0.0-rc2
- llvmorg-13.0.0-rc1
- llvmorg-13-init
- llvmorg-12.0.1
- llvmorg-12.0.1-rc4
- llvmorg-12.0.1-rc3
- llvmorg-12.0.1-rc2
- llvmorg-12.0.1-rc1
- llvmorg-12.0.0
- llvmorg-12.0.0-rc5
- llvmorg-12.0.0-rc4
- llvmorg-12.0.0-rc3
- llvmorg-12.0.0-rc2
- llvmorg-12.0.0-rc1
- llvmorg-12-init
- llvmorg-11.1.0
- llvmorg-11.1.0-rc3
- llvmorg-11.1.0-rc2
- llvmorg-11.1.0-rc1
- llvmorg-11.0.1
- llvmorg-11.0.1-rc2
- llvmorg-11.0.1-rc1
- llvmorg-11.0.0
- llvmorg-11.0.0-rc6
- llvmorg-11.0.0-rc5
- llvmorg-11.0.0-rc4
- llvmorg-11.0.0-rc3
- llvmorg-11.0.0-rc2
- llvmorg-11.0.0-rc1
- llvmorg-11-init
- llvmorg-10.0.1
- llvmorg-10.0.1-rc4
- llvmorg-10.0.1-rc3
- llvmorg-10.0.1-rc2
- llvmorg-10.0.1-rc1
- llvmorg-10.0.0
- llvmorg-10.0.0-rc6
- llvmorg-10.0.0-rc5
- llvmorg-10.0.0-rc4
- llvmorg-10.0.0-rc3
- llvmorg-10.0.0-rc2
- llvmorg-10.0.0-rc1
- llvmorg-10-init
- llvmorg-9.0.1
- llvmorg-9.0.1-rc3
- llvmorg-9.0.1-rc2
- llvmorg-9.0.1-rc1
- llvmorg-9.0.0
- llvmorg-9.0.0-rc6
- llvmorg-9.0.0-rc5
- llvmorg-9.0.0-rc4
- llvmorg-9.0.0-rc3
- llvmorg-9.0.0-rc2
- llvmorg-9.0.0-rc1
- llvmorg-8.0.1
- llvmorg-8.0.1-rc4
- llvmorg-8.0.1-rc3
- llvmorg-8.0.1-rc2
- llvmorg-8.0.1-rc1
- llvmorg-8.0.0
- llvmorg-8.0.0-rc5
- llvmorg-8.0.0-rc4
- llvmorg-8.0.0-rc3
- llvmorg-8.0.0-rc2
- llvmorg-8.0.0-rc1
- llvmorg-7.1.0
- llvmorg-7.1.0-rc1
- llvmorg-7.0.1
- llvmorg-7.0.1-rc3
- llvmorg-7.0.1-rc2
- llvmorg-7.0.1-rc1
- llvmorg-7.0.0
- llvmorg-7.0.0-rc3
- llvmorg-7.0.0-rc2
- llvmorg-7.0.0-rc1
- llvmorg-6.0.1
- llvmorg-6.0.1-rc3
- llvmorg-6.0.1-rc2
- llvmorg-6.0.1-rc1
- llvmorg-6.0.0
- llvmorg-6.0.0-rc3
- llvmorg-6.0.0-rc2
- llvmorg-6.0.0-rc1
- llvmorg-5.0.2
- llvmorg-5.0.2-rc2
- llvmorg-5.0.2-rc1
- llvmorg-5.0.1
- llvmorg-5.0.1-rc3
- llvmorg-5.0.1-rc2
- llvmorg-5.0.1-rc1
- llvmorg-5.0.0
- llvmorg-5.0.0-rc5
- llvmorg-5.0.0-rc4
- llvmorg-5.0.0-rc3
- llvmorg-5.0.0-rc2
- llvmorg-5.0.0-rc1
- llvmorg-4.0.1
- llvmorg-4.0.1-rc3
- llvmorg-4.0.1-rc2
- llvmorg-4.0.1-rc1
- llvmorg-4.0.0
- llvmorg-4.0.0-rc4
- llvmorg-4.0.0-rc3
- llvmorg-4.0.0-rc2
- llvmorg-4.0.0-rc1
- llvmorg-3.9.1
- llvmorg-3.9.1-rc3
- llvmorg-3.9.1-rc2
- llvmorg-3.9.1-rc1
- llvmorg-3.9.0
- llvmorg-3.9.0-rc3
- llvmorg-3.9.0-rc2
- llvmorg-3.9.0-rc1
1 parent
96fccc2
commit 51e1523
Showing
7 changed files
with
308 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
128 changes: 128 additions & 0 deletions
128
clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.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,128 @@ | ||
//===--- FasterStringFindCheck.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 "FasterStringFindCheck.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/ASTMatchers/ASTMatchFinder.h" | ||
#include "llvm/ADT/Optional.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang { | ||
namespace tidy { | ||
namespace performance { | ||
|
||
namespace { | ||
|
||
static const char StringLikeClassesDelimiter[] = ";"; | ||
|
||
std::vector<std::string> ParseClasses(StringRef Option) { | ||
SmallVector<StringRef, 4> Classes; | ||
Option.split(Classes, StringLikeClassesDelimiter); | ||
std::vector<std::string> Result; | ||
for (StringRef &Class : Classes) { | ||
Class = Class.trim(); | ||
if (!Class.empty()) | ||
Result.push_back(Class); | ||
} | ||
return Result; | ||
} | ||
|
||
llvm::Optional<std::string> MakeCharacterLiteral(const StringLiteral *Literal) { | ||
std::string Result; | ||
{ | ||
llvm::raw_string_ostream OS(Result); | ||
Literal->outputString(OS); | ||
} | ||
// Now replace the " with '. | ||
auto pos = Result.find_first_of('"'); | ||
if (pos == Result.npos) return llvm::None; | ||
Result[pos] = '\''; | ||
pos = Result.find_last_of('"'); | ||
if (pos == Result.npos) return llvm::None; | ||
Result[pos] = '\''; | ||
return Result; | ||
} | ||
|
||
AST_MATCHER(StringLiteral, lengthIsOne) { return Node.getLength() == 1; } | ||
|
||
AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>, | ||
hasSubstitutedType) { | ||
return hasType(qualType(anyOf(substTemplateTypeParmType(), | ||
hasDescendant(substTemplateTypeParmType())))); | ||
} | ||
|
||
} // namespace | ||
|
||
FasterStringFindCheck::FasterStringFindCheck(StringRef Name, | ||
ClangTidyContext *Context) | ||
: ClangTidyCheck(Name, Context), | ||
StringLikeClasses( | ||
ParseClasses(Options.get("StringLikeClasses", "std::basic_string"))) { | ||
} | ||
|
||
void FasterStringFindCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | ||
Options.store(Opts, "StringLikeClasses", | ||
llvm::join(StringLikeClasses.begin(), StringLikeClasses.end(), | ||
StringLikeClassesDelimiter)); | ||
} | ||
|
||
void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) { | ||
if (!getLangOpts().CPlusPlus) | ||
return; | ||
|
||
const auto SingleChar = | ||
expr(ignoringParenCasts(stringLiteral(lengthIsOne()).bind("literal"))); | ||
|
||
const auto StringFindFunctions = | ||
anyOf(hasName("find"), hasName("rfind"), hasName("find_first_of"), | ||
hasName("find_first_not_of"), hasName("find_last_of"), | ||
hasName("find_last_not_of")); | ||
|
||
llvm::Optional<ast_matchers::internal::Matcher<NamedDecl>> IsStringClass; | ||
|
||
for (const auto &ClassName : StringLikeClasses) { | ||
const auto HasName = hasName(ClassName); | ||
IsStringClass = IsStringClass ? anyOf(*IsStringClass, HasName) : HasName; | ||
} | ||
|
||
if (IsStringClass) { | ||
Finder->addMatcher( | ||
cxxMemberCallExpr( | ||
callee(functionDecl(StringFindFunctions).bind("func")), | ||
anyOf(argumentCountIs(1), argumentCountIs(2)), | ||
hasArgument(0, SingleChar), | ||
on(expr(hasType(recordDecl(*IsStringClass)), | ||
unless(hasSubstitutedType())))), | ||
this); | ||
} | ||
} | ||
|
||
void FasterStringFindCheck::check(const MatchFinder::MatchResult &Result) { | ||
const auto *Literal = Result.Nodes.getNodeAs<StringLiteral>("literal"); | ||
const auto *FindFunc = Result.Nodes.getNodeAs<FunctionDecl>("func"); | ||
|
||
auto Replacement = MakeCharacterLiteral(Literal); | ||
if (!Replacement) | ||
return; | ||
|
||
diag(Literal->getLocStart(), "%0 called with a string literal consisting of " | ||
"a single character; consider using the more " | ||
"effective overload accepting a character") | ||
<< FindFunc->getName() | ||
<< FixItHint::CreateReplacement( | ||
CharSourceRange::getTokenRange(Literal->getLocStart(), | ||
Literal->getLocEnd()), | ||
*Replacement); | ||
} | ||
|
||
} // namespace performance | ||
} // namespace tidy | ||
} // namespace clang |
43 changes: 43 additions & 0 deletions
43
clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
//===--- FasterStringFindCheck.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_PERFORMANCE_FASTER_STRING_FIND_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_H | ||
|
||
#include "../ClangTidy.h" | ||
|
||
#include <string> | ||
#include <vector> | ||
|
||
namespace clang { | ||
namespace tidy { | ||
namespace performance { | ||
|
||
/// Optimize calls to std::string::find() and friends when the needle passed is | ||
/// a single character string literal. | ||
/// The character literal overload is more efficient. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/performance-faster-string-find.html | ||
class FasterStringFindCheck : public ClangTidyCheck { | ||
public: | ||
FasterStringFindCheck(StringRef Name, ClangTidyContext *Context); | ||
void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
void storeOptions(ClangTidyOptions::OptionMap &Opts) override; | ||
|
||
private: | ||
const std::vector<std::string> StringLikeClasses; | ||
}; | ||
|
||
} // namespace performance | ||
} // namespace tidy | ||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_FASTER_STRING_FIND_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
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
22 changes: 22 additions & 0 deletions
22
clang-tools-extra/docs/clang-tidy/checks/performance-faster-string-find.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,22 @@ | ||
.. title:: clang-tidy - performance-faster-string-find | ||
|
||
performance-faster-string-find | ||
============================== | ||
|
||
Optimize calls to std::string::find() and friends when the needle passed is | ||
a single character string literal. | ||
The character literal overload is more efficient. | ||
|
||
By default only `std::basic_string` is considered. This list can be modified by | ||
passing a `;` separated list of class names using the `StringLikeClasses` | ||
option. The methods to consired are fixed, though. | ||
|
||
Examples: | ||
|
||
.. code-block:: c++ | ||
|
||
str.find("A"); | ||
|
||
// becomes | ||
|
||
str.find('A'); |
110 changes: 110 additions & 0 deletions
110
clang-tools-extra/test/clang-tidy/performance-faster-string-find.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,110 @@ | ||
// RUN: %check_clang_tidy %s performance-faster-string-find %t -- \ | ||
// RUN: -config="{CheckOptions: \ | ||
// RUN: [{key: performance-faster-string-find.StringLikeClasses, \ | ||
// RUN: value: 'std::basic_string; ::llvm::StringRef;'}]}" -- | ||
|
||
namespace std { | ||
template <typename Char> | ||
struct basic_string { | ||
int find(const Char *, int = 0) const; | ||
int find(const Char *, int, int) const; | ||
int rfind(const Char *) const; | ||
int find_first_of(const Char *) const; | ||
int find_first_not_of(const Char *) const; | ||
int find_last_of(const Char *) const; | ||
int find_last_not_of(const Char *) const; | ||
}; | ||
|
||
typedef basic_string<char> string; | ||
typedef basic_string<wchar_t> wstring; | ||
} // namespace std | ||
|
||
namespace llvm { | ||
struct StringRef { | ||
int find(const char *) const; | ||
}; | ||
} // namespace llvm | ||
|
||
struct NotStringRef { | ||
int find(const char *); | ||
}; | ||
|
||
void StringFind() { | ||
std::string Str; | ||
|
||
Str.find("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:12: warning: find called with a string literal consisting of a single character; consider using the more effective overload accepting a character [performance-faster-string-find] | ||
// CHECK-FIXES: Str.find('a'); | ||
|
||
// Works with the pos argument. | ||
Str.find("a", 1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:12: warning: find called with a string literal | ||
// CHECK-FIXES: Str.find('a', 1); | ||
|
||
// Doens't work with strings smaller or larger than 1 char. | ||
Str.find(""); | ||
Str.find("ab"); | ||
|
||
// Doesn't do anything with the 3 argument overload. | ||
Str.find("a", 1, 1); | ||
|
||
// Other methods that can also be replaced | ||
Str.rfind("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: rfind called with a string literal | ||
// CHECK-FIXES: Str.rfind('a'); | ||
Str.find_first_of("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: find_first_of called with a string | ||
// CHECK-FIXES: Str.find_first_of('a'); | ||
Str.find_first_not_of("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:25: warning: find_first_not_of called with a | ||
// CHECK-FIXES: Str.find_first_not_of('a'); | ||
Str.find_last_of("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:20: warning: find_last_of called with a string | ||
// CHECK-FIXES: Str.find_last_of('a'); | ||
Str.find_last_not_of("a"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:24: warning: find_last_not_of called with a | ||
// CHECK-FIXES: Str.find_last_not_of('a'); | ||
|
||
// std::wstring should work. | ||
std::wstring WStr; | ||
WStr.find(L"n"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: find called with a string literal | ||
// CHECK-FIXES: Str.find(L'n'); | ||
// Even with unicode that fits in one wide char. | ||
WStr.find(L"\x3A9"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:13: warning: find called with a string literal | ||
// CHECK-FIXES: Str.find(L'\x3A9'); | ||
|
||
// Also with other types, but only if it was specified in the options. | ||
llvm::StringRef sr; | ||
sr.find("x"); | ||
// CHECK-MESSAGES: [[@LINE-1]]:11: warning: find called with a string literal | ||
// CHECK-FIXES: sr.find('x'); | ||
NotStringRef nsr; | ||
nsr.find("x"); | ||
} | ||
|
||
|
||
template <typename T> | ||
int FindTemplateDependant(T value) { | ||
return value.find("A"); | ||
} | ||
template <typename T> | ||
int FindTemplateNotDependant(T pos) { | ||
return std::string().find("A", pos); | ||
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: find called with a string literal | ||
// CHECK-FIXES: return std::string().find('A', pos); | ||
} | ||
|
||
int FindStr() { | ||
return FindTemplateDependant(std::string()) + FindTemplateNotDependant(1); | ||
} | ||
|
||
#define STR_MACRO(str) str.find("A") | ||
#define POS_MACRO(pos) std::string().find("A",pos) | ||
|
||
int Macros() { | ||
return STR_MACRO(std::string()) + POS_MACRO(1); | ||
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: find called with a string literal | ||
// CHECK-MESSAGES: [[@LINE-2]]:37: warning: find called with a string literal | ||
} |