Skip to content

Commit 3abbce9

Browse files
committed
[clang-tidy] Add check performance-lost-std-move
1 parent ed6dc62 commit 3abbce9

File tree

8 files changed

+264
-0
lines changed

8 files changed

+264
-0
lines changed

clang-tools-extra/clang-tidy/performance/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_clang_library(clangTidyPerformanceModule
1212
InefficientAlgorithmCheck.cpp
1313
InefficientStringConcatenationCheck.cpp
1414
InefficientVectorOperationCheck.cpp
15+
LostStdMoveCheck.cpp
1516
MoveConstArgCheck.cpp
1617
MoveConstructorInitCheck.cpp
1718
NoAutomaticMoveCheck.cpp
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===--- LostStdMoveCheck.cpp - clang-tidy --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "LostStdMoveCheck.h"
10+
#include "../utils/DeclRefExprUtils.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang::tidy::performance {
16+
17+
using utils::decl_ref_expr::allDeclRefExprs;
18+
19+
AST_MATCHER(CXXRecordDecl, hasTrivialMoveConstructor) {
20+
return Node.hasDefinition() && Node.hasTrivialMoveConstructor();
21+
}
22+
23+
void LostStdMoveCheck::registerMatchers(MatchFinder *Finder) {
24+
auto returnParent =
25+
hasParent(expr(hasParent(cxxConstructExpr(hasParent(returnStmt())))));
26+
27+
Finder->addMatcher(
28+
declRefExpr(
29+
// not "return x;"
30+
unless(returnParent),
31+
32+
unless(hasType(namedDecl(hasName("::std::string_view")))),
33+
34+
// non-trivial type
35+
hasType(hasCanonicalType(hasDeclaration(cxxRecordDecl()))),
36+
37+
// non-trivial X(X&&)
38+
unless(hasType(hasCanonicalType(
39+
hasDeclaration(cxxRecordDecl(hasTrivialMoveConstructor()))))),
40+
41+
// Not in a cycle
42+
unless(hasAncestor(forStmt())), unless(hasAncestor(doStmt())),
43+
unless(hasAncestor(whileStmt())),
44+
45+
// only non-X&
46+
unless(hasDeclaration(
47+
varDecl(hasType(qualType(lValueReferenceType()))))),
48+
49+
hasDeclaration(
50+
varDecl(hasAncestor(functionDecl().bind("func"))).bind("decl")),
51+
52+
hasParent(expr(hasParent(cxxConstructExpr())).bind("use_parent")))
53+
.bind("use"),
54+
this);
55+
}
56+
57+
const Expr *LostStdMoveCheck::getLastVarUsage(const VarDecl &Var,
58+
const Decl &Func,
59+
ASTContext &Context) {
60+
auto Exprs = allDeclRefExprs(Var, Func, Context);
61+
62+
const Expr *LastExpr = nullptr;
63+
for (const auto &Expr : Exprs) {
64+
if (!LastExpr)
65+
LastExpr = Expr;
66+
67+
if (LastExpr->getBeginLoc() < Expr->getBeginLoc())
68+
LastExpr = Expr;
69+
}
70+
71+
return LastExpr;
72+
}
73+
74+
void LostStdMoveCheck::check(const MatchFinder::MatchResult &Result) {
75+
const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("decl");
76+
const auto *MatchedFunc = Result.Nodes.getNodeAs<FunctionDecl>("func");
77+
const auto *MatchedUse = Result.Nodes.getNodeAs<Expr>("use");
78+
const auto *MatchedUseCall = Result.Nodes.getNodeAs<CallExpr>("use_parent");
79+
80+
if (MatchedUseCall)
81+
return;
82+
83+
const auto *LastUsage =
84+
getLastVarUsage(*MatchedDecl, *MatchedFunc, *Result.Context);
85+
if (LastUsage == nullptr)
86+
return;
87+
88+
if (LastUsage->getBeginLoc() > MatchedUse->getBeginLoc()) {
89+
// "use" is not the last reference to x
90+
return;
91+
}
92+
93+
diag(LastUsage->getBeginLoc(), "Could be std::move()");
94+
}
95+
96+
} // namespace clang::tidy::performance
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- LostStdMoveCheck.h - clang-tidy ------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_LOSTSTDMOVECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_LOSTSTDMOVECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::performance {
15+
16+
/// Search for lost std::move().
17+
///
18+
/// For the user-facing documentation see:
19+
/// http://clang.llvm.org/extra/clang-tidy/checks/performance/lost-std-move.html
20+
class LostStdMoveCheck : public ClangTidyCheck {
21+
public:
22+
LostStdMoveCheck(StringRef Name, ClangTidyContext *Context)
23+
: ClangTidyCheck(Name, Context) {}
24+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
25+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
26+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
27+
return LangOpts.CPlusPlus;
28+
}
29+
30+
private:
31+
const Expr *getLastVarUsage(const VarDecl &Var, const Decl &Func,
32+
ASTContext &Context);
33+
};
34+
35+
} // namespace clang::tidy::performance
36+
37+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PERFORMANCE_LOSTSTDMOVECHECK_H

