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/CFG.h"
15 #include "clang/Lex/Lexer.h"
16 #include "llvm/ADT/STLExtras.h"
18 #include "../utils/ExprSequence.h"
19 #include "../utils/Matchers.h"
22 using namespace clang::ast_matchers
;
23 using namespace clang::tidy::utils
;
25 namespace clang::tidy::bugprone
{
27 using matchers::hasUnevaluatedContext
;
31 /// Contains information about a use-after-move.
33 // The DeclRefExpr that constituted the use of the object.
34 const DeclRefExpr
*DeclRef
;
36 // Is the order in which the move and the use are evaluated undefined?
37 bool EvaluationOrderUndefined
;
40 /// Finds uses of a variable after a move (and maintains state required by the
41 /// various internal helper functions).
42 class UseAfterMoveFinder
{
44 UseAfterMoveFinder(ASTContext
*TheContext
);
46 // Within the given code block, finds the first use of 'MovedVariable' that
47 // occurs after 'MovingCall' (the expression that performs the move). If a
48 // use-after-move is found, writes information about it to 'TheUseAfterMove'.
49 // Returns whether a use-after-move was found.
50 bool find(Stmt
*CodeBlock
, const Expr
*MovingCall
,
51 const ValueDecl
*MovedVariable
, UseAfterMove
*TheUseAfterMove
);
54 bool findInternal(const CFGBlock
*Block
, const Expr
*MovingCall
,
55 const ValueDecl
*MovedVariable
,
56 UseAfterMove
*TheUseAfterMove
);
57 void getUsesAndReinits(const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
58 llvm::SmallVectorImpl
<const DeclRefExpr
*> *Uses
,
59 llvm::SmallPtrSetImpl
<const Stmt
*> *Reinits
);
60 void getDeclRefs(const CFGBlock
*Block
, const Decl
*MovedVariable
,
61 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
);
62 void getReinits(const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
63 llvm::SmallPtrSetImpl
<const Stmt
*> *Stmts
,
64 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
);
67 std::unique_ptr
<ExprSequence
> Sequence
;
68 std::unique_ptr
<StmtToBlockMap
> BlockMap
;
69 llvm::SmallPtrSet
<const CFGBlock
*, 8> Visited
;
74 // Matches nodes that are
75 // - Part of a decltype argument or class template argument (we check this by
76 // seeing if they are children of a TypeLoc), or
77 // - Part of a function template argument (we check this by seeing if they are
78 // children of a DeclRefExpr that references a function template).
79 // DeclRefExprs that fulfill these conditions should not be counted as a use or
81 static StatementMatcher
inDecltypeOrTemplateArg() {
82 return anyOf(hasAncestor(typeLoc()),
83 hasAncestor(declRefExpr(
84 to(functionDecl(ast_matchers::isTemplateInstantiation())))),
85 hasAncestor(expr(hasUnevaluatedContext())));
88 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext
*TheContext
)
89 : Context(TheContext
) {}
91 bool UseAfterMoveFinder::find(Stmt
*CodeBlock
, const Expr
*MovingCall
,
92 const ValueDecl
*MovedVariable
,
93 UseAfterMove
*TheUseAfterMove
) {
94 // Generate the CFG manually instead of through an AnalysisDeclContext because
95 // it seems the latter can't be used to generate a CFG for the body of a
98 // We include implicit and temporary destructors in the CFG so that
99 // destructors marked [[noreturn]] are handled correctly in the control flow
100 // analysis. (These are used in some styles of assertion macros.)
101 CFG::BuildOptions Options
;
102 Options
.AddImplicitDtors
= true;
103 Options
.AddTemporaryDtors
= true;
104 std::unique_ptr
<CFG
> TheCFG
=
105 CFG::buildCFG(nullptr, CodeBlock
, Context
, Options
);
109 Sequence
= std::make_unique
<ExprSequence
>(TheCFG
.get(), CodeBlock
, Context
);
110 BlockMap
= std::make_unique
<StmtToBlockMap
>(TheCFG
.get(), Context
);
113 const CFGBlock
*Block
= BlockMap
->blockContainingStmt(MovingCall
);
115 // This can happen if MovingCall is in a constructor initializer, which is
116 // not included in the CFG because the CFG is built only from the function
118 Block
= &TheCFG
->getEntry();
121 return findInternal(Block
, MovingCall
, MovedVariable
, TheUseAfterMove
);
124 bool UseAfterMoveFinder::findInternal(const CFGBlock
*Block
,
125 const Expr
*MovingCall
,
126 const ValueDecl
*MovedVariable
,
127 UseAfterMove
*TheUseAfterMove
) {
128 if (Visited
.count(Block
))
131 // Mark the block as visited (except if this is the block containing the
132 // std::move() and it's being visited the first time).
134 Visited
.insert(Block
);
136 // Get all uses and reinits in the block.
137 llvm::SmallVector
<const DeclRefExpr
*, 1> Uses
;
138 llvm::SmallPtrSet
<const Stmt
*, 1> Reinits
;
139 getUsesAndReinits(Block
, MovedVariable
, &Uses
, &Reinits
);
141 // Ignore all reinitializations where the move potentially comes after the
143 // If `Reinit` is identical to `MovingCall`, we're looking at a move-to-self
144 // (e.g. `a = std::move(a)`). Count these as reinitializations.
145 llvm::SmallVector
<const Stmt
*, 1> ReinitsToDelete
;
146 for (const Stmt
*Reinit
: Reinits
) {
147 if (MovingCall
&& Reinit
!= MovingCall
&&
148 Sequence
->potentiallyAfter(MovingCall
, Reinit
))
149 ReinitsToDelete
.push_back(Reinit
);
151 for (const Stmt
*Reinit
: ReinitsToDelete
) {
152 Reinits
.erase(Reinit
);
155 // Find all uses that potentially come after the move.
156 for (const DeclRefExpr
*Use
: Uses
) {
157 if (!MovingCall
|| Sequence
->potentiallyAfter(Use
, MovingCall
)) {
158 // Does the use have a saving reinit? A reinit is saving if it definitely
159 // comes before the use, i.e. if there's no potential that the reinit is
161 bool HaveSavingReinit
= false;
162 for (const Stmt
*Reinit
: Reinits
) {
163 if (!Sequence
->potentiallyAfter(Reinit
, Use
))
164 HaveSavingReinit
= true;
167 if (!HaveSavingReinit
) {
168 TheUseAfterMove
->DeclRef
= Use
;
170 // Is this a use-after-move that depends on order of evaluation?
171 // This is the case if the move potentially comes after the use (and we
172 // already know that use potentially comes after the move, which taken
173 // together tells us that the ordering is unclear).
174 TheUseAfterMove
->EvaluationOrderUndefined
=
175 MovingCall
!= nullptr &&
176 Sequence
->potentiallyAfter(MovingCall
, Use
);
183 // If the object wasn't reinitialized, call ourselves recursively on all
185 if (Reinits
.empty()) {
186 for (const auto &Succ
: Block
->succs()) {
187 if (Succ
&& findInternal(Succ
, nullptr, MovedVariable
, TheUseAfterMove
))
195 void UseAfterMoveFinder::getUsesAndReinits(
196 const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
197 llvm::SmallVectorImpl
<const DeclRefExpr
*> *Uses
,
198 llvm::SmallPtrSetImpl
<const Stmt
*> *Reinits
) {
199 llvm::SmallPtrSet
<const DeclRefExpr
*, 1> DeclRefs
;
200 llvm::SmallPtrSet
<const DeclRefExpr
*, 1> ReinitDeclRefs
;
202 getDeclRefs(Block
, MovedVariable
, &DeclRefs
);
203 getReinits(Block
, MovedVariable
, Reinits
, &ReinitDeclRefs
);
205 // All references to the variable that aren't reinitializations are uses.
207 for (const DeclRefExpr
*DeclRef
: DeclRefs
) {
208 if (!ReinitDeclRefs
.count(DeclRef
))
209 Uses
->push_back(DeclRef
);
212 // Sort the uses by their occurrence in the source code.
213 llvm::sort(*Uses
, [](const DeclRefExpr
*D1
, const DeclRefExpr
*D2
) {
214 return D1
->getExprLoc() < D2
->getExprLoc();
218 bool isStandardSmartPointer(const ValueDecl
*VD
) {
219 const Type
*TheType
= VD
->getType().getNonReferenceType().getTypePtrOrNull();
223 const CXXRecordDecl
*RecordDecl
= TheType
->getAsCXXRecordDecl();
227 const IdentifierInfo
*ID
= RecordDecl
->getIdentifier();
231 StringRef Name
= ID
->getName();
232 if (Name
!= "unique_ptr" && Name
!= "shared_ptr" && Name
!= "weak_ptr")
235 return RecordDecl
->getDeclContext()->isStdNamespace();
238 void UseAfterMoveFinder::getDeclRefs(
239 const CFGBlock
*Block
, const Decl
*MovedVariable
,
240 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
) {
242 for (const auto &Elem
: *Block
) {
243 std::optional
<CFGStmt
> S
= Elem
.getAs
<CFGStmt
>();
247 auto AddDeclRefs
= [this, Block
,
248 DeclRefs
](const ArrayRef
<BoundNodes
> Matches
) {
249 for (const auto &Match
: Matches
) {
250 const auto *DeclRef
= Match
.getNodeAs
<DeclRefExpr
>("declref");
251 const auto *Operator
= Match
.getNodeAs
<CXXOperatorCallExpr
>("operator");
252 if (DeclRef
&& BlockMap
->blockContainingStmt(DeclRef
) == Block
) {
253 // Ignore uses of a standard smart pointer that don't dereference the
255 if (Operator
|| !isStandardSmartPointer(DeclRef
->getDecl())) {
256 DeclRefs
->insert(DeclRef
);
262 auto DeclRefMatcher
= declRefExpr(hasDeclaration(equalsNode(MovedVariable
)),
263 unless(inDecltypeOrTemplateArg()))
266 AddDeclRefs(match(traverse(TK_AsIs
, findAll(DeclRefMatcher
)), *S
->getStmt(),
268 AddDeclRefs(match(findAll(cxxOperatorCallExpr(
269 hasAnyOverloadedOperatorName("*", "->", "[]"),
270 hasArgument(0, DeclRefMatcher
))
272 *S
->getStmt(), *Context
));
276 void UseAfterMoveFinder::getReinits(
277 const CFGBlock
*Block
, const ValueDecl
*MovedVariable
,
278 llvm::SmallPtrSetImpl
<const Stmt
*> *Stmts
,
279 llvm::SmallPtrSetImpl
<const DeclRefExpr
*> *DeclRefs
) {
280 auto DeclRefMatcher
=
281 declRefExpr(hasDeclaration(equalsNode(MovedVariable
))).bind("declref");
283 auto StandardContainerTypeMatcher
= hasType(hasUnqualifiedDesugaredType(
284 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
285 "::std::basic_string", "::std::vector", "::std::deque",
286 "::std::forward_list", "::std::list", "::std::set", "::std::map",
287 "::std::multiset", "::std::multimap", "::std::unordered_set",
288 "::std::unordered_map", "::std::unordered_multiset",
289 "::std::unordered_multimap"))))));
291 auto StandardSmartPointerTypeMatcher
= hasType(hasUnqualifiedDesugaredType(
292 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
293 "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr"))))));
295 // Matches different types of reinitialization.
298 // Assignment. In addition to the overloaded assignment operator,
299 // test for built-in assignment as well, since template functions
300 // may be instantiated to use std::move() on built-in types.
301 binaryOperation(hasOperatorName("="), hasLHS(DeclRefMatcher
)),
302 // Declaration. We treat this as a type of reinitialization too,
303 // so we don't need to treat it separately.
304 declStmt(hasDescendant(equalsNode(MovedVariable
))),
305 // clear() and assign() on standard containers.
307 on(expr(DeclRefMatcher
, StandardContainerTypeMatcher
)),
308 // To keep the matcher simple, we check for assign() calls
309 // on all standard containers, even though only vector,
310 // deque, forward_list and list have assign(). If assign()
311 // is called on any of the other containers, this will be
312 // flagged by a compile error anyway.
313 callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
314 // reset() on standard smart pointers.
316 on(expr(DeclRefMatcher
, StandardSmartPointerTypeMatcher
)),
317 callee(cxxMethodDecl(hasName("reset")))),
318 // Methods that have the [[clang::reinitializes]] attribute.
321 callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes
)))),
322 // Passing variable to a function as a non-const pointer.
323 callExpr(forEachArgumentWithParam(
324 unaryOperator(hasOperatorName("&"),
325 hasUnaryOperand(DeclRefMatcher
)),
326 unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
327 // Passing variable to a function as a non-const lvalue reference
328 // (unless that function is std::move()).
329 callExpr(forEachArgumentWithParam(
330 traverse(TK_AsIs
, DeclRefMatcher
),
331 unless(parmVarDecl(hasType(
332 references(qualType(isConstQualified())))))),
333 unless(callee(functionDecl(hasName("::std::move")))))))
338 for (const auto &Elem
: *Block
) {
339 std::optional
<CFGStmt
> S
= Elem
.getAs
<CFGStmt
>();
343 SmallVector
<BoundNodes
, 1> Matches
=
344 match(findAll(ReinitMatcher
), *S
->getStmt(), *Context
);
346 for (const auto &Match
: Matches
) {
347 const auto *TheStmt
= Match
.getNodeAs
<Stmt
>("reinit");
348 const auto *TheDeclRef
= Match
.getNodeAs
<DeclRefExpr
>("declref");
349 if (TheStmt
&& BlockMap
->blockContainingStmt(TheStmt
) == Block
) {
350 Stmts
->insert(TheStmt
);
352 // We count DeclStmts as reinitializations, but they don't have a
353 // DeclRefExpr associated with them -- so we need to check 'TheDeclRef'
354 // before adding it to the set.
356 DeclRefs
->insert(TheDeclRef
);
362 static void emitDiagnostic(const Expr
*MovingCall
, const DeclRefExpr
*MoveArg
,
363 const UseAfterMove
&Use
, ClangTidyCheck
*Check
,
364 ASTContext
*Context
) {
365 SourceLocation UseLoc
= Use
.DeclRef
->getExprLoc();
366 SourceLocation MoveLoc
= MovingCall
->getExprLoc();
368 Check
->diag(UseLoc
, "'%0' used after it was moved")
369 << MoveArg
->getDecl()->getName();
370 Check
->diag(MoveLoc
, "move occurred here", DiagnosticIDs::Note
);
371 if (Use
.EvaluationOrderUndefined
) {
373 "the use and move are unsequenced, i.e. there is no guarantee "
374 "about the order in which they are evaluated",
375 DiagnosticIDs::Note
);
376 } else if (UseLoc
< MoveLoc
|| Use
.DeclRef
== MoveArg
) {
378 "the use happens in a later loop iteration than the move",
379 DiagnosticIDs::Note
);
383 void UseAfterMoveCheck::registerMatchers(MatchFinder
*Finder
) {
384 // try_emplace is a common maybe-moving function that returns a
385 // bool to tell callers whether it moved. Ignore std::move inside
386 // try_emplace to avoid false positives as we don't track uses of
388 auto TryEmplaceMatcher
=
389 cxxMemberCallExpr(callee(cxxMethodDecl(hasName("try_emplace"))));
390 auto CallMoveMatcher
=
391 callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))),
392 hasArgument(0, declRefExpr().bind("arg")),
393 unless(inDecltypeOrTemplateArg()),
394 unless(hasParent(TryEmplaceMatcher
)), expr().bind("call-move"),
395 anyOf(hasAncestor(compoundStmt(
396 hasParent(lambdaExpr().bind("containing-lambda")))),
397 hasAncestor(functionDecl(anyOf(
399 hasAnyConstructorInitializer(withInitializer(
400 expr(anyOf(equalsBoundNode("call-move"),
402 equalsBoundNode("call-move")))))
403 .bind("containing-ctor-init"))))
404 .bind("containing-ctor"),
405 functionDecl().bind("containing-func"))))));
410 // To find the Stmt that we assume performs the actual move, we look
411 // for the direct ancestor of the std::move() that isn't one of the
412 // node types ignored by ignoringParenImpCasts().
414 forEach(expr(ignoringParenImpCasts(CallMoveMatcher
))),
415 // Don't allow an InitListExpr to be the moving call. An
416 // InitListExpr has both a syntactic and a semantic form, and the
417 // parent-child relationships are different between the two. This
418 // could cause an InitListExpr to be analyzed as the moving call
419 // in addition to the Expr that we actually want, resulting in two
420 // diagnostics with different code locations for the same move.
421 unless(initListExpr()),
422 unless(expr(ignoringParenImpCasts(equalsBoundNode("call-move")))))
423 .bind("moving-call")),
427 void UseAfterMoveCheck::check(const MatchFinder::MatchResult
&Result
) {
428 const auto *ContainingCtor
=
429 Result
.Nodes
.getNodeAs
<CXXConstructorDecl
>("containing-ctor");
430 const auto *ContainingCtorInit
=
431 Result
.Nodes
.getNodeAs
<Expr
>("containing-ctor-init");
432 const auto *ContainingLambda
=
433 Result
.Nodes
.getNodeAs
<LambdaExpr
>("containing-lambda");
434 const auto *ContainingFunc
=
435 Result
.Nodes
.getNodeAs
<FunctionDecl
>("containing-func");
436 const auto *CallMove
= Result
.Nodes
.getNodeAs
<CallExpr
>("call-move");
437 const auto *MovingCall
= Result
.Nodes
.getNodeAs
<Expr
>("moving-call");
438 const auto *Arg
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("arg");
440 if (!MovingCall
|| !MovingCall
->getExprLoc().isValid())
441 MovingCall
= CallMove
;
443 // Ignore the std::move if the variable that was passed to it isn't a local
445 if (!Arg
->getDecl()->getDeclContext()->isFunctionOrMethod())
448 // Collect all code blocks that could use the arg after move.
449 llvm::SmallVector
<Stmt
*> CodeBlocks
{};
450 if (ContainingCtor
) {
451 CodeBlocks
.push_back(ContainingCtor
->getBody());
452 if (ContainingCtorInit
) {
453 // Collect the constructor initializer expressions.
454 bool BeforeMove
{true};
455 for (CXXCtorInitializer
*Init
: ContainingCtor
->inits()) {
456 if (BeforeMove
&& Init
->getInit()->IgnoreImplicit() ==
457 ContainingCtorInit
->IgnoreImplicit())
460 CodeBlocks
.push_back(Init
->getInit());
463 } else if (ContainingLambda
) {
464 CodeBlocks
.push_back(ContainingLambda
->getBody());
465 } else if (ContainingFunc
) {
466 CodeBlocks
.push_back(ContainingFunc
->getBody());
469 for (Stmt
*CodeBlock
: CodeBlocks
) {
470 UseAfterMoveFinder
Finder(Result
.Context
);
472 if (Finder
.find(CodeBlock
, MovingCall
, Arg
->getDecl(), &Use
))
473 emitDiagnostic(MovingCall
, Arg
, Use
, this, Result
.Context
);
477 } // namespace clang::tidy::bugprone