forked from OSchip/llvm-project
[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
This commit is contained in:
parent
96fccc2d09
commit
51e1523d70
|
@ -1,6 +1,7 @@
|
|||
set(LLVM_LINK_COMPONENTS support)
|
||||
|
||||
add_clang_library(clangTidyPerformanceModule
|
||||
FasterStringFindCheck.cpp
|
||||
ForRangeCopyCheck.cpp
|
||||
ImplicitCastInLoopCheck.cpp
|
||||
PerformanceTidyModule.cpp
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -11,6 +11,7 @@
|
|||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
|
||||
#include "FasterStringFindCheck.h"
|
||||
#include "ForRangeCopyCheck.h"
|
||||
#include "ImplicitCastInLoopCheck.h"
|
||||
#include "UnnecessaryCopyInitialization.h"
|
||||
|
@ -22,6 +23,8 @@ namespace performance {
|
|||
class PerformanceModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<FasterStringFindCheck>(
|
||||
"performance-faster-string-find");
|
||||
CheckFactories.registerCheck<ForRangeCopyCheck>(
|
||||
"performance-for-range-copy");
|
||||
CheckFactories.registerCheck<ImplicitCastInLoopCheck>(
|
||||
|
|
|
@ -80,6 +80,7 @@ Clang-Tidy Checks
|
|||
modernize-use-default
|
||||
modernize-use-nullptr
|
||||
modernize-use-override
|
||||
performance-faster-string-find
|
||||
performance-for-range-copy
|
||||
performance-implicit-cast-in-loop
|
||||
readability-braces-around-statements
|
||||
|
|
|
@ -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');
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue