xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp (revision 0b344b4feff5cd04d63db7b914d783fd941fbda0)
1 //===--- UseAfterMoveCheck.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 "UseAfterMoveCheck.h"
10 
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"
19 
20 #include "../utils/ExprSequence.h"
21 #include "../utils/Matchers.h"
22 #include <optional>
23 
24 using namespace clang::ast_matchers;
25 using namespace clang::tidy::utils;
26 
27 namespace clang::tidy::bugprone {
28 
29 using matchers::hasUnevaluatedContext;
30 
31 namespace {
32 
33 /// Contains information about a use-after-move.
34 struct UseAfterMove {
35   // The DeclRefExpr that constituted the use of the object.
36   const DeclRefExpr *DeclRef;
37 
38   // Is the order in which the move and the use are evaluated undefined?
39   bool EvaluationOrderUndefined = false;
40 
41   // Does the use happen in a later loop iteration than the move?
42   //
43   // We default to false and change it to true if required in find().
44   bool UseHappensInLaterLoopIteration = false;
45 };
46 
47 /// Finds uses of a variable after a move (and maintains state required by the
48 /// various internal helper functions).
49 class UseAfterMoveFinder {
50 public:
51   UseAfterMoveFinder(ASTContext *TheContext);
52 
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);
59 
60 private:
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);
72 
73   ASTContext *Context;
74   std::unique_ptr<ExprSequence> Sequence;
75   std::unique_ptr<StmtToBlockMap> BlockMap;
76   llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
77 };
78 
79 } // namespace
80 
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
87 // move.
88 static StatementMatcher inDecltypeOrTemplateArg() {
89   return anyOf(hasAncestor(typeLoc()),
90                hasAncestor(declRefExpr(
91                    to(functionDecl(ast_matchers::isTemplateInstantiation())))),
92                hasAncestor(expr(hasUnevaluatedContext())));
93 }
94 
95 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
96     : Context(TheContext) {}
97 
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
103   // lambda.
104   //
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);
113   if (!TheCFG)
114     return std::nullopt;
115 
116   Sequence = std::make_unique<ExprSequence>(TheCFG.get(), CodeBlock, Context);
117   BlockMap = std::make_unique<StmtToBlockMap>(TheCFG.get(), Context);
118   Visited.clear();
119 
120   const CFGBlock *MoveBlock = BlockMap->blockContainingStmt(MovingCall);
121   if (!MoveBlock) {
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
124     // body.
125     MoveBlock = &TheCFG->getEntry();
126   }
127 
128   auto TheUseAfterMove =
129       findInternal(MoveBlock, MovingCall, MovedVariable->getDecl());
130 
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);
143     }
144   }
145   return TheUseAfterMove;
146 }
147 
148 std::optional<UseAfterMove>
149 UseAfterMoveFinder::findInternal(const CFGBlock *Block, const Expr *MovingCall,
150                                  const ValueDecl *MovedVariable) {
151   if (Visited.count(Block))
152     return std::nullopt;
153 
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).
156   if (!MovingCall)
157     Visited.insert(Block);
158 
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);
163 
164   // Ignore all reinitializations where the move potentially comes after the
165   // reinit.
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);
173   }
174   for (const Stmt *Reinit : ReinitsToDelete) {
175     Reinits.erase(Reinit);
176   }
177 
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
183       // after the use.
184       bool HaveSavingReinit = false;
185       for (const Stmt *Reinit : Reinits) {
186         if (!Sequence->potentiallyAfter(Reinit, Use))
187           HaveSavingReinit = true;
188       }
189 
190       if (!HaveSavingReinit) {
191         UseAfterMove TheUseAfterMove;
192         TheUseAfterMove.DeclRef = Use;
193 
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);
201 
202         return TheUseAfterMove;
203       }
204     }
205   }
206 
207   // If the object wasn't reinitialized, call ourselves recursively on all
208   // successors.
209   if (Reinits.empty()) {
210     for (const auto &Succ : Block->succs()) {
211       if (Succ) {
212         if (auto Found = findInternal(Succ, nullptr, MovedVariable)) {
213           return Found;
214         }
215       }
216     }
217   }
218 
219   return std::nullopt;
220 }
221 
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;
228 
229   getDeclRefs(Block, MovedVariable, &DeclRefs);
230   getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
231 
232   // All references to the variable that aren't reinitializations are uses.
233   Uses->clear();
234   for (const DeclRefExpr *DeclRef : DeclRefs) {
235     if (!ReinitDeclRefs.count(DeclRef))
236       Uses->push_back(DeclRef);
237   }
238 
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();
242   });
243 }
244 
245 bool isStandardSmartPointer(const ValueDecl *VD) {
246   const Type *TheType = VD->getType().getNonReferenceType().getTypePtrOrNull();
247   if (!TheType)
248     return false;
249 
250   const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
251   if (!RecordDecl)
252     return false;
253 
254   const IdentifierInfo *ID = RecordDecl->getIdentifier();
255   if (!ID)
256     return false;
257 
258   StringRef Name = ID->getName();
259   if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
260     return false;
261 
262   return RecordDecl->getDeclContext()->isStdNamespace();
263 }
264 
265 void UseAfterMoveFinder::getDeclRefs(
266     const CFGBlock *Block, const Decl *MovedVariable,
267     llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
268   DeclRefs->clear();
269   for (const auto &Elem : *Block) {
270     std::optional<CFGStmt> S = Elem.getAs<CFGStmt>();
271     if (!S)
272       continue;
273 
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
281           // pointer.
282           if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
283             DeclRefs->insert(DeclRef);
284           }
285         }
286       }
287     };
288 
289     auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
290                                       unless(inDecltypeOrTemplateArg()))
291                               .bind("declref");
292 
293     AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(),
294                       *Context));
295     AddDeclRefs(match(findAll(cxxOperatorCallExpr(
296                                   hasAnyOverloadedOperatorName("*", "->", "[]"),
297                                   hasArgument(0, DeclRefMatcher))
298                                   .bind("operator")),
299                       *S->getStmt(), *Context));
300   }
301 }
302 
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");
309 
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"))))));
317 
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"))))));
322 
323   // Matches different types of reinitialization.
324   auto ReinitMatcher =
325       stmt(anyOf(
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.
334                cxxMemberCallExpr(
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.
343                cxxMemberCallExpr(
344                    on(expr(DeclRefMatcher, StandardResettableOwnerTypeMatcher)),
345                    callee(cxxMethodDecl(hasName("reset")))),
346                // Methods that have the [[clang::reinitializes]] attribute.
347                cxxMemberCallExpr(
348                    on(DeclRefMatcher),
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")))))))
363           .bind("reinit");
364 
365   Stmts->clear();
366   DeclRefs->clear();
367   for (const auto &Elem : *Block) {
368     std::optional<CFGStmt> S = Elem.getAs<CFGStmt>();
369     if (!S)
370       continue;
371 
372     SmallVector<BoundNodes, 1> Matches =
373         match(findAll(ReinitMatcher), *S->getStmt(), *Context);
374 
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);
380 
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.
384         if (TheDeclRef)
385           DeclRefs->insert(TheDeclRef);
386       }
387     }
388   }
389 }
390 
391 enum class MoveType {
392   Move,    // std::move
393   Forward, // std::forward
394 };
395 
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;
401 
402   llvm_unreachable("Invalid move type");
403 }
404 
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();
410 
411   const bool IsMove = (Type == MoveType::Move);
412 
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",
416               DiagnosticIDs::Note)
417       << IsMove;
418   if (Use.EvaluationOrderUndefined) {
419     Check->diag(
420         UseLoc,
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",
423         DiagnosticIDs::Note)
424         << IsMove;
425   } else if (Use.UseHappensInLaterLoopIteration) {
426     Check->diag(UseLoc,
427                 "the use happens in a later loop iteration than the "
428                 "%select{forward|move}0",
429                 DiagnosticIDs::Note)
430         << IsMove;
431   }
432 }
433 
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
438   // the bool.
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"))
444                           .bind("move-decl")),
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(
451                          cxxConstructorDecl(
452                              hasAnyConstructorInitializer(withInitializer(
453                                  expr(anyOf(equalsBoundNode("call-move"),
454                                             hasDescendant(expr(
455                                                 equalsBoundNode("call-move")))))
456                                      .bind("containing-ctor-init"))))
457                              .bind("containing-ctor"),
458                          functionDecl().bind("containing-func"))))));
459 
460   Finder->addMatcher(
461       traverse(
462           TK_AsIs,
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().
466           stmt(
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")),
477       this);
478 }
479 
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");
493 
494   if (!MovingCall || !MovingCall->getExprLoc().isValid())
495     MovingCall = CallMove;
496 
497   // Ignore the std::move if the variable that was passed to it isn't a local
498   // variable.
499   if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
500     return;
501 
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())
512           BeforeMove = false;
513         if (!BeforeMove)
514           CodeBlocks.push_back(Init->getInit());
515       }
516     }
517   } else if (ContainingLambda) {
518     CodeBlocks.push_back(ContainingLambda->getBody());
519   } else if (ContainingFunc) {
520     CodeBlocks.push_back(ContainingFunc->getBody());
521   }
522 
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));
528   }
529 }
530 
531 } // namespace clang::tidy::bugprone
532