1 //===--- UseAfterMoveCheck.cpp - clang-tidy -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "UseAfterMoveCheck.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
15 #include "clang/Analysis/CFG.h"
16 #include "clang/Lex/Lexer.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/SmallPtrSet.h"
20 #include "../utils/ExprSequence.h"
21 #include "../utils/Matchers.h"
24 using namespace clang::ast_matchers
;
25 using namespace clang::tidy::utils
;
27 namespace clang::tidy::bugprone
{
29 using matchers::hasUnevaluatedContext
;
33 /// Contains information about a use-after-move.
35 // The DeclRefExpr that constituted the use of the object.
36 const DeclRefExpr
*DeclRef
;
38 // Is the order in which the move and the use are evaluated undefined?
39 bool EvaluationOrderUndefined
= false;
41 // Does the use happen in a later loop iteration than the move?
43 // We default to false and change it to true if required in find().
44 bool UseHappensInLaterLoopIteration
= false;
47 /// Finds uses of a variable after a move (and maintains state required by the
48 /// various internal helper functions).
49 class UseAfterMoveFinder
{
51 UseAfterMoveFinder(ASTContext
*TheContext
);
53 // Within the given code block, finds the first use of 'MovedVariable' that
54 // occurs after 'MovingCall' (the expression that performs the move). If a
55 // use-after-move is found, writes information about it to 'TheUseAfterMove'.
56 // Returns whether a use-after-move was found.
57 std::optional
<UseAfterMove
> find(Stmt
*CodeBlock
, const Expr
*MovingCall
,
58 const DeclRefExpr
*MovedVariable
);
61 std::optional
<UseAfterMove
> findInternal(const CFGBlock
*Block
,
62 const Expr
*MovingCall
,
63 const ValueDecl
*MovedVariable
);
64 void getUsesAndReinits(const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
65 llvm::SmallVectorImpl
<const DeclRefExpr
*> *Uses
,
66 llvm::SmallPtrSetImpl
<const Stmt
*> *Reinits
);
67 void getDeclRefs(const CFGBlock
*Block
, const Decl
*MovedVariable
,
68 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
);
69 void getReinits(const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
70 llvm::SmallPtrSetImpl
<const Stmt
*> *Stmts
,
71 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
);
74 std::unique_ptr
<ExprSequence
> Sequence
;
75 std::unique_ptr
<StmtToBlockMap
> BlockMap
;
76 llvm::SmallPtrSet
<const CFGBlock
*, 8> Visited
;
81 // Matches nodes that are
82 // - Part of a decltype argument or class template argument (we check this by
83 // seeing if they are children of a TypeLoc), or
84 // - Part of a function template argument (we check this by seeing if they are
85 // children of a DeclRefExpr that references a function template).
86 // DeclRefExprs that fulfill these conditions should not be counted as a use or
88 static StatementMatcher
inDecltypeOrTemplateArg() {
89 return anyOf(hasAncestor(typeLoc()),
90 hasAncestor(declRefExpr(
91 to(functionDecl(ast_matchers::isTemplateInstantiation())))),
92 hasAncestor(expr(hasUnevaluatedContext())));
95 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext
*TheContext
)
96 : Context(TheContext
) {}
98 std::optional
<UseAfterMove
>
99 UseAfterMoveFinder::find(Stmt
*CodeBlock
, const Expr
*MovingCall
,
100 const DeclRefExpr
*MovedVariable
) {
101 // Generate the CFG manually instead of through an AnalysisDeclContext because
102 // it seems the latter can't be used to generate a CFG for the body of a
105 // We include implicit and temporary destructors in the CFG so that
106 // destructors marked [[noreturn]] are handled correctly in the control flow
107 // analysis. (These are used in some styles of assertion macros.)
108 CFG::BuildOptions Options
;
109 Options
.AddImplicitDtors
= true;
110 Options
.AddTemporaryDtors
= true;
111 std::unique_ptr
<CFG
> TheCFG
=
112 CFG::buildCFG(nullptr, CodeBlock
, Context
, Options
);
116 Sequence
= std::make_unique
<ExprSequence
>(TheCFG
.get(), CodeBlock
, Context
);
117 BlockMap
= std::make_unique
<StmtToBlockMap
>(TheCFG
.get(), Context
);
120 const CFGBlock
*MoveBlock
= BlockMap
->blockContainingStmt(MovingCall
);
122 // This can happen if MovingCall is in a constructor initializer, which is
123 // not included in the CFG because the CFG is built only from the function
125 MoveBlock
= &TheCFG
->getEntry();
128 auto TheUseAfterMove
=
129 findInternal(MoveBlock
, MovingCall
, MovedVariable
->getDecl());
131 if (TheUseAfterMove
) {
132 if (const CFGBlock
*UseBlock
=
133 BlockMap
->blockContainingStmt(TheUseAfterMove
->DeclRef
)) {
134 // Does the use happen in a later loop iteration than the move?
135 // - If they are in the same CFG block, we know the use happened in a
136 // later iteration if we visited that block a second time.
137 // - Otherwise, we know the use happened in a later iteration if the
138 // move is reachable from the use.
139 CFGReverseBlockReachabilityAnalysis
CFA(*TheCFG
);
140 TheUseAfterMove
->UseHappensInLaterLoopIteration
=
141 UseBlock
== MoveBlock
? Visited
.contains(UseBlock
)
142 : CFA
.isReachable(UseBlock
, MoveBlock
);
145 return TheUseAfterMove
;
148 std::optional
<UseAfterMove
>
149 UseAfterMoveFinder::findInternal(const CFGBlock
*Block
, const Expr
*MovingCall
,
150 const ValueDecl
*MovedVariable
) {
151 if (Visited
.count(Block
))
154 // Mark the block as visited (except if this is the block containing the
155 // std::move() and it's being visited the first time).
157 Visited
.insert(Block
);
159 // Get all uses and reinits in the block.
160 llvm::SmallVector
<const DeclRefExpr
*, 1> Uses
;
161 llvm::SmallPtrSet
<const Stmt
*, 1> Reinits
;
162 getUsesAndReinits(Block
, MovedVariable
, &Uses
, &Reinits
);
164 // Ignore all reinitializations where the move potentially comes after the
166 // If `Reinit` is identical to `MovingCall`, we're looking at a move-to-self
167 // (e.g. `a = std::move(a)`). Count these as reinitializations.
168 llvm::SmallVector
<const Stmt
*, 1> ReinitsToDelete
;
169 for (const Stmt
*Reinit
: Reinits
) {
170 if (MovingCall
&& Reinit
!= MovingCall
&&
171 Sequence
->potentiallyAfter(MovingCall
, Reinit
))
172 ReinitsToDelete
.push_back(Reinit
);
174 for (const Stmt
*Reinit
: ReinitsToDelete
) {
175 Reinits
.erase(Reinit
);
178 // Find all uses that potentially come after the move.
179 for (const DeclRefExpr
*Use
: Uses
) {
180 if (!MovingCall
|| Sequence
->potentiallyAfter(Use
, MovingCall
)) {
181 // Does the use have a saving reinit? A reinit is saving if it definitely
182 // comes before the use, i.e. if there's no potential that the reinit is
184 bool HaveSavingReinit
= false;
185 for (const Stmt
*Reinit
: Reinits
) {
186 if (!Sequence
->potentiallyAfter(Reinit
, Use
))
187 HaveSavingReinit
= true;
190 if (!HaveSavingReinit
) {
191 UseAfterMove TheUseAfterMove
;
192 TheUseAfterMove
.DeclRef
= Use
;
194 // Is this a use-after-move that depends on order of evaluation?
195 // This is the case if the move potentially comes after the use (and we
196 // already know that use potentially comes after the move, which taken
197 // together tells us that the ordering is unclear).
198 TheUseAfterMove
.EvaluationOrderUndefined
=
199 MovingCall
!= nullptr &&
200 Sequence
->potentiallyAfter(MovingCall
, Use
);
202 return TheUseAfterMove
;
207 // If the object wasn't reinitialized, call ourselves recursively on all
209 if (Reinits
.empty()) {
210 for (const auto &Succ
: Block
->succs()) {
212 if (auto Found
= findInternal(Succ
, nullptr, MovedVariable
)) {
222 void UseAfterMoveFinder::getUsesAndReinits(
223 const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
224 llvm::SmallVectorImpl
<const DeclRefExpr
*> *Uses
,
225 llvm::SmallPtrSetImpl
<const Stmt
*> *Reinits
) {
226 llvm::SmallPtrSet
<const DeclRefExpr
*, 1> DeclRefs
;
227 llvm::SmallPtrSet
<const DeclRefExpr
*, 1> ReinitDeclRefs
;
229 getDeclRefs(Block
, MovedVariable
, &DeclRefs
);
230 getReinits(Block
, MovedVariable
, Reinits
, &ReinitDeclRefs
);
232 // All references to the variable that aren't reinitializations are uses.
234 for (const DeclRefExpr
*DeclRef
: DeclRefs
) {
235 if (!ReinitDeclRefs
.count(DeclRef
))
236 Uses
->push_back(DeclRef
);
239 // Sort the uses by their occurrence in the source code.
240 llvm::sort(*Uses
, [](const DeclRefExpr
*D1
, const DeclRefExpr
*D2
) {
241 return D1
->getExprLoc() < D2
->getExprLoc();
245 bool isStandardSmartPointer(const ValueDecl
*VD
) {
246 const Type
*TheType
= VD
->getType().getNonReferenceType().getTypePtrOrNull();
250 const CXXRecordDecl
*RecordDecl
= TheType
->getAsCXXRecordDecl();
254 const IdentifierInfo
*ID
= RecordDecl
->getIdentifier();
258 StringRef Name
= ID
->getName();
259 if (Name
!= "unique_ptr" && Name
!= "shared_ptr" && Name
!= "weak_ptr")
262 return RecordDecl
->getDeclContext()->isStdNamespace();
265 void UseAfterMoveFinder::getDeclRefs(
266 const CFGBlock
*Block
, const Decl
*MovedVariable
,
267 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
) {
269 for (const auto &Elem
: *Block
) {
270 std::optional
<CFGStmt
> S
= Elem
.getAs
<CFGStmt
>();
274 auto AddDeclRefs
= [this, Block
,
275 DeclRefs
](const ArrayRef
<BoundNodes
> Matches
) {
276 for (const auto &Match
: Matches
) {
277 const auto *DeclRef
= Match
.getNodeAs
<DeclRefExpr
>("declref");
278 const auto *Operator
= Match
.getNodeAs
<CXXOperatorCallExpr
>("operator");
279 if (DeclRef
&& BlockMap
->blockContainingStmt(DeclRef
) == Block
) {
280 // Ignore uses of a standard smart pointer that don't dereference the
282 if (Operator
|| !isStandardSmartPointer(DeclRef
->getDecl())) {
283 DeclRefs
->insert(DeclRef
);
289 auto DeclRefMatcher
= declRefExpr(hasDeclaration(equalsNode(MovedVariable
)),
290 unless(inDecltypeOrTemplateArg()))
293 AddDeclRefs(match(traverse(TK_AsIs
, findAll(DeclRefMatcher
)), *S
->getStmt(),
295 AddDeclRefs(match(findAll(cxxOperatorCallExpr(
296 hasAnyOverloadedOperatorName("*", "->", "[]"),
297 hasArgument(0, DeclRefMatcher
))
299 *S
->getStmt(), *Context
));
303 void UseAfterMoveFinder::getReinits(
304 const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
305 llvm::SmallPtrSetImpl
<const Stmt
*> *Stmts
,
306 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
) {
307 auto DeclRefMatcher
=
308 declRefExpr(hasDeclaration(equalsNode(MovedVariable
))).bind("declref");
310 auto StandardContainerTypeMatcher
= hasType(hasUnqualifiedDesugaredType(
311 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
312 "::std::basic_string", "::std::vector", "::std::deque",
313 "::std::forward_list", "::std::list", "::std::set", "::std::map",
314 "::std::multiset", "::std::multimap", "::std::unordered_set",
315 "::std::unordered_map", "::std::unordered_multiset",
316 "::std::unordered_multimap"))))));
318 auto StandardResettableOwnerTypeMatcher
= hasType(
319 hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
320 hasAnyName("::std::unique_ptr", "::std::shared_ptr",
321 "::std::weak_ptr", "::std::optional", "::std::any"))))));
323 // Matches different types of reinitialization.
326 // Assignment. In addition to the overloaded assignment operator,
327 // test for built-in assignment as well, since template functions
328 // may be instantiated to use std::move() on built-in types.
329 binaryOperation(hasOperatorName("="), hasLHS(DeclRefMatcher
)),
330 // Declaration. We treat this as a type of reinitialization too,
331 // so we don't need to treat it separately.
332 declStmt(hasDescendant(equalsNode(MovedVariable
))),
333 // clear() and assign() on standard containers.
335 on(expr(DeclRefMatcher
, StandardContainerTypeMatcher
)),
336 // To keep the matcher simple, we check for assign() calls
337 // on all standard containers, even though only vector,
338 // deque, forward_list and list have assign(). If assign()
339 // is called on any of the other containers, this will be
340 // flagged by a compile error anyway.
341 callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
342 // reset() on standard smart pointers.
344 on(expr(DeclRefMatcher
, StandardResettableOwnerTypeMatcher
)),
345 callee(cxxMethodDecl(hasName("reset")))),
346 // Methods that have the [[clang::reinitializes]] attribute.
349 callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes
)))),
350 // Passing variable to a function as a non-const pointer.
351 callExpr(forEachArgumentWithParam(
352 unaryOperator(hasOperatorName("&"),
353 hasUnaryOperand(DeclRefMatcher
)),
354 unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
355 // Passing variable to a function as a non-const lvalue reference
356 // (unless that function is std::move()).
357 callExpr(forEachArgumentWithParam(
358 traverse(TK_AsIs
, DeclRefMatcher
),
359 unless(parmVarDecl(hasType(
360 references(qualType(isConstQualified())))))),
361 unless(callee(functionDecl(
362 hasAnyName("::std::move", "::std::forward")))))))
367 for (const auto &Elem
: *Block
) {
368 std::optional
<CFGStmt
> S
= Elem
.getAs
<CFGStmt
>();
372 SmallVector
<BoundNodes
, 1> Matches
=
373 match(findAll(ReinitMatcher
), *S
->getStmt(), *Context
);
375 for (const auto &Match
: Matches
) {
376 const auto *TheStmt
= Match
.getNodeAs
<Stmt
>("reinit");
377 const auto *TheDeclRef
= Match
.getNodeAs
<DeclRefExpr
>("declref");
378 if (TheStmt
&& BlockMap
->blockContainingStmt(TheStmt
) == Block
) {
379 Stmts
->insert(TheStmt
);
381 // We count DeclStmts as reinitializations, but they don't have a
382 // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'
383 // before adding it to the set.
385 DeclRefs
->insert(TheDeclRef
);
391 enum class MoveType
{
393 Forward
, // std::forward
396 static MoveType
determineMoveType(const FunctionDecl
*FuncDecl
) {
397 if (FuncDecl
->getName() == "move")
398 return MoveType::Move
;
399 if (FuncDecl
->getName() == "forward")
400 return MoveType::Forward
;
402 llvm_unreachable("Invalid move type");
405 static void emitDiagnostic(const Expr
*MovingCall
, const DeclRefExpr
*MoveArg
,
406 const UseAfterMove
&Use
, ClangTidyCheck
*Check
,
407 ASTContext
*Context
, MoveType Type
) {
408 const SourceLocation UseLoc
= Use
.DeclRef
->getExprLoc();
409 const SourceLocation MoveLoc
= MovingCall
->getExprLoc();
411 const bool IsMove
= (Type
== MoveType::Move
);
413 Check
->diag(UseLoc
, "'%0' used after it was %select{forwarded|moved}1")
414 << MoveArg
->getDecl()->getName() << IsMove
;
415 Check
->diag(MoveLoc
, "%select{forward|move}0 occurred here",
418 if (Use
.EvaluationOrderUndefined
) {
421 "the use and %select{forward|move}0 are unsequenced, i.e. "
422 "there is no guarantee about the order in which they are evaluated",
425 } else if (Use
.UseHappensInLaterLoopIteration
) {
427 "the use happens in a later loop iteration than the "
428 "%select{forward|move}0",
434 void UseAfterMoveCheck::registerMatchers(MatchFinder
*Finder
) {
435 // try_emplace is a common maybe-moving function that returns a
436 // bool to tell callers whether it moved. Ignore std::move inside
437 // try_emplace to avoid false positives as we don't track uses of
439 auto TryEmplaceMatcher
=
440 cxxMemberCallExpr(callee(cxxMethodDecl(hasName("try_emplace"))));
441 auto CallMoveMatcher
=
442 callExpr(argumentCountIs(1),
443 callee(functionDecl(hasAnyName("::std::move", "::std::forward"))
445 hasArgument(0, declRefExpr().bind("arg")),
446 unless(inDecltypeOrTemplateArg()),
447 unless(hasParent(TryEmplaceMatcher
)), expr().bind("call-move"),
448 anyOf(hasAncestor(compoundStmt(
449 hasParent(lambdaExpr().bind("containing-lambda")))),
450 hasAncestor(functionDecl(anyOf(
452 hasAnyConstructorInitializer(withInitializer(
453 expr(anyOf(equalsBoundNode("call-move"),
455 equalsBoundNode("call-move")))))
456 .bind("containing-ctor-init"))))
457 .bind("containing-ctor"),
458 functionDecl().bind("containing-func"))))));
463 // To find the Stmt that we assume performs the actual move, we look
464 // for the direct ancestor of the std::move() that isn't one of the
465 // node types ignored by ignoringParenImpCasts().
467 forEach(expr(ignoringParenImpCasts(CallMoveMatcher
))),
468 // Don't allow an InitListExpr to be the moving call. An
469 // InitListExpr has both a syntactic and a semantic form, and the
470 // parent-child relationships are different between the two. This
471 // could cause an InitListExpr to be analyzed as the moving call
472 // in addition to the Expr that we actually want, resulting in two
473 // diagnostics with different code locations for the same move.
474 unless(initListExpr()),
475 unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
476 .bind("moving-call")),
480 void UseAfterMoveCheck::check(const MatchFinder::MatchResult
&Result
) {
481 const auto *ContainingCtor
=
482 Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("containing-ctor");
483 const auto *ContainingCtorInit
=
484 Result
.Nodes
.getNodeAs
<Expr
>("containing-ctor-init");
485 const auto *ContainingLambda
=
486 Result
.Nodes
.getNodeAs
<LambdaExpr
>("containing-lambda");
487 const auto *ContainingFunc
=
488 Result
.Nodes
.getNodeAs
<FunctionDecl
>("containing-func");
489 const auto *CallMove
= Result
.Nodes
.getNodeAs
<CallExpr
>("call-move");
490 const auto *MovingCall
= Result
.Nodes
.getNodeAs
<Expr
>("moving-call");
491 const auto *Arg
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("arg");
492 const auto *MoveDecl
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("move-decl");
494 if (!MovingCall
|| !MovingCall
->getExprLoc().isValid())
495 MovingCall
= CallMove
;
497 // Ignore the std::move if the variable that was passed to it isn't a local
499 if (!Arg
->getDecl()->getDeclContext()->isFunctionOrMethod())
502 // Collect all code blocks that could use the arg after move.
503 llvm::SmallVector
<Stmt
*> CodeBlocks
{};
504 if (ContainingCtor
) {
505 CodeBlocks
.push_back(ContainingCtor
->getBody());
506 if (ContainingCtorInit
) {
507 // Collect the constructor initializer expressions.
508 bool BeforeMove
{true};
509 for (CXXCtorInitializer
*Init
: ContainingCtor
->inits()) {
510 if (BeforeMove
&& Init
->getInit()->IgnoreImplicit() ==
511 ContainingCtorInit
->IgnoreImplicit())
514 CodeBlocks
.push_back(Init
->getInit());
517 } else if (ContainingLambda
) {
518 CodeBlocks
.push_back(ContainingLambda
->getBody());
519 } else if (ContainingFunc
) {
520 CodeBlocks
.push_back(ContainingFunc
->getBody());
523 for (Stmt
*CodeBlock
: CodeBlocks
) {
524 UseAfterMoveFinder
Finder(Result
.Context
);
525 if (auto Use
= Finder
.find(CodeBlock
, MovingCall
, Arg
))
526 emitDiagnostic(MovingCall
, Arg
, *Use
, this, Result
.Context
,
527 determineMoveType(MoveDecl
));
531 } // namespace clang::tidy::bugprone