clang-tools-extra/clang-tidy/performance/PerformanceTidyModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "InefficientAlgorithmCheck.h"
1818
#include "InefficientStringConcatenationCheck.h"
1919
#include "InefficientVectorOperationCheck.h"
20+
#include "LostStdMoveCheck.h"
2021
#include "MoveConstArgCheck.h"
2122
#include "MoveConstructorInitCheck.h"
2223
#include "NoAutomaticMoveCheck.h"
@@ -49,6 +50,7 @@ class PerformanceModule : public ClangTidyModule {
4950
"performance-inefficient-string-concatenation");
5051
CheckFactories.registerCheck<InefficientVectorOperationCheck>(
5152
"performance-inefficient-vector-operation");
53+
CheckFactories.registerCheck<LostStdMoveCheck>("performance-lost-std-move");
5254
CheckFactories.registerCheck<MoveConstArgCheck>(
5355
"performance-move-const-arg");
5456
CheckFactories.registerCheck<MoveConstructorInitCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ New checks
212212
Recommends the smallest possible underlying type for an ``enum`` or ``enum``
213213
class based on the range of its enumerators.
214214

215+
- New :doc:`performance-lost-std-move
216+
<clang-tidy/checks/performance/lost-std-move>` check.
217+
218+
Searches for lost std::move().
219+
215220
- New :doc:`readability-reference-to-constructed-temporary
216221
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
217222

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ Clang-Tidy Checks
321321
:doc:`performance-inefficient-algorithm <performance/inefficient-algorithm>`, "Yes"
322322
:doc:`performance-inefficient-string-concatenation <performance/inefficient-string-concatenation>`,
323323
:doc:`performance-inefficient-vector-operation <performance/inefficient-vector-operation>`, "Yes"
324+
:doc:`performance-lost-std-move <performance/lost-std-move>`, "Yes"
324325
:doc:`performance-move-const-arg <performance/move-const-arg>`, "Yes"
325326
:doc:`performance-move-constructor-init <performance/move-constructor-init>`,
326327
:doc:`performance-no-automatic-move <performance/no-automatic-move>`,
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.. title:: clang-tidy - performance-lost-std-move
2+
3+
performance-lost-std-move
4+
=========================
5+
6+
The check warns if copy constructor is used instead of std::move().
7+
8+
.. code-block:: c++
9+
10+
void f(X);
11+
12+
void g(X x) {
13+
f(x); // warning: Could be std::move() [performance-lost-std-move]
14+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %check_clang_tidy %s performance-lost-std-move %t
2+
3+
namespace std {
4+
5+
template<typename T>
6+
class shared_ptr {
7+
public:
8+
T& operator*() { return reinterpret_cast<T&>(*this); }
9+
shared_ptr() {}
10+
shared_ptr(const shared_ptr<T>&) {}
11+
};
12+
13+
template<typename T>
14+
T&& move(T&)
15+
{
16+
}
17+
18+
} // namespace std
19+
20+
int f(std::shared_ptr<int>);
21+
22+
void f_arg(std::shared_ptr<int> ptr)
23+
{
24+
if (*ptr)
25+
f(ptr);
26+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Could be std::move() [performance-lost-std-move]
27+
}
28+
29+
void f_rvalue_ref(std::shared_ptr<int>&& ptr)
30+
{
31+
if (*ptr)
32+
f(ptr);
33+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Could be std::move() [performance-lost-std-move]
34+
}
35+
36+
using SharedPtr = std::shared_ptr<int>;
37+
void f_using(SharedPtr ptr)
38+
{
39+
if (*ptr)
40+
f(ptr);
41+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Could be std::move() [performance-lost-std-move]
42+
}
43+
44+
void f_local()
45+
{
46+
std::shared_ptr<int> ptr;
47+
if (*ptr)
48+
f(ptr);
49+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: Could be std::move() [performance-lost-std-move]
50+
}
51+
52+
void f_move()
53+
{
54+
std::shared_ptr<int> ptr;
55+
if (*ptr)
56+
f(std::move(ptr));
57+
}
58+
59+
void f_ref(std::shared_ptr<int> &ptr)
60+
{
61+
if (*ptr)
62+
f(ptr);
63+
}
64+
65+
std::shared_ptr<int> f_return()
66+
{
67+
std::shared_ptr<int> ptr;
68+
return ptr;
69+
}
70+
71+
void f_still_used(std::shared_ptr<int> ptr)
72+
{
73+
if (*ptr)
74+
f(ptr);
75+
76+
*ptr = 1;
77+
*ptr = *ptr;
78+
}
79+
80+
void f_cycle1()
81+
{
82+
std::shared_ptr<int> ptr;
83+
for(;;)
84+
f(ptr);
85+
}
86+
87+
void f_cycle2()
88+
{
89+
std::shared_ptr<int> ptr;
90+
for(int i=0; i<5; i++)
91+
f(ptr);
92+
}
93+
94+
void f_cycle3()
95+
{
96+
std::shared_ptr<int> ptr;
97+
while (*ptr) {
98+
f(ptr);
99+
}
100+
}
101+
102+
void f_cycle4()
103+
{
104+
std::shared_ptr<int> ptr;
105+
do {
106+
f(ptr);
107+
} while (*ptr);
108+
}

0 commit comments

Comments
 (0)