11 #include "clang/AST/Expr.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/AST/ExprConcepts.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Analysis/CFG.h"
16 #include "clang/Lex/Lexer.h"
18 #include "../utils/ExprSequence.h"
31 if (isa<CXXNoexceptExpr>(Node) || isa<RequiresExpr>(Node))
33 if (
const auto *UnaryExpr = dyn_cast<UnaryExprOrTypeTraitExpr>(&Node)) {
34 switch (UnaryExpr->getKind()) {
42 if (
const auto *TypeIDExpr = dyn_cast<CXXTypeidExpr>(&Node))
43 return !TypeIDExpr->isPotentiallyEvaluated();
58 class UseAfterMoveFinder {
60 UseAfterMoveFinder(ASTContext *TheContext);
66 bool find(Stmt *FunctionBody,
const Expr *MovingCall,
67 const ValueDecl *MovedVariable, UseAfterMove *TheUseAfterMove);
70 bool findInternal(
const CFGBlock *Block,
const Expr *MovingCall,
71 const ValueDecl *MovedVariable,
72 UseAfterMove *TheUseAfterMove);
73 void getUsesAndReinits(
const CFGBlock *Block,
const ValueDecl *MovedVariable,
75 llvm::SmallPtrSetImpl<const Stmt *> *Reinits);
76 void getDeclRefs(
const CFGBlock *Block,
const Decl *MovedVariable,
77 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
78 void getReinits(
const CFGBlock *Block,
const ValueDecl *MovedVariable,
79 llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
80 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs);
83 std::unique_ptr<ExprSequence> Sequence;
84 std::unique_ptr<StmtToBlockMap> BlockMap;
85 llvm::SmallPtrSet<const CFGBlock *, 8> Visited;
99 return anyOf(hasAncestor(typeLoc()),
100 hasAncestor(declRefExpr(
101 to(functionDecl(ast_matchers::isTemplateInstantiation())))),
102 hasAncestor(expr(hasUnevaluatedContext())));
105 UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext)
106 : Context(TheContext) {}
108 bool UseAfterMoveFinder::find(Stmt *FunctionBody,
const Expr *MovingCall,
109 const ValueDecl *MovedVariable,
110 UseAfterMove *TheUseAfterMove) {
118 CFG::BuildOptions Options;
119 Options.AddImplicitDtors =
true;
120 Options.AddTemporaryDtors =
true;
121 std::unique_ptr<CFG> TheCFG =
122 CFG::buildCFG(
nullptr, FunctionBody, Context, Options);
127 std::make_unique<ExprSequence>(TheCFG.get(), FunctionBody, Context);
128 BlockMap = std::make_unique<StmtToBlockMap>(TheCFG.get(), Context);
131 const CFGBlock *Block = BlockMap->blockContainingStmt(MovingCall);
136 Block = &TheCFG->getEntry();
139 return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
142 bool UseAfterMoveFinder::findInternal(
const CFGBlock *Block,
143 const Expr *MovingCall,
144 const ValueDecl *MovedVariable,
145 UseAfterMove *TheUseAfterMove) {
146 if (Visited.count(Block))
152 Visited.insert(Block);
155 llvm::SmallVector<const DeclRefExpr *, 1> Uses;
156 llvm::SmallPtrSet<const Stmt *, 1> Reinits;
157 getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);
163 llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;
164 for (
const Stmt *Reinit : Reinits) {
165 if (MovingCall && Reinit != MovingCall &&
166 Sequence->potentiallyAfter(MovingCall, Reinit))
167 ReinitsToDelete.push_back(Reinit);
169 for (
const Stmt *Reinit : ReinitsToDelete) {
170 Reinits.erase(Reinit);
174 for (
const DeclRefExpr *Use : Uses) {
175 if (!MovingCall || Sequence->potentiallyAfter(Use, MovingCall)) {
179 bool HaveSavingReinit =
false;
180 for (
const Stmt *Reinit : Reinits) {
181 if (!Sequence->potentiallyAfter(Reinit, Use))
182 HaveSavingReinit =
true;
185 if (!HaveSavingReinit) {
186 TheUseAfterMove->DeclRef = Use;
192 TheUseAfterMove->EvaluationOrderUndefined =
193 MovingCall !=
nullptr &&
194 Sequence->potentiallyAfter(MovingCall, Use);
203 if (Reinits.empty()) {
204 for (
const auto &Succ : Block->succs()) {
205 if (Succ && findInternal(Succ,
nullptr, MovedVariable, TheUseAfterMove))
213 void UseAfterMoveFinder::getUsesAndReinits(
214 const CFGBlock *Block,
const ValueDecl *MovedVariable,
216 llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {
217 llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;
218 llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;
220 getDeclRefs(Block, MovedVariable, &DeclRefs);
221 getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
225 for (
const DeclRefExpr *
DeclRef : DeclRefs) {
226 if (!ReinitDeclRefs.count(
DeclRef))
231 std::sort(Uses->begin(), Uses->end(),
232 [](
const DeclRefExpr *D1,
const DeclRefExpr *D2) {
233 return D1->getExprLoc() < D2->getExprLoc();
238 const Type *TheType = VD->getType().getNonReferenceType().getTypePtrOrNull();
242 const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
246 const IdentifierInfo *
ID = RecordDecl->getIdentifier();
250 StringRef
Name =
ID->getName();
251 if (
Name !=
"unique_ptr" &&
Name !=
"shared_ptr" &&
Name !=
"weak_ptr")
254 return RecordDecl->getDeclContext()->isStdNamespace();
257 void UseAfterMoveFinder::getDeclRefs(
258 const CFGBlock *Block,
const Decl *MovedVariable,
259 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
261 for (
const auto &Elem : *Block) {
262 Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
266 auto AddDeclRefs = [
this, Block,
267 DeclRefs](
const ArrayRef<BoundNodes> Matches) {
268 for (
const auto &Match : Matches) {
269 const auto *
DeclRef = Match.getNodeAs<DeclRefExpr>(
"declref");
270 const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>(
"operator");
281 auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
285 AddDeclRefs(
match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(),
287 AddDeclRefs(
match(findAll(cxxOperatorCallExpr(
288 hasAnyOverloadedOperatorName(
"*",
"->",
"[]"),
289 hasArgument(0, DeclRefMatcher))
291 *S->getStmt(), *Context));
295 void UseAfterMoveFinder::getReinits(
296 const CFGBlock *Block,
const ValueDecl *MovedVariable,
297 llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
298 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
299 auto DeclRefMatcher =
300 declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind(
"declref");
302 auto StandardContainerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
303 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
304 "::std::basic_string",
"::std::vector",
"::std::deque",
305 "::std::forward_list",
"::std::list",
"::std::set",
"::std::map",
306 "::std::multiset",
"::std::multimap",
"::std::unordered_set",
307 "::std::unordered_map",
"::std::unordered_multiset",
308 "::std::unordered_multimap"))))));
310 auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
311 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
312 "::std::unique_ptr",
"::std::shared_ptr",
"::std::weak_ptr"))))));
320 binaryOperation(hasOperatorName(
"="), hasLHS(DeclRefMatcher)),
323 declStmt(hasDescendant(equalsNode(MovedVariable))),
326 on(expr(DeclRefMatcher, StandardContainerTypeMatcher)),
332 callee(cxxMethodDecl(hasAnyName(
"clear",
"assign")))),
335 on(expr(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
336 callee(cxxMethodDecl(hasName(
"reset")))),
340 callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes)))),
342 callExpr(forEachArgumentWithParam(
343 unaryOperator(hasOperatorName(
"&"),
344 hasUnaryOperand(DeclRefMatcher)),
345 unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
348 callExpr(forEachArgumentWithParam(
349 traverse(TK_AsIs, DeclRefMatcher),
350 unless(parmVarDecl(hasType(
351 references(qualType(isConstQualified())))))),
352 unless(callee(functionDecl(hasName(
"::std::move")))))))
357 for (
const auto &Elem : *Block) {
358 Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
362 SmallVector<BoundNodes, 1> Matches =
363 match(findAll(ReinitMatcher), *S->getStmt(), *Context);
365 for (
const auto &Match : Matches) {
366 const auto *TheStmt = Match.getNodeAs<Stmt>(
"reinit");
367 const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>(
"declref");
368 if (TheStmt && BlockMap->blockContainingStmt(TheStmt) == Block) {
369 Stmts->insert(TheStmt);
375 DeclRefs->insert(TheDeclRef);
383 ASTContext *Context) {
384 SourceLocation UseLoc = Use.DeclRef->getExprLoc();
385 SourceLocation MoveLoc = MovingCall->getExprLoc();
387 Check->
diag(UseLoc,
"'%0' used after it was moved")
388 << MoveArg->getDecl()->getName();
389 Check->
diag(MoveLoc,
"move occurred here", DiagnosticIDs::Note);
390 if (Use.EvaluationOrderUndefined) {
392 "the use and move are unsequenced, i.e. there is no guarantee "
393 "about the order in which they are evaluated",
394 DiagnosticIDs::Note);
395 }
else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
397 "the use happens in a later loop iteration than the move",
398 DiagnosticIDs::Note);
402 void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
403 auto CallMoveMatcher =
404 callExpr(callee(functionDecl(hasName(
"::std::move"))), argumentCountIs(1),
405 hasArgument(0, declRefExpr().bind(
"arg")),
406 anyOf(hasAncestor(compoundStmt(
407 hasParent(lambdaExpr().bind(
"containing-lambda")))),
408 hasAncestor(functionDecl().bind(
"containing-func"))),
414 unless(hasParent(cxxMemberCallExpr(
415 callee(cxxMethodDecl(hasName(
"try_emplace")))))))
425 forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
432 unless(initListExpr()),
433 unless(expr(ignoringParenImpCasts(equalsBoundNode(
"call-move")))))
434 .bind(
"moving-call")),
439 const auto *ContainingLambda =
440 Result.Nodes.getNodeAs<LambdaExpr>(
"containing-lambda");
441 const auto *ContainingFunc =
442 Result.Nodes.getNodeAs<FunctionDecl>(
"containing-func");
443 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>(
"call-move");
444 const auto *MovingCall = Result.Nodes.getNodeAs<Expr>(
"moving-call");
445 const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>(
"arg");
447 if (!MovingCall || !MovingCall->getExprLoc().isValid())
448 MovingCall = CallMove;
450 Stmt *FunctionBody =
nullptr;
451 if (ContainingLambda)
452 FunctionBody = ContainingLambda->getBody();
453 else if (ContainingFunc)
454 FunctionBody = ContainingFunc->getBody();
460 if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
463 UseAfterMoveFinder Finder(Result.Context);
465 if (Finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use))