Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/performance/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ add_clang_library(clangTidyPerformanceModule STATIC
NoexceptMoveConstructorCheck.cpp
NoexceptSwapCheck.cpp
PerformanceTidyModule.cpp
ReplaceWithStringViewCheck.cpp
TriviallyDestructibleCheck.cpp
TypePromotionInMathFnCheck.cpp
UnnecessaryCopyInitializationCheck.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "NoexceptDestructorCheck.h"
#include "NoexceptMoveConstructorCheck.h"
#include "NoexceptSwapCheck.h"
#include "ReplaceWithStringViewCheck.h"
#include "TriviallyDestructibleCheck.h"
#include "TypePromotionInMathFnCheck.h"
#include "UnnecessaryCopyInitializationCheck.h"
Expand Down Expand Up @@ -62,6 +63,8 @@ class PerformanceModule : public ClangTidyModule {
"performance-noexcept-move-constructor");
CheckFactories.registerCheck<NoexceptSwapCheck>(
"performance-noexcept-swap");
CheckFactories.registerCheck<ReplaceWithStringViewCheck>(
"performance-replace-with-string-view");
CheckFactories.registerCheck<TriviallyDestructibleCheck>(
"performance-trivially-destructible");
CheckFactories.registerCheck<TypePromotionInMathFnCheck>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "ReplaceWithStringViewCheck.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang::tidy::performance {

static llvm::StringLiteral toStringViewTypeStr(StringRef Type) {
if (Type.contains("wchar_t"))
return "std::wstring_view";
if (Type.contains("wchar_t"))
return "std::wstring_view";
if (Type.contains("char8_t"))
return "std::u8string_view";
if (Type.contains("char16_t"))
return "std::u16string_view";
if (Type.contains("char32_t"))
return "std::u32string_view";
return "std::string_view";
}

void ReplaceWithStringViewCheck::registerMatchers(MatchFinder *Finder) {
const auto IsStdString = hasCanonicalType(
hasDeclaration(cxxRecordDecl(hasName("::std::basic_string"))));
const auto IsStdStringView = expr(hasType(hasCanonicalType(
hasDeclaration(cxxRecordDecl(hasName("::std::basic_string_view"))))));

Finder->addMatcher(
functionDecl(
isDefinition(),
unless(cxxMethodDecl(anyOf(isOverride(), isVirtual()))),
returns(IsStdString), hasDescendant(returnStmt()),
unless(hasDescendant(
returnStmt(hasReturnValue(unless(ignoringImplicit(anyOf(
stringLiteral(), IsStdStringView,
cxxConstructExpr(
hasType(IsStdString),
anyOf(argumentCountIs(0),
hasArgument(0, ignoringParenImpCasts(anyOf(
stringLiteral(),
IsStdStringView)))))))))))))
.bind("func"),
this);
}

void ReplaceWithStringViewCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("func")) {
const llvm::StringLiteral DestReturnTypeStr =
toStringViewTypeStr(MatchedDecl->getReturnType()
.getDesugaredType(*Result.Context)
.getAsString());

auto Diag = diag(MatchedDecl->getReturnTypeSourceRange().getBegin(),
"consider using `%0' to avoid unnecessary "
"copying and allocations")
<< DestReturnTypeStr << MatchedDecl;

for (const auto *FuncDecl = MatchedDecl; FuncDecl != nullptr;
FuncDecl = FuncDecl->getPreviousDecl()) {
Diag << FixItHint::CreateReplacement(FuncDecl->getReturnTypeSourceRange(),
DestReturnTypeStr);
}
}
}

} // namespace clang::tidy::performance
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_REPLACEWITHSTRINGVIEWCHECK_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_REPLACEWITHSTRINGVIEWCHECK_H

#include "../ClangTidyCheck.h"

namespace clang::tidy::performance {

/// Looks for functions returning `std::[w|u8|u16|u32]string` and suggests to
/// change it to `std::[...]string_view` if possible and profitable.
///
/// Example:
///
/// std::string foo(int i) { // <---- can be replaced to std::string_view f(...)
/// switch(i) {
/// case 1:
/// return "case1";
/// case 2:
/// return "case2";
/// default:
/// return {};
/// }
/// }
///
/// For the user-facing documentation see:
/// https://clang.llvm.org/extra/clang-tidy/checks/performance/replace-with-string-view.html
class ReplaceWithStringViewCheck : public ClangTidyCheck {
public:
ReplaceWithStringViewCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus17;
}
};

} // namespace clang::tidy::performance

#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_REPLACEWITHSTRINGVIEWCHECK_H
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ New checks
Finds virtual function overrides with different visibility than the function
in the base class.

- New :doc:`performance-replace-with-string-view
<clang-tidy/checks/performance/replace-with-string-view>` check.

Looks for functions returning ``std::[w|u8|u16|u32]string`` and suggests to
change it to ``std::[...]string_view`` for performance reasons if possible.

- New :doc:`readability-redundant-parentheses
<clang-tidy/checks/readability/redundant-parentheses>` check.

Expand Down
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ Clang-Tidy Checks
:doc:`performance-noexcept-destructor <performance/noexcept-destructor>`, "Yes"
:doc:`performance-noexcept-move-constructor <performance/noexcept-move-constructor>`, "Yes"
:doc:`performance-noexcept-swap <performance/noexcept-swap>`, "Yes"
:doc:`performance-replace-with-string-view <performance/replace-with-string-view>`, "Yes"
:doc:`performance-trivially-destructible <performance/trivially-destructible>`, "Yes"
:doc:`performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn>`, "Yes"
:doc:`performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization>`, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
.. title:: clang-tidy - performance-replace-with-string-view

performance-replace-with-string-view
====================================

Looks for functions returning ``std::[w|u8|u16|u32]string`` and suggests to
change it to ``std::[...]string_view`` for performance reasons if possible.

Rationale:

Each time a new ``std::string`` is created from a literal, a copy of that
literal is allocated either in ``std::string``'s internal buffer
(for short literals) or in a heap.

For the cases where ``std::string`` is returned from a function,
such allocations can be eliminated sometimes by using ``std::string_view``
as a return type.

This check looks for such functions returning ``std::string``
baked from the literals and suggests replacing the return type to ``std::string_view``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems longer than 80 chars, please maintain 80 chars limit :)


It handles ``std::string``, ``std::wstring``, ``std::u8string``,
``std::u16string`` and ``std::u32string`` along with their aliases and selects
the proper kind of ``std::string_view`` to return.

Example:

Consider the following code:

.. code-block:: c++

std::string foo(int i) {
switch(i)
{
case 1:
return "case 1";
case 2:
return "case 2";
case 3:
return "case 3";
default:
return "default";
}
}

In the code above a new ``std::string`` object is created on each function
invocation, making a copy of a string literal and possibly allocating a memory
in a heap.

The check gets this code transformed into:

.. code-block:: c++

std::string_view foo(int i) {
switch(i)
{
case 1:
return "case 1";
case 2:
return "case 2";
case 3:
return "case 3";
default:
return "default";
}
}

New version re-uses statically allocated literals without additional overhead.
Loading