1 // MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===//
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 // This defines checker which checks for potential misuses of a moved-from
10 // object. That means method calls on the object or copying it in moved-from
13 //===----------------------------------------------------------------------===//
15 #include "clang/AST/Attr.h"
16 #include "clang/AST/ExprCXX.h"
17 #include "clang/Driver/DriverDiagnostic.h"
18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 #include "llvm/ADT/StringSet.h"
26 using namespace clang
;
32 enum Kind
{ Moved
, Reported
} K
;
33 RegionState(Kind InK
) : K(InK
) {}
36 bool isReported() const { return K
== Reported
; }
37 bool isMoved() const { return K
== Moved
; }
39 static RegionState
getReported() { return RegionState(Reported
); }
40 static RegionState
getMoved() { return RegionState(Moved
); }
42 bool operator==(const RegionState
&X
) const { return K
== X
.K
; }
43 void Profile(llvm::FoldingSetNodeID
&ID
) const { ID
.AddInteger(K
); }
45 } // end of anonymous namespace
49 : public Checker
<check::PreCall
, check::PostCall
,
50 check::DeadSymbols
, check::RegionChanges
> {
52 void checkPreCall(const CallEvent
&MC
, CheckerContext
&C
) const;
53 void checkPostCall(const CallEvent
&MC
, CheckerContext
&C
) const;
54 void checkDeadSymbols(SymbolReaper
&SR
, CheckerContext
&C
) const;
56 checkRegionChanges(ProgramStateRef State
,
57 const InvalidatedSymbols
*Invalidated
,
58 ArrayRef
<const MemRegion
*> RequestedRegions
,
59 ArrayRef
<const MemRegion
*> InvalidatedRegions
,
60 const LocationContext
*LCtx
, const CallEvent
*Call
) const;
61 void printState(raw_ostream
&Out
, ProgramStateRef State
,
62 const char *NL
, const char *Sep
) const override
;
65 enum MisuseKind
{ MK_FunCall
, MK_Copy
, MK_Move
, MK_Dereference
};
66 enum StdObjectKind
{ SK_NonStd
, SK_Unsafe
, SK_Safe
, SK_SmartPtr
};
68 enum AggressivenessKind
{ // In any case, don't warn after a reset.
70 AK_KnownsOnly
= 0, // Warn only about known move-unsafe classes.
71 AK_KnownsAndLocals
= 1, // Also warn about all local objects.
72 AK_All
= 2, // Warn on any use-after-move.
76 static bool misuseCausesCrash(MisuseKind MK
) {
77 return MK
== MK_Dereference
;
81 // Is this a local variable or a local rvalue reference?
83 // Is this an STL object? If so, of what kind?
84 StdObjectKind StdKind
;
87 // STL smart pointers are automatically re-initialized to null when moved
88 // from. So we can't warn on many methods, but we can warn when it is
89 // dereferenced, which is UB even if the resulting lvalue never gets read.
90 const llvm::StringSet
<> StdSmartPtrClasses
= {
96 // Not all of these are entirely move-safe, but they do provide *some*
97 // guarantees, and it means that somebody is using them after move
99 // TODO: We can still try to identify *unsafe* use after move,
100 // like we did with smart pointers.
101 const llvm::StringSet
<> StdSafeClasses
= {
114 // Should we bother tracking the state of the object?
115 bool shouldBeTracked(ObjectKind OK
) const {
116 // In non-aggressive mode, only warn on use-after-move of local variables
117 // (or local rvalue references) and of STL objects. The former is possible
118 // because local variables (or local rvalue references) are not tempting
119 // their user to re-use the storage. The latter is possible because STL
120 // objects are known to end up in a valid but unspecified state after the
121 // move and their state-reset methods are also known, which allows us to
122 // predict precisely when use-after-move is invalid.
123 // Some STL objects are known to conform to additional contracts after move,
124 // so they are not tracked. However, smart pointers specifically are tracked
125 // because we can perform extra checking over them.
126 // In aggressive mode, warn on any use-after-move because the user has
127 // intentionally asked us to completely eliminate use-after-move
129 return (Aggressiveness
== AK_All
) ||
130 (Aggressiveness
>= AK_KnownsAndLocals
&& OK
.IsLocal
) ||
131 OK
.StdKind
== SK_Unsafe
|| OK
.StdKind
== SK_SmartPtr
;
134 // Some objects only suffer from some kinds of misuses, but we need to track
135 // them anyway because we cannot know in advance what misuse will we find.
136 bool shouldWarnAbout(ObjectKind OK
, MisuseKind MK
) const {
137 // Additionally, only warn on smart pointers when they are dereferenced (or
138 // local or we are aggressive).
139 return shouldBeTracked(OK
) &&
140 ((Aggressiveness
== AK_All
) ||
141 (Aggressiveness
>= AK_KnownsAndLocals
&& OK
.IsLocal
) ||
142 OK
.StdKind
!= SK_SmartPtr
|| MK
== MK_Dereference
);
145 // Obtains ObjectKind of an object. Because class declaration cannot always
146 // be easily obtained from the memory region, it is supplied separately.
147 ObjectKind
classifyObject(const MemRegion
*MR
, const CXXRecordDecl
*RD
) const;
149 // Classifies the object and dumps a user-friendly description string to
151 void explainObject(llvm::raw_ostream
&OS
, const MemRegion
*MR
,
152 const CXXRecordDecl
*RD
, MisuseKind MK
) const;
154 bool belongsTo(const CXXRecordDecl
*RD
, const llvm::StringSet
<> &Set
) const;
156 class MovedBugVisitor
: public BugReporterVisitor
{
158 MovedBugVisitor(const MoveChecker
&Chk
, const MemRegion
*R
,
159 const CXXRecordDecl
*RD
, MisuseKind MK
)
160 : Chk(Chk
), Region(R
), RD(RD
), MK(MK
), Found(false) {}
162 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
165 ID
.AddPointer(Region
);
166 // Don't add RD because it's, in theory, uniquely determined by
167 // the region. In practice though, it's not always possible to obtain
168 // the declaration directly from the region, that's why we store it
169 // in the first place.
172 PathDiagnosticPieceRef
VisitNode(const ExplodedNode
*N
,
173 BugReporterContext
&BRC
,
174 PathSensitiveBugReport
&BR
) override
;
177 const MoveChecker
&Chk
;
178 // The tracked region.
179 const MemRegion
*Region
;
180 // The class of the tracked object.
181 const CXXRecordDecl
*RD
;
182 // How exactly the object was misused.
187 AggressivenessKind Aggressiveness
= AK_KnownsAndLocals
;
190 void setAggressiveness(StringRef Str
, CheckerManager
&Mgr
) {
192 llvm::StringSwitch
<AggressivenessKind
>(Str
)
193 .Case("KnownsOnly", AK_KnownsOnly
)
194 .Case("KnownsAndLocals", AK_KnownsAndLocals
)
196 .Default(AK_Invalid
);
198 if (Aggressiveness
== AK_Invalid
)
199 Mgr
.reportInvalidCheckerOptionValue(this, "WarnOn",
200 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
204 BugType BT
{this, "Use-after-move", categories::CXXMoveSemantics
};
206 // Check if the given form of potential misuse of a given object
207 // should be reported. If so, get it reported. The callback from which
208 // this function was called should immediately return after the call
209 // because this function adds one or two transitions.
210 void modelUse(ProgramStateRef State
, const MemRegion
*Region
,
211 const CXXRecordDecl
*RD
, MisuseKind MK
,
212 CheckerContext
&C
) const;
214 // Returns the exploded node against which the report was emitted.
215 // The caller *must* add any further transitions against this node.
216 // Returns nullptr and does not report if such node already exists.
217 ExplodedNode
*tryToReportBug(const MemRegion
*Region
, const CXXRecordDecl
*RD
,
218 CheckerContext
&C
, MisuseKind MK
) const;
220 bool isInMoveSafeContext(const LocationContext
*LC
) const;
221 bool isStateResetMethod(const CXXMethodDecl
*MethodDec
) const;
222 bool isMoveSafeMethod(const CXXMethodDecl
*MethodDec
) const;
223 const ExplodedNode
*getMoveLocation(const ExplodedNode
*N
,
224 const MemRegion
*Region
,
225 CheckerContext
&C
) const;
227 } // end anonymous namespace
229 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap
, const MemRegion
*, RegionState
)
231 // Define the inter-checker API.
235 bool isMovedFrom(ProgramStateRef State
, const MemRegion
*Region
) {
236 const RegionState
*RS
= State
->get
<TrackedRegionMap
>(Region
);
237 return RS
&& (RS
->isMoved() || RS
->isReported());
243 // If a region is removed all of the subregions needs to be removed too.
244 static ProgramStateRef
removeFromState(ProgramStateRef State
,
245 const MemRegion
*Region
) {
248 for (auto &E
: State
->get
<TrackedRegionMap
>()) {
249 if (E
.first
->isSubRegionOf(Region
))
250 State
= State
->remove
<TrackedRegionMap
>(E
.first
);
255 static bool isAnyBaseRegionReported(ProgramStateRef State
,
256 const MemRegion
*Region
) {
257 for (auto &E
: State
->get
<TrackedRegionMap
>()) {
258 if (Region
->isSubRegionOf(E
.first
) && E
.second
.isReported())
264 static const MemRegion
*unwrapRValueReferenceIndirection(const MemRegion
*MR
) {
265 if (const auto *SR
= dyn_cast_or_null
<SymbolicRegion
>(MR
)) {
266 SymbolRef Sym
= SR
->getSymbol();
267 if (Sym
->getType()->isRValueReferenceType())
268 if (const MemRegion
*OriginMR
= Sym
->getOriginRegion())
274 PathDiagnosticPieceRef
275 MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode
*N
,
276 BugReporterContext
&BRC
,
277 PathSensitiveBugReport
&BR
) {
278 // We need only the last move of the reported object's region.
279 // The visitor walks the ExplodedGraph backwards.
282 ProgramStateRef State
= N
->getState();
283 ProgramStateRef StatePrev
= N
->getFirstPred()->getState();
284 const RegionState
*TrackedObject
= State
->get
<TrackedRegionMap
>(Region
);
285 const RegionState
*TrackedObjectPrev
=
286 StatePrev
->get
<TrackedRegionMap
>(Region
);
289 if (TrackedObjectPrev
&& TrackedObject
)
292 // Retrieve the associated statement.
293 const Stmt
*S
= N
->getStmtForDiagnostics();
298 SmallString
<128> Str
;
299 llvm::raw_svector_ostream
OS(Str
);
301 ObjectKind OK
= Chk
.classifyObject(Region
, RD
);
302 switch (OK
.StdKind
) {
304 if (MK
== MK_Dereference
) {
305 OS
<< "Smart pointer";
306 Chk
.explainObject(OS
, Region
, RD
, MK
);
307 OS
<< " is reset to null when moved from";
311 // If it's not a dereference, we don't care if it was reset to null
312 // or that it is even a smart pointer.
317 Chk
.explainObject(OS
, Region
, RD
, MK
);
322 Chk
.explainObject(OS
, Region
, RD
, MK
);
323 OS
<< " is left in a valid but unspecified state after move";
327 // Generate the extra diagnostic.
328 PathDiagnosticLocation
Pos(S
, BRC
.getSourceManager(),
329 N
->getLocationContext());
330 return std::make_shared
<PathDiagnosticEventPiece
>(Pos
, OS
.str(), true);
333 const ExplodedNode
*MoveChecker::getMoveLocation(const ExplodedNode
*N
,
334 const MemRegion
*Region
,
335 CheckerContext
&C
) const {
336 // Walk the ExplodedGraph backwards and find the first node that referred to
337 // the tracked region.
338 const ExplodedNode
*MoveNode
= N
;
341 ProgramStateRef State
= N
->getState();
342 if (!State
->get
<TrackedRegionMap
>(Region
))
345 N
= N
->pred_empty() ? nullptr : *(N
->pred_begin());
350 void MoveChecker::modelUse(ProgramStateRef State
, const MemRegion
*Region
,
351 const CXXRecordDecl
*RD
, MisuseKind MK
,
352 CheckerContext
&C
) const {
353 assert(!C
.isDifferent() && "No transitions should have been made by now");
354 const RegionState
*RS
= State
->get
<TrackedRegionMap
>(Region
);
355 ObjectKind OK
= classifyObject(Region
, RD
);
357 // Just in case: if it's not a smart pointer but it does have operator *,
358 // we shouldn't call the bug a dereference.
359 if (MK
== MK_Dereference
&& OK
.StdKind
!= SK_SmartPtr
)
362 if (!RS
|| !shouldWarnAbout(OK
, MK
)
363 || isInMoveSafeContext(C
.getLocationContext())) {
364 // Finalize changes made by the caller.
365 C
.addTransition(State
);
369 // Don't report it in case if any base region is already reported.
370 // But still generate a sink in case of UB.
371 // And still finalize changes made by the caller.
372 if (isAnyBaseRegionReported(State
, Region
)) {
373 if (misuseCausesCrash(MK
)) {
374 C
.generateSink(State
, C
.getPredecessor());
376 C
.addTransition(State
);
381 ExplodedNode
*N
= tryToReportBug(Region
, RD
, C
, MK
);
383 // If the program has already crashed on this path, don't bother.
384 if (!N
|| N
->isSink())
387 State
= State
->set
<TrackedRegionMap
>(Region
, RegionState::getReported());
388 C
.addTransition(State
, N
);
391 ExplodedNode
*MoveChecker::tryToReportBug(const MemRegion
*Region
,
392 const CXXRecordDecl
*RD
,
394 MisuseKind MK
) const {
395 if (ExplodedNode
*N
= misuseCausesCrash(MK
) ? C
.generateErrorNode()
396 : C
.generateNonFatalErrorNode()) {
397 // Uniqueing report to the same object.
398 PathDiagnosticLocation LocUsedForUniqueing
;
399 const ExplodedNode
*MoveNode
= getMoveLocation(N
, Region
, C
);
401 if (const Stmt
*MoveStmt
= MoveNode
->getStmtForDiagnostics())
402 LocUsedForUniqueing
= PathDiagnosticLocation::createBegin(
403 MoveStmt
, C
.getSourceManager(), MoveNode
->getLocationContext());
405 // Creating the error message.
406 llvm::SmallString
<128> Str
;
407 llvm::raw_svector_ostream
OS(Str
);
410 OS
<< "Method called on moved-from object";
411 explainObject(OS
, Region
, RD
, MK
);
414 OS
<< "Moved-from object";
415 explainObject(OS
, Region
, RD
, MK
);
419 OS
<< "Moved-from object";
420 explainObject(OS
, Region
, RD
, MK
);
424 OS
<< "Dereference of null smart pointer";
425 explainObject(OS
, Region
, RD
, MK
);
429 auto R
= std::make_unique
<PathSensitiveBugReport
>(
430 BT
, OS
.str(), N
, LocUsedForUniqueing
,
431 MoveNode
->getLocationContext()->getDecl());
432 R
->addVisitor(std::make_unique
<MovedBugVisitor
>(*this, Region
, RD
, MK
));
433 C
.emitReport(std::move(R
));
439 void MoveChecker::checkPostCall(const CallEvent
&Call
,
440 CheckerContext
&C
) const {
441 const auto *AFC
= dyn_cast
<AnyFunctionCall
>(&Call
);
445 ProgramStateRef State
= C
.getState();
446 const auto MethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(AFC
->getDecl());
450 // Check if an object became moved-from.
451 // Object can become moved from after a call to move assignment operator or
452 // move constructor .
453 const auto *ConstructorDecl
= dyn_cast
<CXXConstructorDecl
>(MethodDecl
);
454 if (ConstructorDecl
&& !ConstructorDecl
->isMoveConstructor())
457 if (!ConstructorDecl
&& !MethodDecl
->isMoveAssignmentOperator())
460 const auto ArgRegion
= AFC
->getArgSVal(0).getAsRegion();
464 // Skip moving the object to itself.
465 const auto *CC
= dyn_cast_or_null
<CXXConstructorCall
>(&Call
);
466 if (CC
&& CC
->getCXXThisVal().getAsRegion() == ArgRegion
)
469 if (const auto *IC
= dyn_cast
<CXXInstanceCall
>(AFC
))
470 if (IC
->getCXXThisVal().getAsRegion() == ArgRegion
)
473 const MemRegion
*BaseRegion
= ArgRegion
->getBaseRegion();
474 // Skip temp objects because of their short lifetime.
475 if (BaseRegion
->getAs
<CXXTempObjectRegion
>() ||
476 AFC
->getArgExpr(0)->isPRValue())
478 // If it has already been reported do not need to modify the state.
480 if (State
->get
<TrackedRegionMap
>(ArgRegion
))
483 const CXXRecordDecl
*RD
= MethodDecl
->getParent();
484 ObjectKind OK
= classifyObject(ArgRegion
, RD
);
485 if (shouldBeTracked(OK
)) {
486 // Mark object as moved-from.
487 State
= State
->set
<TrackedRegionMap
>(ArgRegion
, RegionState::getMoved());
488 C
.addTransition(State
);
491 assert(!C
.isDifferent() && "Should not have made transitions on this path!");
494 bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl
*MethodDec
) const {
495 // We abandon the cases where bool/void/void* conversion happens.
496 if (const auto *ConversionDec
=
497 dyn_cast_or_null
<CXXConversionDecl
>(MethodDec
)) {
498 const Type
*Tp
= ConversionDec
->getConversionType().getTypePtrOrNull();
501 if (Tp
->isBooleanType() || Tp
->isVoidType() || Tp
->isVoidPointerType())
504 // Function call `empty` can be skipped.
505 return (MethodDec
&& MethodDec
->getDeclName().isIdentifier() &&
506 (MethodDec
->getName().lower() == "empty" ||
507 MethodDec
->getName().lower() == "isempty"));
510 bool MoveChecker::isStateResetMethod(const CXXMethodDecl
*MethodDec
) const {
513 if (MethodDec
->hasAttr
<ReinitializesAttr
>())
515 if (MethodDec
->getDeclName().isIdentifier()) {
516 std::string MethodName
= MethodDec
->getName().lower();
517 // TODO: Some of these methods (eg., resize) are not always resetting
518 // the state, so we should consider looking at the arguments.
519 if (MethodName
== "assign" || MethodName
== "clear" ||
520 MethodName
== "destroy" || MethodName
== "reset" ||
521 MethodName
== "resize" || MethodName
== "shrink")
527 // Don't report an error inside a move related operation.
528 // We assume that the programmer knows what she does.
529 bool MoveChecker::isInMoveSafeContext(const LocationContext
*LC
) const {
531 const auto *CtxDec
= LC
->getDecl();
532 auto *CtorDec
= dyn_cast_or_null
<CXXConstructorDecl
>(CtxDec
);
533 auto *DtorDec
= dyn_cast_or_null
<CXXDestructorDecl
>(CtxDec
);
534 auto *MethodDec
= dyn_cast_or_null
<CXXMethodDecl
>(CtxDec
);
535 if (DtorDec
|| (CtorDec
&& CtorDec
->isCopyOrMoveConstructor()) ||
536 (MethodDec
&& MethodDec
->isOverloadedOperator() &&
537 MethodDec
->getOverloadedOperator() == OO_Equal
) ||
538 isStateResetMethod(MethodDec
) || isMoveSafeMethod(MethodDec
))
540 } while ((LC
= LC
->getParent()));
544 bool MoveChecker::belongsTo(const CXXRecordDecl
*RD
,
545 const llvm::StringSet
<> &Set
) const {
546 const IdentifierInfo
*II
= RD
->getIdentifier();
547 return II
&& Set
.count(II
->getName());
550 MoveChecker::ObjectKind
551 MoveChecker::classifyObject(const MemRegion
*MR
,
552 const CXXRecordDecl
*RD
) const {
553 // Local variables and local rvalue references are classified as "Local".
554 // For the purposes of this checker, we classify move-safe STL types
555 // as not-"STL" types, because that's how the checker treats them.
556 MR
= unwrapRValueReferenceIndirection(MR
);
558 isa_and_nonnull
<VarRegion
, CXXLifetimeExtendedObjectRegion
>(MR
) &&
559 isa
<StackSpaceRegion
>(MR
->getMemorySpace());
561 if (!RD
|| !RD
->getDeclContext()->isStdNamespace())
562 return { IsLocal
, SK_NonStd
};
564 if (belongsTo(RD
, StdSmartPtrClasses
))
565 return { IsLocal
, SK_SmartPtr
};
567 if (belongsTo(RD
, StdSafeClasses
))
568 return { IsLocal
, SK_Safe
};
570 return { IsLocal
, SK_Unsafe
};
573 void MoveChecker::explainObject(llvm::raw_ostream
&OS
, const MemRegion
*MR
,
574 const CXXRecordDecl
*RD
, MisuseKind MK
) const {
575 // We may need a leading space every time we actually explain anything,
576 // and we never know if we are to explain anything until we try.
578 dyn_cast_or_null
<DeclRegion
>(unwrapRValueReferenceIndirection(MR
))) {
579 const auto *RegionDecl
= cast
<NamedDecl
>(DR
->getDecl());
580 OS
<< " '" << RegionDecl
->getDeclName() << "'";
583 ObjectKind OK
= classifyObject(MR
, RD
);
584 switch (OK
.StdKind
) {
589 if (MK
!= MK_Dereference
)
592 // We only care about the type if it's a dereference.
595 OS
<< " of type '" << RD
->getQualifiedNameAsString() << "'";
600 void MoveChecker::checkPreCall(const CallEvent
&Call
, CheckerContext
&C
) const {
601 ProgramStateRef State
= C
.getState();
603 // Remove the MemRegions from the map on which a ctor/dtor call or assignment
606 // Checking constructor calls.
607 if (const auto *CC
= dyn_cast
<CXXConstructorCall
>(&Call
)) {
608 State
= removeFromState(State
, CC
->getCXXThisVal().getAsRegion());
609 auto CtorDec
= CC
->getDecl();
610 // Check for copying a moved-from object and report the bug.
611 if (CtorDec
&& CtorDec
->isCopyOrMoveConstructor()) {
612 const MemRegion
*ArgRegion
= CC
->getArgSVal(0).getAsRegion();
613 const CXXRecordDecl
*RD
= CtorDec
->getParent();
614 MisuseKind MK
= CtorDec
->isMoveConstructor() ? MK_Move
: MK_Copy
;
615 modelUse(State
, ArgRegion
, RD
, MK
, C
);
620 const auto IC
= dyn_cast
<CXXInstanceCall
>(&Call
);
624 const MemRegion
*ThisRegion
= IC
->getCXXThisVal().getAsRegion();
628 // The remaining part is check only for method call on a moved-from object.
629 const auto MethodDecl
= dyn_cast_or_null
<CXXMethodDecl
>(IC
->getDecl());
633 // Calling a destructor on a moved object is fine.
634 if (isa
<CXXDestructorDecl
>(MethodDecl
))
637 // We want to investigate the whole object, not only sub-object of a parent
638 // class in which the encountered method defined.
639 ThisRegion
= ThisRegion
->getMostDerivedObjectRegion();
641 if (isStateResetMethod(MethodDecl
)) {
642 State
= removeFromState(State
, ThisRegion
);
643 C
.addTransition(State
);
647 if (isMoveSafeMethod(MethodDecl
))
650 // Store class declaration as well, for bug reporting purposes.
651 const CXXRecordDecl
*RD
= MethodDecl
->getParent();
653 if (MethodDecl
->isOverloadedOperator()) {
654 OverloadedOperatorKind OOK
= MethodDecl
->getOverloadedOperator();
656 if (OOK
== OO_Equal
) {
657 // Remove the tracked object for every assignment operator, but report bug
658 // only for move or copy assignment's argument.
659 State
= removeFromState(State
, ThisRegion
);
661 if (MethodDecl
->isCopyAssignmentOperator() ||
662 MethodDecl
->isMoveAssignmentOperator()) {
663 const MemRegion
*ArgRegion
= IC
->getArgSVal(0).getAsRegion();
665 MethodDecl
->isMoveAssignmentOperator() ? MK_Move
: MK_Copy
;
666 modelUse(State
, ArgRegion
, RD
, MK
, C
);
669 C
.addTransition(State
);
673 if (OOK
== OO_Star
|| OOK
== OO_Arrow
) {
674 modelUse(State
, ThisRegion
, RD
, MK_Dereference
, C
);
679 modelUse(State
, ThisRegion
, RD
, MK_FunCall
, C
);
682 void MoveChecker::checkDeadSymbols(SymbolReaper
&SymReaper
,
683 CheckerContext
&C
) const {
684 ProgramStateRef State
= C
.getState();
685 TrackedRegionMapTy TrackedRegions
= State
->get
<TrackedRegionMap
>();
686 for (auto E
: TrackedRegions
) {
687 const MemRegion
*Region
= E
.first
;
688 bool IsRegDead
= !SymReaper
.isLiveRegion(Region
);
690 // Remove the dead regions from the region map.
692 State
= State
->remove
<TrackedRegionMap
>(Region
);
695 C
.addTransition(State
);
698 ProgramStateRef
MoveChecker::checkRegionChanges(
699 ProgramStateRef State
, const InvalidatedSymbols
*Invalidated
,
700 ArrayRef
<const MemRegion
*> RequestedRegions
,
701 ArrayRef
<const MemRegion
*> InvalidatedRegions
,
702 const LocationContext
*LCtx
, const CallEvent
*Call
) const {
704 // Relax invalidation upon function calls: only invalidate parameters
705 // that are passed directly via non-const pointers or non-const references
706 // or rvalue references.
707 // In case of an InstanceCall don't invalidate the this-region since
708 // it is fully handled in checkPreCall and checkPostCall.
709 const MemRegion
*ThisRegion
= nullptr;
710 if (const auto *IC
= dyn_cast
<CXXInstanceCall
>(Call
))
711 ThisRegion
= IC
->getCXXThisVal().getAsRegion();
713 // Requested ("explicit") regions are the regions passed into the call
714 // directly, but not all of them end up being invalidated.
715 // But when they do, they appear in the InvalidatedRegions array as well.
716 for (const auto *Region
: RequestedRegions
) {
717 if (ThisRegion
!= Region
&&
718 llvm::is_contained(InvalidatedRegions
, Region
))
719 State
= removeFromState(State
, Region
);
722 // For invalidations that aren't caused by calls, assume nothing. In
723 // particular, direct write into an object's field invalidates the status.
724 for (const auto *Region
: InvalidatedRegions
)
725 State
= removeFromState(State
, Region
->getBaseRegion());
731 void MoveChecker::printState(raw_ostream
&Out
, ProgramStateRef State
,
732 const char *NL
, const char *Sep
) const {
734 TrackedRegionMapTy RS
= State
->get
<TrackedRegionMap
>();
737 Out
<< Sep
<< "Moved-from objects :" << NL
;
739 I
.first
->dumpToStream(Out
);
740 if (I
.second
.isMoved())
743 Out
<< ": moved and reported";
748 void ento::registerMoveChecker(CheckerManager
&mgr
) {
749 MoveChecker
*chk
= mgr
.registerChecker
<MoveChecker
>();
750 chk
->setAggressiveness(
751 mgr
.getAnalyzerOptions().getCheckerStringOption(chk
, "WarnOn"), mgr
);
754 bool ento::shouldRegisterMoveChecker(const CheckerManager
&mgr
) {