clang 20.0.0git
MoveChecker.cpp
Go to the documentation of this file.
1// MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===//
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// 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
11// state.
12//
13//===----------------------------------------------------------------------===//
14
15#include "Move.h"
16#include "clang/AST/Attr.h"
17#include "clang/AST/ExprCXX.h"
25#include "llvm/ADT/StringSet.h"
26
27using namespace clang;
28using namespace ento;
29
30namespace {
31struct RegionState {
32private:
33 enum Kind { Moved, Reported } K;
34 RegionState(Kind InK) : K(InK) {}
35
36public:
37 bool isReported() const { return K == Reported; }
38 bool isMoved() const { return K == Moved; }
39
40 static RegionState getReported() { return RegionState(Reported); }
41 static RegionState getMoved() { return RegionState(Moved); }
42
43 bool operator==(const RegionState &X) const { return K == X.K; }
44 void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
45};
46} // end of anonymous namespace
47
48namespace {
49class MoveChecker
50 : public Checker<check::PreCall, check::PostCall,
51 check::DeadSymbols, check::RegionChanges> {
52public:
53 void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
54 void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
55 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
57 checkRegionChanges(ProgramStateRef State,
58 const InvalidatedSymbols *Invalidated,
59 ArrayRef<const MemRegion *> RequestedRegions,
60 ArrayRef<const MemRegion *> InvalidatedRegions,
61 const LocationContext *LCtx, const CallEvent *Call) const;
62 void printState(raw_ostream &Out, ProgramStateRef State,
63 const char *NL, const char *Sep) const override;
64
65private:
66 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
67 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
68
69 enum AggressivenessKind { // In any case, don't warn after a reset.
70 AK_Invalid = -1,
71 AK_KnownsOnly = 0, // Warn only about known move-unsafe classes.
72 AK_KnownsAndLocals = 1, // Also warn about all local objects.
73 AK_All = 2, // Warn on any use-after-move.
74 AK_NumKinds = AK_All
75 };
76
77 static bool misuseCausesCrash(MisuseKind MK) {
78 return MK == MK_Dereference;
79 }
80
81 struct ObjectKind {
82 // Is this a local variable or a local rvalue reference?
83 bool IsLocal;
84 // Is this an STL object? If so, of what kind?
85 StdObjectKind StdKind;
86 };
87
88 // STL smart pointers are automatically re-initialized to null when moved
89 // from. So we can't warn on many methods, but we can warn when it is
90 // dereferenced, which is UB even if the resulting lvalue never gets read.
91 const llvm::StringSet<> StdSmartPtrClasses = {
92 "shared_ptr",
93 "unique_ptr",
94 "weak_ptr",
95 };
96
97 // Not all of these are entirely move-safe, but they do provide *some*
98 // guarantees, and it means that somebody is using them after move
99 // in a valid manner.
100 // TODO: We can still try to identify *unsafe* use after move,
101 // like we did with smart pointers.
102 const llvm::StringSet<> StdSafeClasses = {
103 "basic_filebuf",
104 "basic_ios",
105 "future",
106 "optional",
107 "packaged_task",
108 "promise",
109 "shared_future",
110 "shared_lock",
111 "thread",
112 "unique_lock",
113 };
114
115 // Should we bother tracking the state of the object?
116 bool shouldBeTracked(ObjectKind OK) const {
117 // In non-aggressive mode, only warn on use-after-move of local variables
118 // (or local rvalue references) and of STL objects. The former is possible
119 // because local variables (or local rvalue references) are not tempting
120 // their user to re-use the storage. The latter is possible because STL
121 // objects are known to end up in a valid but unspecified state after the
122 // move and their state-reset methods are also known, which allows us to
123 // predict precisely when use-after-move is invalid.
124 // Some STL objects are known to conform to additional contracts after move,
125 // so they are not tracked. However, smart pointers specifically are tracked
126 // because we can perform extra checking over them.
127 // In aggressive mode, warn on any use-after-move because the user has
128 // intentionally asked us to completely eliminate use-after-move
129 // in his code.
130 return (Aggressiveness == AK_All) ||
131 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
132 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
133 }
134
135 // Some objects only suffer from some kinds of misuses, but we need to track
136 // them anyway because we cannot know in advance what misuse will we find.
137 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const {
138 // Additionally, only warn on smart pointers when they are dereferenced (or
139 // local or we are aggressive).
140 return shouldBeTracked(OK) &&
141 ((Aggressiveness == AK_All) ||
142 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
143 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
144 }
145
146 // Obtains ObjectKind of an object. Because class declaration cannot always
147 // be easily obtained from the memory region, it is supplied separately.
148 ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const;
149
150 // Classifies the object and dumps a user-friendly description string to
151 // the stream.
152 void explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
153 const CXXRecordDecl *RD, MisuseKind MK) const;
154
155 bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const;
156
157 class MovedBugVisitor : public BugReporterVisitor {
158 public:
159 MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R,
160 const CXXRecordDecl *RD, MisuseKind MK)
161 : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {}
162
163 void Profile(llvm::FoldingSetNodeID &ID) const override {
164 static int X = 0;
165 ID.AddPointer(&X);
166 ID.AddPointer(Region);
167 // Don't add RD because it's, in theory, uniquely determined by
168 // the region. In practice though, it's not always possible to obtain
169 // the declaration directly from the region, that's why we store it
170 // in the first place.
171 }
172
173 PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
175 PathSensitiveBugReport &BR) override;
176
177 private:
178 const MoveChecker &Chk;
179 // The tracked region.
180 const MemRegion *Region;
181 // The class of the tracked object.
182 const CXXRecordDecl *RD;
183 // How exactly the object was misused.
184 const MisuseKind MK;
185 bool Found;
186 };
187
188 AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
189
190public:
191 void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
192 Aggressiveness =
193 llvm::StringSwitch<AggressivenessKind>(Str)
194 .Case("KnownsOnly", AK_KnownsOnly)
195 .Case("KnownsAndLocals", AK_KnownsAndLocals)
196 .Case("All", AK_All)
197 .Default(AK_Invalid);
198
199 if (Aggressiveness == AK_Invalid)
200 Mgr.reportInvalidCheckerOptionValue(this, "WarnOn",
201 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
202 };
203
204private:
205 BugType BT{this, "Use-after-move", categories::CXXMoveSemantics};
206
207 // Check if the given form of potential misuse of a given object
208 // should be reported. If so, get it reported. The callback from which
209 // this function was called should immediately return after the call
210 // because this function adds one or two transitions.
211 void modelUse(ProgramStateRef State, const MemRegion *Region,
212 const CXXRecordDecl *RD, MisuseKind MK,
213 CheckerContext &C) const;
214
215 // Returns the exploded node against which the report was emitted.
216 // The caller *must* add any further transitions against this node.
217 // Returns nullptr and does not report if such node already exists.
218 ExplodedNode *tryToReportBug(const MemRegion *Region, const CXXRecordDecl *RD,
219 CheckerContext &C, MisuseKind MK) const;
220
221 bool isInMoveSafeContext(const LocationContext *LC) const;
222 bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
223 bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
224 const ExplodedNode *getMoveLocation(const ExplodedNode *N,
225 const MemRegion *Region,
226 CheckerContext &C) const;
227};
228} // end anonymous namespace
229
230REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
231
232// Define the inter-checker API.
233namespace clang {
234namespace ento {
235namespace move {
236bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) {
237 const RegionState *RS = State->get<TrackedRegionMap>(Region);
238 return RS && (RS->isMoved() || RS->isReported());
239}
240} // namespace move
241} // namespace ento
242} // namespace clang
243
244// If a region is removed all of the subregions needs to be removed too.
246 const MemRegion *Region) {
247 if (!Region)
248 return State;
249 for (auto &E : State->get<TrackedRegionMap>()) {
250 if (E.first->isSubRegionOf(Region))
251 State = State->remove<TrackedRegionMap>(E.first);
252 }
253 return State;
254}
255
257 const MemRegion *Region) {
258 for (auto &E : State->get<TrackedRegionMap>()) {
259 if (Region->isSubRegionOf(E.first) && E.second.isReported())
260 return true;
261 }
262 return false;
263}
264
266 if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
267 SymbolRef Sym = SR->getSymbol();
268 if (Sym->getType()->isRValueReferenceType())
269 if (const MemRegion *OriginMR = Sym->getOriginRegion())
270 return OriginMR;
271 }
272 return MR;
273}
274
276MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
279 // We need only the last move of the reported object's region.
280 // The visitor walks the ExplodedGraph backwards.
281 if (Found)
282 return nullptr;
283 ProgramStateRef State = N->getState();
284 ProgramStateRef StatePrev = N->getFirstPred()->getState();
285 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
286 const RegionState *TrackedObjectPrev =
287 StatePrev->get<TrackedRegionMap>(Region);
288 if (!TrackedObject)
289 return nullptr;
290 if (TrackedObjectPrev && TrackedObject)
291 return nullptr;
292
293 // Retrieve the associated statement.
294 const Stmt *S = N->getStmtForDiagnostics();
295 if (!S)
296 return nullptr;
297 Found = true;
298
300 llvm::raw_svector_ostream OS(Str);
301
302 ObjectKind OK = Chk.classifyObject(Region, RD);
303 switch (OK.StdKind) {
304 case SK_SmartPtr:
305 if (MK == MK_Dereference) {
306 OS << "Smart pointer";
307 Chk.explainObject(OS, Region, RD, MK);
308 OS << " is reset to null when moved from";
309 break;
310 }
311
312 // If it's not a dereference, we don't care if it was reset to null
313 // or that it is even a smart pointer.
314 [[fallthrough]];
315 case SK_NonStd:
316 case SK_Safe:
317 OS << "Object";
318 Chk.explainObject(OS, Region, RD, MK);
319 OS << " is moved";
320 break;
321 case SK_Unsafe:
322 OS << "Object";
323 Chk.explainObject(OS, Region, RD, MK);
324 OS << " is left in a valid but unspecified state after move";
325 break;
326 }
327
328 // Generate the extra diagnostic.
330 N->getLocationContext());
331 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
332}
333
334const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N,
335 const MemRegion *Region,
336 CheckerContext &C) const {
337 // Walk the ExplodedGraph backwards and find the first node that referred to
338 // the tracked region.
339 const ExplodedNode *MoveNode = N;
340
341 while (N) {
342 ProgramStateRef State = N->getState();
343 if (!State->get<TrackedRegionMap>(Region))
344 break;
345 MoveNode = N;
346 N = N->pred_empty() ? nullptr : *(N->pred_begin());
347 }
348 return MoveNode;
349}
350
351void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
352 const CXXRecordDecl *RD, MisuseKind MK,
353 CheckerContext &C) const {
354 assert(!C.isDifferent() && "No transitions should have been made by now");
355 const RegionState *RS = State->get<TrackedRegionMap>(Region);
356 ObjectKind OK = classifyObject(Region, RD);
357
358 // Just in case: if it's not a smart pointer but it does have operator *,
359 // we shouldn't call the bug a dereference.
360 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
361 MK = MK_FunCall;
362
363 if (!RS || !shouldWarnAbout(OK, MK)
364 || isInMoveSafeContext(C.getLocationContext())) {
365 // Finalize changes made by the caller.
366 C.addTransition(State);
367 return;
368 }
369
370 // Don't report it in case if any base region is already reported.
371 // But still generate a sink in case of UB.
372 // And still finalize changes made by the caller.
373 if (isAnyBaseRegionReported(State, Region)) {
374 if (misuseCausesCrash(MK)) {
375 C.generateSink(State, C.getPredecessor());
376 } else {
377 C.addTransition(State);
378 }
379 return;
380 }
381
382 ExplodedNode *N = tryToReportBug(Region, RD, C, MK);
383
384 // If the program has already crashed on this path, don't bother.
385 if (!N || N->isSink())
386 return;
387
388 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
389 C.addTransition(State, N);
390}
391
392ExplodedNode *MoveChecker::tryToReportBug(const MemRegion *Region,
393 const CXXRecordDecl *RD,
395 MisuseKind MK) const {
396 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
397 : C.generateNonFatalErrorNode()) {
398 // Uniqueing report to the same object.
399 PathDiagnosticLocation LocUsedForUniqueing;
400 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
401
402 if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
403 LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
404 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
405
406 // Creating the error message.
408 llvm::raw_svector_ostream OS(Str);
409 switch(MK) {
410 case MK_FunCall:
411 OS << "Method called on moved-from object";
412 explainObject(OS, Region, RD, MK);
413 break;
414 case MK_Copy:
415 OS << "Moved-from object";
416 explainObject(OS, Region, RD, MK);
417 OS << " is copied";
418 break;
419 case MK_Move:
420 OS << "Moved-from object";
421 explainObject(OS, Region, RD, MK);
422 OS << " is moved";
423 break;
424 case MK_Dereference:
425 OS << "Dereference of null smart pointer";
426 explainObject(OS, Region, RD, MK);
427 break;
428 }
429
430 auto R = std::make_unique<PathSensitiveBugReport>(
431 BT, OS.str(), N, LocUsedForUniqueing,
432 MoveNode->getLocationContext()->getDecl());
433 R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
434 C.emitReport(std::move(R));
435 return N;
436 }
437 return nullptr;
438}
439
440void MoveChecker::checkPostCall(const CallEvent &Call,
441 CheckerContext &C) const {
442 const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
443 if (!AFC)
444 return;
445
446 ProgramStateRef State = C.getState();
447 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
448 if (!MethodDecl)
449 return;
450
451 // Check if an object became moved-from.
452 // Object can become moved from after a call to move assignment operator or
453 // move constructor .
454 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
455 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
456 return;
457
458 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
459 return;
460
461 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
462 if (!ArgRegion)
463 return;
464
465 // Skip moving the object to itself.
466 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
467 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
468 return;
469
470 if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
471 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
472 return;
473
474 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
475 // Skip temp objects because of their short lifetime.
476 if (BaseRegion->getAs<CXXTempObjectRegion>() ||
477 AFC->getArgExpr(0)->isPRValue())
478 return;
479 // If it has already been reported do not need to modify the state.
480
481 if (State->get<TrackedRegionMap>(ArgRegion))
482 return;
483
484 const CXXRecordDecl *RD = MethodDecl->getParent();
485 ObjectKind OK = classifyObject(ArgRegion, RD);
486 if (shouldBeTracked(OK)) {
487 // Mark object as moved-from.
488 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
489 C.addTransition(State);
490 return;
491 }
492 assert(!C.isDifferent() && "Should not have made transitions on this path!");
493}
494
495bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const {
496 // We abandon the cases where bool/void/void* conversion happens.
497 if (const auto *ConversionDec =
498 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
499 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
500 if (!Tp)
501 return false;
502 if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
503 return true;
504 }
505 // Function call `empty` can be skipped.
506 return (MethodDec && MethodDec->getDeclName().isIdentifier() &&
507 (MethodDec->getName().lower() == "empty" ||
508 MethodDec->getName().lower() == "isempty"));
509}
510
511bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const {
512 if (!MethodDec)
513 return false;
514 if (MethodDec->hasAttr<ReinitializesAttr>())
515 return true;
516 if (MethodDec->getDeclName().isIdentifier()) {
517 std::string MethodName = MethodDec->getName().lower();
518 // TODO: Some of these methods (eg., resize) are not always resetting
519 // the state, so we should consider looking at the arguments.
520 if (MethodName == "assign" || MethodName == "clear" ||
521 MethodName == "destroy" || MethodName == "reset" ||
522 MethodName == "resize" || MethodName == "shrink")
523 return true;
524 }
525 return false;
526}
527
528// Don't report an error inside a move related operation.
529// We assume that the programmer knows what she does.
530bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const {
531 do {
532 const auto *CtxDec = LC->getDecl();
533 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
534 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
535 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
536 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
537 (MethodDec && MethodDec->isOverloadedOperator() &&
538 MethodDec->getOverloadedOperator() == OO_Equal) ||
539 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
540 return true;
541 } while ((LC = LC->getParent()));
542 return false;
543}
544
545bool MoveChecker::belongsTo(const CXXRecordDecl *RD,
546 const llvm::StringSet<> &Set) const {
547 const IdentifierInfo *II = RD->getIdentifier();
548 return II && Set.count(II->getName());
549}
550
551MoveChecker::ObjectKind
552MoveChecker::classifyObject(const MemRegion *MR,
553 const CXXRecordDecl *RD) const {
554 // Local variables and local rvalue references are classified as "Local".
555 // For the purposes of this checker, we classify move-safe STL types
556 // as not-"STL" types, because that's how the checker treats them.
558 bool IsLocal =
559 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
560 isa<StackSpaceRegion>(MR->getMemorySpace());
561
562 if (!RD || !RD->getDeclContext()->isStdNamespace())
563 return { IsLocal, SK_NonStd };
564
565 if (belongsTo(RD, StdSmartPtrClasses))
566 return { IsLocal, SK_SmartPtr };
567
568 if (belongsTo(RD, StdSafeClasses))
569 return { IsLocal, SK_Safe };
570
571 return { IsLocal, SK_Unsafe };
572}
573
574void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
575 const CXXRecordDecl *RD, MisuseKind MK) const {
576 // We may need a leading space every time we actually explain anything,
577 // and we never know if we are to explain anything until we try.
578 if (const auto DR =
579 dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) {
580 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
581 OS << " '" << RegionDecl->getDeclName() << "'";
582 }
583
584 ObjectKind OK = classifyObject(MR, RD);
585 switch (OK.StdKind) {
586 case SK_NonStd:
587 case SK_Safe:
588 break;
589 case SK_SmartPtr:
590 if (MK != MK_Dereference)
591 break;
592
593 // We only care about the type if it's a dereference.
594 [[fallthrough]];
595 case SK_Unsafe:
596 OS << " of type '" << RD->getQualifiedNameAsString() << "'";
597 break;
598 };
599}
600
601void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
602 ProgramStateRef State = C.getState();
603
604 // Remove the MemRegions from the map on which a ctor/dtor call or assignment
605 // happened.
606
607 // Checking constructor calls.
608 if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
609 State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
610 auto CtorDec = CC->getDecl();
611 // Check for copying a moved-from object and report the bug.
612 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
613 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
614 const CXXRecordDecl *RD = CtorDec->getParent();
615 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
616 modelUse(State, ArgRegion, RD, MK, C);
617 return;
618 }
619 }
620
621 const auto IC = dyn_cast<CXXInstanceCall>(&Call);
622 if (!IC)
623 return;
624
625 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
626 if (!ThisRegion)
627 return;
628
629 // The remaining part is check only for method call on a moved-from object.
630 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
631 if (!MethodDecl)
632 return;
633
634 // Calling a destructor on a moved object is fine.
635 if (isa<CXXDestructorDecl>(MethodDecl))
636 return;
637
638 // We want to investigate the whole object, not only sub-object of a parent
639 // class in which the encountered method defined.
640 ThisRegion = ThisRegion->getMostDerivedObjectRegion();
641
642 if (isStateResetMethod(MethodDecl)) {
643 State = removeFromState(State, ThisRegion);
644 C.addTransition(State);
645 return;
646 }
647
648 if (isMoveSafeMethod(MethodDecl))
649 return;
650
651 // Store class declaration as well, for bug reporting purposes.
652 const CXXRecordDecl *RD = MethodDecl->getParent();
653
654 if (MethodDecl->isOverloadedOperator()) {
655 OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator();
656
657 if (OOK == OO_Equal) {
658 // Remove the tracked object for every assignment operator, but report bug
659 // only for move or copy assignment's argument.
660 State = removeFromState(State, ThisRegion);
661
662 if (MethodDecl->isCopyAssignmentOperator() ||
663 MethodDecl->isMoveAssignmentOperator()) {
664 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
665 MisuseKind MK =
666 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
667 modelUse(State, ArgRegion, RD, MK, C);
668 return;
669 }
670 C.addTransition(State);
671 return;
672 }
673
674 if (OOK == OO_Star || OOK == OO_Arrow) {
675 modelUse(State, ThisRegion, RD, MK_Dereference, C);
676 return;
677 }
678 }
679
680 modelUse(State, ThisRegion, RD, MK_FunCall, C);
681}
682
683void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
684 CheckerContext &C) const {
685 ProgramStateRef State = C.getState();
686 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
687 for (auto E : TrackedRegions) {
688 const MemRegion *Region = E.first;
689 bool IsRegDead = !SymReaper.isLiveRegion(Region);
690
691 // Remove the dead regions from the region map.
692 if (IsRegDead) {
693 State = State->remove<TrackedRegionMap>(Region);
694 }
695 }
696 C.addTransition(State);
697}
698
699ProgramStateRef MoveChecker::checkRegionChanges(
700 ProgramStateRef State, const InvalidatedSymbols *Invalidated,
701 ArrayRef<const MemRegion *> RequestedRegions,
702 ArrayRef<const MemRegion *> InvalidatedRegions,
703 const LocationContext *LCtx, const CallEvent *Call) const {
704 if (Call) {
705 // Relax invalidation upon function calls: only invalidate parameters
706 // that are passed directly via non-const pointers or non-const references
707 // or rvalue references.
708 // In case of an InstanceCall don't invalidate the this-region since
709 // it is fully handled in checkPreCall and checkPostCall.
710 const MemRegion *ThisRegion = nullptr;
711 if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
712 ThisRegion = IC->getCXXThisVal().getAsRegion();
713
714 // Requested ("explicit") regions are the regions passed into the call
715 // directly, but not all of them end up being invalidated.
716 // But when they do, they appear in the InvalidatedRegions array as well.
717 for (const auto *Region : RequestedRegions) {
718 if (ThisRegion != Region &&
719 llvm::is_contained(InvalidatedRegions, Region))
720 State = removeFromState(State, Region);
721 }
722 } else {
723 // For invalidations that aren't caused by calls, assume nothing. In
724 // particular, direct write into an object's field invalidates the status.
725 for (const auto *Region : InvalidatedRegions)
726 State = removeFromState(State, Region->getBaseRegion());
727 }
728
729 return State;
730}
731
732void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
733 const char *NL, const char *Sep) const {
734
735 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
736
737 if (!RS.isEmpty()) {
738 Out << Sep << "Moved-from objects :" << NL;
739 for (auto I: RS) {
740 I.first->dumpToStream(Out);
741 if (I.second.isMoved())
742 Out << ": moved";
743 else
744 Out << ": moved and reported";
745 Out << NL;
746 }
747 }
748}
749void ento::registerMoveChecker(CheckerManager &mgr) {
750 MoveChecker *chk = mgr.registerChecker<MoveChecker>();
751 chk->setAggressiveness(
752 mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr);
753}
754
755bool ento::shouldRegisterMoveChecker(const CheckerManager &mgr) {
756 return true;
757}
Expr * E
Defines the clang::Expr interface and subclasses for C++ expressions.
#define X(type, name)
Definition: Value.h:144
static bool isAnyBaseRegionReported(ProgramStateRef State, const MemRegion *Region)
static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region)
static const MemRegion * unwrapRValueReferenceIndirection(const MemRegion *MR)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
StringRef getCheckerStringOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Query an option's string value.
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2078
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:2089
bool isStdNamespace() const
Definition: DeclBase.cpp:1318
DeclContext * getDeclContext()
Definition: DeclBase.h:451
bool hasAttr() const
Definition: DeclBase.h:580
bool isIdentifier() const
Predicate functions for querying what type of name this is.
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
Definition: Decl.h:2808
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
Definition: Decl.cpp:3989
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const LocationContext * getParent() const
It might return null.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:274
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:280
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:319
std::string getQualifiedNameAsString() const
Definition: Decl.cpp:1668
Stmt - This represents one statement.
Definition: Stmt.h:84
The base class of the type hierarchy.
Definition: Type.h:1828
bool isVoidType() const
Definition: Type.h:8510
bool isBooleanType() const
Definition: Type.h:8638
bool isRValueReferenceType() const
Definition: Type.h:8212
bool isVoidPointerType() const
Definition: Type.cpp:698
const SourceManager & getSourceManager() const
Definition: BugReporter.h:737
BugReporterVisitors are used to add custom diagnostics along a path.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
Definition: Checker.h:496
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
void reportInvalidCheckerOptionValue(const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) const
Emits an error through a DiagnosticsEngine about an invalid user supplied checker option value.
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:97
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
Definition: MemRegion.cpp:1351
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
Definition: MemRegion.cpp:1404
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1377
const RegionTy * getAs() const
Definition: MemRegion.h:1388
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getMostDerivedObjectRegion() const
Recursively retrieve the region of the most derived class instance of regions of C++ base class insta...
Definition: MemRegion.cpp:1397
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
Symbolic value.
Definition: SymExpr.h:30
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
Definition: SymExpr.h:104
virtual QualType getType() const =0
A class responsible for cleaning up unused symbols.
bool isLiveRegion(const MemRegion *region)
const char *const CXXMoveSemantics
bool isMovedFrom(ProgramStateRef State, const MemRegion *Region)
Returns true if the object is known to have been recently std::moved.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
Definition: OperatorKinds.h:21
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition: CallGraph.h:204
#define false
Definition: stdbool.h:26