Skip to content

Commit 8033867

Browse files
committed
[clang-tidy] Let bugprone-use-after-move also handle calls to std::forward
Fixes #82023
1 parent f1e0392 commit 8033867

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-12
lines changed

clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ void UseAfterMoveFinder::getReinits(
330330
traverse(TK_AsIs, DeclRefMatcher),
331331
unless(parmVarDecl(hasType(
332332
references(qualType(isConstQualified())))))),
333-
unless(callee(functionDecl(hasName("::std::move")))))))
333+
unless(callee(functionDecl(
334+
hasAnyName("::std::move", "::std::forward")))))))
334335
.bind("reinit");
335336

336337
Stmts->clear();
@@ -359,24 +360,52 @@ void UseAfterMoveFinder::getReinits(
359360
}
360361
}
361362

363+
enum class MoveType {
364+
Move, // std::move
365+
Forward, // std::forward
366+
};
367+
368+
static MoveType determineMoveType(const FunctionDecl *FuncDecl) {
369+
if (FuncDecl->getName() == "move")
370+
return MoveType::Move;
371+
if (FuncDecl->getName() == "forward")
372+
return MoveType::Forward;
373+
374+
assert(false && "Invalid move type");
375+
}
376+
362377
static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg,
363378
const UseAfterMove &Use, ClangTidyCheck *Check,
364-
ASTContext *Context) {
379+
ASTContext *Context, MoveType Type) {
365380
SourceLocation UseLoc = Use.DeclRef->getExprLoc();
366381
SourceLocation MoveLoc = MovingCall->getExprLoc();
367382

368-
Check->diag(UseLoc, "'%0' used after it was moved")
369-
<< MoveArg->getDecl()->getName();
370-
Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note);
383+
StringRef ActionType;
384+
StringRef ActionTypePastTense;
385+
switch (Type) {
386+
case MoveType::Move:
387+
ActionType = "move";
388+
ActionTypePastTense = "moved";
389+
break;
390+
case MoveType::Forward:
391+
ActionType = "forward";
392+
ActionTypePastTense = "forwarded";
393+
break;
394+
}
395+
396+
Check->diag(UseLoc, "'%0' used after it was %1")
397+
<< MoveArg->getDecl()->getName() << ActionTypePastTense;
398+
Check->diag(MoveLoc, "%0 occurred here", DiagnosticIDs::Note) << ActionType;
371399
if (Use.EvaluationOrderUndefined) {
372400
Check->diag(UseLoc,
373-
"the use and move are unsequenced, i.e. there is no guarantee "
401+
"the use and %0 are unsequenced, i.e. there is no guarantee "
374402
"about the order in which they are evaluated",
375-
DiagnosticIDs::Note);
403+
DiagnosticIDs::Note)
404+
<< ActionType;
376405
} else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
377-
Check->diag(UseLoc,
378-
"the use happens in a later loop iteration than the move",
379-
DiagnosticIDs::Note);
406+
Check->diag(UseLoc, "the use happens in a later loop iteration than the %0",
407+
DiagnosticIDs::Note)
408+
<< ActionType;
380409
}
381410
}
382411

@@ -388,7 +417,9 @@ void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
388417
auto TryEmplaceMatcher =
389418
cxxMemberCallExpr(callee(cxxMethodDecl(hasName("try_emplace"))));
390419
auto CallMoveMatcher =
391-
callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))),
420+
callExpr(argumentCountIs(1),
421+
callee(functionDecl(hasAnyName("::std::move", "::std::forward"))
422+
.bind("move-decl")),
392423
hasArgument(0, declRefExpr().bind("arg")),
393424
unless(inDecltypeOrTemplateArg()),
394425
unless(hasParent(TryEmplaceMatcher)), expr().bind("call-move"),
@@ -436,6 +467,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
436467
const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move");
437468
const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call");
438469
const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg");
470+
const auto *MoveDecl = Result.Nodes.getNodeAs<FunctionDecl>("move-decl");
439471

440472
if (!MovingCall || !MovingCall->getExprLoc().isValid())
441473
MovingCall = CallMove;
@@ -470,7 +502,8 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) {
470502
UseAfterMoveFinder Finder(Result.Context);
471503
UseAfterMove Use;
472504
if (Finder.find(CodeBlock, MovingCall, Arg->getDecl(), &Use))
473-
emitDiagnostic(MovingCall, Arg, Use, this, Result.Context);
505+
emitDiagnostic(MovingCall, Arg, Use, this, Result.Context,
506+
determineMoveType(MoveDecl));
474507
}
475508
}
476509

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ Changes in existing checks
130130
<clang-tidy/checks/bugprone/unused-local-non-trivial-variable>` check by
131131
ignoring local variable with ``[maybe_unused]`` attribute.
132132

133+
- Improved :doc:`bugprone-use-after-move
134+
<clang-tidy/checks/bugprone/use-after-move>` check to also handle
135+
calls to ``std::forward``.
136+
133137
- Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer
134138
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>`
135139
by removing enforcement of rule `C.48

clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept {
111111
return static_cast<typename remove_reference<_Tp>::type &&>(__t);
112112
}
113113

114+
template <class _Tp>
115+
constexpr _Tp&&
116+
forward(typename std::remove_reference<_Tp>::type& __t) noexcept {
117+
return static_cast<_Tp&&>(__t);
118+
}
119+
120+
template <class _Tp>
121+
constexpr _Tp&&
122+
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept {
123+
return static_cast<_Tp&&>(__t);
124+
}
125+
114126
} // namespace std
115127

116128
class A {
@@ -1525,3 +1537,28 @@ class PR38187 {
15251537
private:
15261538
std::string val_;
15271539
};
1540+
1541+
namespace issue82023
1542+
{
1543+
1544+
struct S {
1545+
S();
1546+
S(S&&);
1547+
};
1548+
1549+
void consume(S s);
1550+
1551+
template <typename T>
1552+
void forward(T&& t) {
1553+
consume(std::forward<T>(t));
1554+
consume(std::forward<T>(t));
1555+
// CHECK-NOTES: [[@LINE-1]]:27: warning: 't' used after it was forwarded
1556+
// CHECK-NOTES: [[@LINE-3]]:11: note: forward occurred here
1557+
}
1558+
1559+
void create() {
1560+
S s;
1561+
forward(std::move(s));
1562+
}
1563+
1564+
} // namespace issue82023

0 commit comments

Comments
 (0)