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,
74 llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
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);
135 return findInternal(Block, MovingCall, MovedVariable, TheUseAfterMove);
138 bool UseAfterMoveFinder::findInternal(
const CFGBlock *Block,
139 const Expr *MovingCall,
140 const ValueDecl *MovedVariable,
141 UseAfterMove *TheUseAfterMove) {
142 if (Visited.count(Block))
148 Visited.insert(Block);
151 llvm::SmallVector<const DeclRefExpr *, 1> Uses;
152 llvm::SmallPtrSet<const Stmt *, 1> Reinits;
153 getUsesAndReinits(Block, MovedVariable, &Uses, &Reinits);
157 llvm::SmallVector<const Stmt *, 1> ReinitsToDelete;
158 for (
const Stmt *Reinit : Reinits) {
159 if (MovingCall && Sequence->potentiallyAfter(MovingCall, Reinit))
160 ReinitsToDelete.push_back(Reinit);
162 for (
const Stmt *Reinit : ReinitsToDelete) {
163 Reinits.erase(Reinit);
167 for (
const DeclRefExpr *Use : Uses) {
168 if (!MovingCall || Sequence->potentiallyAfter(Use, MovingCall)) {
172 bool HaveSavingReinit =
false;
173 for (
const Stmt *Reinit : Reinits) {
174 if (!Sequence->potentiallyAfter(Reinit, Use))
175 HaveSavingReinit =
true;
178 if (!HaveSavingReinit) {
179 TheUseAfterMove->DeclRef = Use;
185 TheUseAfterMove->EvaluationOrderUndefined =
186 MovingCall !=
nullptr &&
187 Sequence->potentiallyAfter(MovingCall, Use);
196 if (Reinits.empty()) {
197 for (
const auto &Succ : Block->succs()) {
198 if (Succ && findInternal(Succ,
nullptr, MovedVariable, TheUseAfterMove))
206 void UseAfterMoveFinder::getUsesAndReinits(
207 const CFGBlock *Block,
const ValueDecl *MovedVariable,
208 llvm::SmallVectorImpl<const DeclRefExpr *> *Uses,
209 llvm::SmallPtrSetImpl<const Stmt *> *Reinits) {
210 llvm::SmallPtrSet<const DeclRefExpr *, 1> DeclRefs;
211 llvm::SmallPtrSet<const DeclRefExpr *, 1> ReinitDeclRefs;
213 getDeclRefs(Block, MovedVariable, &DeclRefs);
214 getReinits(Block, MovedVariable, Reinits, &ReinitDeclRefs);
218 for (
const DeclRefExpr *
DeclRef : DeclRefs) {
219 if (!ReinitDeclRefs.count(
DeclRef))
224 std::sort(Uses->begin(), Uses->end(),
225 [](
const DeclRefExpr *D1,
const DeclRefExpr *D2) {
226 return D1->getExprLoc() < D2->getExprLoc();
231 const Type *TheType = VD->getType().getNonReferenceType().getTypePtrOrNull();
235 const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
239 const IdentifierInfo *ID = RecordDecl->getIdentifier();
243 StringRef
Name = ID->getName();
244 if (
Name !=
"unique_ptr" &&
Name !=
"shared_ptr" &&
Name !=
"weak_ptr")
247 return RecordDecl->getDeclContext()->isStdNamespace();
250 void UseAfterMoveFinder::getDeclRefs(
251 const CFGBlock *Block,
const Decl *MovedVariable,
252 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
254 for (
const auto &Elem : *Block) {
255 Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
259 auto addDeclRefs = [
this, Block,
260 DeclRefs](
const ArrayRef<BoundNodes> Matches) {
261 for (
const auto &Match : Matches) {
262 const auto *
DeclRef = Match.getNodeAs<DeclRefExpr>(
"declref");
263 const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>(
"operator");
274 auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
279 match(traverse(ast_type_traits::TK_AsIs, findAll(DeclRefMatcher)),
280 *S->getStmt(), *Context));
281 addDeclRefs(
match(findAll(cxxOperatorCallExpr(
282 hasAnyOverloadedOperatorName(
"*",
"->",
"[]"),
283 hasArgument(0, DeclRefMatcher))
285 *S->getStmt(), *Context));
289 void UseAfterMoveFinder::getReinits(
290 const CFGBlock *Block,
const ValueDecl *MovedVariable,
291 llvm::SmallPtrSetImpl<const Stmt *> *Stmts,
292 llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
293 auto DeclRefMatcher =
294 declRefExpr(hasDeclaration(equalsNode(MovedVariable))).bind(
"declref");
296 auto StandardContainerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
297 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
298 "::std::basic_string",
"::std::vector",
"::std::deque",
299 "::std::forward_list",
"::std::list",
"::std::set",
"::std::map",
300 "::std::multiset",
"::std::multimap",
"::std::unordered_set",
301 "::std::unordered_map",
"::std::unordered_multiset",
302 "::std::unordered_multimap"))))));
304 auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
305 recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
306 "::std::unique_ptr",
"::std::shared_ptr",
"::std::weak_ptr"))))));
314 binaryOperator(hasOperatorName(
"="), hasLHS(DeclRefMatcher)),
315 cxxOperatorCallExpr(hasOverloadedOperatorName(
"="),
316 hasArgument(0, DeclRefMatcher)),
319 declStmt(hasDescendant(equalsNode(MovedVariable))),
322 on(expr(DeclRefMatcher, StandardContainerTypeMatcher)),
328 callee(cxxMethodDecl(hasAnyName(
"clear",
"assign")))),
331 on(expr(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
332 callee(cxxMethodDecl(hasName(
"reset")))),
336 callee(cxxMethodDecl(hasAttr(clang::attr::Reinitializes)))),
338 callExpr(forEachArgumentWithParam(
339 unaryOperator(hasOperatorName(
"&"),
340 hasUnaryOperand(DeclRefMatcher)),
341 unless(parmVarDecl(hasType(pointsTo(isConstQualified())))))),
344 callExpr(forEachArgumentWithParam(
345 traverse(ast_type_traits::TK_AsIs, DeclRefMatcher),
346 unless(parmVarDecl(hasType(
347 references(qualType(isConstQualified())))))),
348 unless(callee(functionDecl(hasName(
"::std::move")))))))
353 for (
const auto &Elem : *Block) {
354 Optional<CFGStmt> S = Elem.getAs<CFGStmt>();
358 SmallVector<BoundNodes, 1> Matches =
359 match(findAll(ReinitMatcher), *S->getStmt(), *Context);
361 for (
const auto &Match : Matches) {
362 const auto *TheStmt = Match.getNodeAs<Stmt>(
"reinit");
363 const auto *TheDeclRef = Match.getNodeAs<DeclRefExpr>(
"declref");
364 if (TheStmt && BlockMap->blockContainingStmt(TheStmt) == Block) {
365 Stmts->insert(TheStmt);
371 DeclRefs->insert(TheDeclRef);
379 ASTContext *Context) {
380 SourceLocation UseLoc = Use.DeclRef->getExprLoc();
381 SourceLocation MoveLoc = MovingCall->getExprLoc();
383 Check->diag(UseLoc,
"'%0' used after it was moved")
384 << MoveArg->getDecl()->getName();
385 Check->diag(MoveLoc,
"move occurred here", DiagnosticIDs::Note);
386 if (Use.EvaluationOrderUndefined) {
388 "the use and move are unsequenced, i.e. there is no guarantee "
389 "about the order in which they are evaluated",
390 DiagnosticIDs::Note);
391 }
else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) {
393 "the use happens in a later loop iteration than the move",
394 DiagnosticIDs::Note);
398 void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
399 auto CallMoveMatcher =
400 callExpr(callee(functionDecl(hasName(
"::std::move"))), argumentCountIs(1),
401 hasArgument(0, declRefExpr().bind(
"arg")),
402 anyOf(hasAncestor(lambdaExpr().bind(
"containing-lambda")),
403 hasAncestor(functionDecl().bind(
"containing-func"))),
409 ast_type_traits::TK_AsIs,
414 forEach(expr(ignoringParenImpCasts(CallMoveMatcher))),
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 *ContainingLambda =
429 Result.Nodes.getNodeAs<LambdaExpr>(
"containing-lambda");
430 const auto *ContainingFunc =
431 Result.Nodes.getNodeAs<FunctionDecl>(
"containing-func");
432 const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>(
"call-move");
433 const auto *MovingCall = Result.Nodes.getNodeAs<Expr>(
"moving-call");
434 const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>(
"arg");
436 if (!MovingCall || !MovingCall->getExprLoc().isValid())
437 MovingCall = CallMove;
439 Stmt *FunctionBody =
nullptr;
440 if (ContainingLambda)
441 FunctionBody = ContainingLambda->getBody();
442 else if (ContainingFunc)
443 FunctionBody = ContainingFunc->getBody();
449 if (!Arg->getDecl()->getDeclContext()->isFunctionOrMethod())
452 UseAfterMoveFinder finder(Result.Context);
454 if (finder.find(FunctionBody, MovingCall, Arg->getDecl(), &Use))