1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | #include "clang/AST/ExprCXX.h" |
16 | #include "clang/Driver/DriverDiagnostic.h" |
17 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
18 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
19 | #include "clang/StaticAnalyzer/Core/Checker.h" |
20 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
21 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
22 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
23 | #include "llvm/ADT/StringSet.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | namespace { |
29 | struct RegionState { |
30 | private: |
31 | enum Kind { Moved, Reported } K; |
32 | RegionState(Kind InK) : K(InK) {} |
33 | |
34 | public: |
35 | bool isReported() const { return K == Reported; } |
36 | bool isMoved() const { return K == Moved; } |
37 | |
38 | static RegionState getReported() { return RegionState(Reported); } |
39 | static RegionState getMoved() { return RegionState(Moved); } |
40 | |
41 | bool operator==(const RegionState &X) const { return K == X.K; } |
42 | void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } |
43 | }; |
44 | } |
45 | |
46 | namespace { |
47 | class MoveChecker |
48 | : public Checker<check::PreCall, check::PostCall, |
49 | check::DeadSymbols, check::RegionChanges> { |
50 | public: |
51 | void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; |
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; |
55 | ProgramStateRef |
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; |
63 | |
64 | private: |
65 | enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference }; |
66 | enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr }; |
67 | |
68 | enum AggressivenessKind { |
69 | AK_Invalid = -1, |
70 | AK_KnownsOnly = 0, |
71 | AK_KnownsAndLocals = 1, |
72 | AK_All = 2, |
73 | AK_NumKinds = AK_All |
74 | }; |
75 | |
76 | static bool misuseCausesCrash(MisuseKind MK) { |
77 | return MK == MK_Dereference; |
78 | } |
79 | |
80 | struct ObjectKind { |
81 | |
82 | bool IsLocal; |
83 | |
84 | StdObjectKind StdKind; |
85 | }; |
86 | |
87 | |
88 | |
89 | |
90 | const llvm::StringSet<> StdSmartPtrClasses = { |
91 | "shared_ptr", |
92 | "unique_ptr", |
93 | "weak_ptr", |
94 | }; |
95 | |
96 | |
97 | |
98 | |
99 | |
100 | |
101 | const llvm::StringSet<> StdSafeClasses = { |
102 | "basic_filebuf", |
103 | "basic_ios", |
104 | "future", |
105 | "optional", |
106 | "packaged_task" |
107 | "promise", |
108 | "shared_future", |
109 | "shared_lock", |
110 | "thread", |
111 | "unique_lock", |
112 | }; |
113 | |
114 | |
115 | bool shouldBeTracked(ObjectKind OK) const { |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | |
122 | |
123 | |
124 | |
125 | |
126 | |
127 | |
128 | |
129 | return (Aggressiveness == AK_All) || |
130 | (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || |
131 | OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr; |
132 | } |
133 | |
134 | |
135 | |
136 | bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const { |
137 | |
138 | |
139 | return shouldBeTracked(OK) && |
140 | ((Aggressiveness == AK_All) || |
141 | (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || |
142 | OK.StdKind != SK_SmartPtr || MK == MK_Dereference); |
143 | } |
144 | |
145 | |
146 | |
147 | ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; |
148 | |
149 | |
150 | |
151 | void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, |
152 | const CXXRecordDecl *RD, MisuseKind MK) const; |
153 | |
154 | bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; |
155 | |
156 | class MovedBugVisitor : public BugReporterVisitor { |
157 | public: |
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) {} |
161 | |
162 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
163 | static int X = 0; |
164 | ID.AddPointer(&X); |
165 | ID.AddPointer(Region); |
166 | |
167 | |
168 | |
169 | |
170 | } |
171 | |
172 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
173 | BugReporterContext &BRC, |
174 | BugReport &BR) override; |
175 | |
176 | private: |
177 | const MoveChecker &Chk; |
178 | |
179 | const MemRegion *Region; |
180 | |
181 | const CXXRecordDecl *RD; |
182 | |
183 | const MisuseKind MK; |
184 | bool Found; |
185 | }; |
186 | |
187 | AggressivenessKind Aggressiveness; |
188 | |
189 | public: |
190 | void setAggressiveness(StringRef Str, CheckerManager &Mgr) { |
191 | Aggressiveness = |
192 | llvm::StringSwitch<AggressivenessKind>(Str) |
193 | .Case("KnownsOnly", AK_KnownsOnly) |
194 | .Case("KnownsAndLocals", AK_KnownsAndLocals) |
195 | .Case("All", AK_All) |
196 | .Default(AK_Invalid); |
197 | |
198 | if (Aggressiveness == AK_Invalid) |
199 | Mgr.reportInvalidCheckerOptionValue(this, "WarnOn", |
200 | "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value"); |
201 | }; |
202 | |
203 | private: |
204 | mutable std::unique_ptr<BugType> BT; |
205 | |
206 | |
207 | |
208 | |
209 | |
210 | void modelUse(ProgramStateRef State, const MemRegion *Region, |
211 | const CXXRecordDecl *RD, MisuseKind MK, |
212 | CheckerContext &C) const; |
213 | |
214 | |
215 | |
216 | ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, |
217 | CheckerContext &C, MisuseKind MK) const; |
218 | |
219 | bool isInMoveSafeContext(const LocationContext *LC) const; |
220 | bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; |
221 | bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; |
222 | const ExplodedNode *getMoveLocation(const ExplodedNode *N, |
223 | const MemRegion *Region, |
224 | CheckerContext &C) const; |
225 | }; |
226 | } |
227 | |
228 | REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) |
229 | |
230 | |
231 | static ProgramStateRef removeFromState(ProgramStateRef State, |
232 | const MemRegion *Region) { |
233 | if (!Region) |
234 | return State; |
235 | for (auto &E : State->get<TrackedRegionMap>()) { |
236 | if (E.first->isSubRegionOf(Region)) |
237 | State = State->remove<TrackedRegionMap>(E.first); |
238 | } |
239 | return State; |
240 | } |
241 | |
242 | static bool isAnyBaseRegionReported(ProgramStateRef State, |
243 | const MemRegion *Region) { |
244 | for (auto &E : State->get<TrackedRegionMap>()) { |
245 | if (Region->isSubRegionOf(E.first) && E.second.isReported()) |
246 | return true; |
247 | } |
248 | return false; |
249 | } |
250 | |
251 | static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { |
252 | if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { |
253 | SymbolRef Sym = SR->getSymbol(); |
254 | if (Sym->getType()->isRValueReferenceType()) |
255 | if (const MemRegion *OriginMR = Sym->getOriginRegion()) |
256 | return OriginMR; |
257 | } |
258 | return MR; |
259 | } |
260 | |
261 | std::shared_ptr<PathDiagnosticPiece> |
262 | MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, |
263 | BugReporterContext &BRC, BugReport &BR) { |
264 | |
265 | |
266 | if (Found) |
267 | return nullptr; |
268 | ProgramStateRef State = N->getState(); |
269 | ProgramStateRef StatePrev = N->getFirstPred()->getState(); |
270 | const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); |
271 | const RegionState *TrackedObjectPrev = |
272 | StatePrev->get<TrackedRegionMap>(Region); |
273 | if (!TrackedObject) |
274 | return nullptr; |
275 | if (TrackedObjectPrev && TrackedObject) |
276 | return nullptr; |
277 | |
278 | |
279 | const Stmt *S = PathDiagnosticLocation::getStmt(N); |
280 | if (!S) |
281 | return nullptr; |
282 | Found = true; |
283 | |
284 | SmallString<128> Str; |
285 | llvm::raw_svector_ostream OS(Str); |
286 | |
287 | ObjectKind OK = Chk.classifyObject(Region, RD); |
288 | switch (OK.StdKind) { |
289 | case SK_SmartPtr: |
290 | if (MK == MK_Dereference) { |
291 | OS << "Smart pointer"; |
292 | Chk.explainObject(OS, Region, RD, MK); |
293 | OS << " is reset to null when moved from"; |
294 | break; |
295 | } |
296 | |
297 | |
298 | |
299 | LLVM_FALLTHROUGH; |
300 | case SK_NonStd: |
301 | case SK_Safe: |
302 | OS << "Object"; |
303 | Chk.explainObject(OS, Region, RD, MK); |
304 | OS << " is moved"; |
305 | break; |
306 | case SK_Unsafe: |
307 | OS << "Object"; |
308 | Chk.explainObject(OS, Region, RD, MK); |
309 | OS << " is left in a valid but unspecified state after move"; |
310 | break; |
311 | } |
312 | |
313 | |
314 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
315 | N->getLocationContext()); |
316 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); |
317 | } |
318 | |
319 | const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, |
320 | const MemRegion *Region, |
321 | CheckerContext &C) const { |
322 | |
323 | |
324 | const ExplodedNode *MoveNode = N; |
325 | |
326 | while (N) { |
327 | ProgramStateRef State = N->getState(); |
328 | if (!State->get<TrackedRegionMap>(Region)) |
329 | break; |
330 | MoveNode = N; |
331 | N = N->pred_empty() ? nullptr : *(N->pred_begin()); |
332 | } |
333 | return MoveNode; |
334 | } |
335 | |
336 | void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, |
337 | const CXXRecordDecl *RD, MisuseKind MK, |
338 | CheckerContext &C) const { |
339 | (0) . __assert_fail ("!C.isDifferent() && \"No transitions should have been made by now\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp", 339, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(!C.isDifferent() && "No transitions should have been made by now"); |
340 | const RegionState *RS = State->get<TrackedRegionMap>(Region); |
341 | ObjectKind OK = classifyObject(Region, RD); |
342 | |
343 | |
344 | |
345 | if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) |
346 | MK = MK_FunCall; |
347 | |
348 | if (!RS || !shouldWarnAbout(OK, MK) |
349 | || isInMoveSafeContext(C.getLocationContext())) { |
350 | |
351 | C.addTransition(State); |
352 | return; |
353 | } |
354 | |
355 | |
356 | |
357 | |
358 | if (isAnyBaseRegionReported(State, Region)) { |
359 | if (misuseCausesCrash(MK)) { |
360 | C.generateSink(State, C.getPredecessor()); |
361 | } else { |
362 | C.addTransition(State); |
363 | } |
364 | return; |
365 | } |
366 | |
367 | ExplodedNode *N = reportBug(Region, RD, C, MK); |
368 | |
369 | |
370 | if (N->isSink()) |
371 | return; |
372 | |
373 | State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); |
374 | C.addTransition(State, N); |
375 | } |
376 | |
377 | ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, |
378 | const CXXRecordDecl *RD, CheckerContext &C, |
379 | MisuseKind MK) const { |
380 | if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() |
381 | : C.generateNonFatalErrorNode()) { |
382 | |
383 | if (!BT) |
384 | BT.reset(new BugType(this, "Use-after-move", |
385 | "C++ move semantics")); |
386 | |
387 | |
388 | PathDiagnosticLocation LocUsedForUniqueing; |
389 | const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); |
390 | |
391 | if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode)) |
392 | LocUsedForUniqueing = PathDiagnosticLocation::createBegin( |
393 | MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); |
394 | |
395 | |
396 | llvm::SmallString<128> Str; |
397 | llvm::raw_svector_ostream OS(Str); |
398 | switch(MK) { |
399 | case MK_FunCall: |
400 | OS << "Method called on moved-from object"; |
401 | explainObject(OS, Region, RD, MK); |
402 | break; |
403 | case MK_Copy: |
404 | OS << "Moved-from object"; |
405 | explainObject(OS, Region, RD, MK); |
406 | OS << " is copied"; |
407 | break; |
408 | case MK_Move: |
409 | OS << "Moved-from object"; |
410 | explainObject(OS, Region, RD, MK); |
411 | OS << " is moved"; |
412 | break; |
413 | case MK_Dereference: |
414 | OS << "Dereference of null smart pointer"; |
415 | explainObject(OS, Region, RD, MK); |
416 | break; |
417 | } |
418 | |
419 | auto R = |
420 | llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing, |
421 | MoveNode->getLocationContext()->getDecl()); |
422 | R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); |
423 | C.emitReport(std::move(R)); |
424 | return N; |
425 | } |
426 | return nullptr; |
427 | } |
428 | |
429 | void MoveChecker::checkPostCall(const CallEvent &Call, |
430 | CheckerContext &C) const { |
431 | const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); |
432 | if (!AFC) |
433 | return; |
434 | |
435 | ProgramStateRef State = C.getState(); |
436 | const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); |
437 | if (!MethodDecl) |
438 | return; |
439 | |
440 | |
441 | |
442 | |
443 | const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); |
444 | if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) |
445 | return; |
446 | |
447 | if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) |
448 | return; |
449 | |
450 | const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); |
451 | if (!ArgRegion) |
452 | return; |
453 | |
454 | |
455 | const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); |
456 | if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) |
457 | return; |
458 | |
459 | if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) |
460 | if (IC->getCXXThisVal().getAsRegion() == ArgRegion) |
461 | return; |
462 | |
463 | const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); |
464 | |
465 | if (BaseRegion->getAs<CXXTempObjectRegion>() || |
466 | AFC->getArgExpr(0)->isRValue()) |
467 | return; |
468 | |
469 | |
470 | if (State->get<TrackedRegionMap>(ArgRegion)) |
471 | return; |
472 | |
473 | const CXXRecordDecl *RD = MethodDecl->getParent(); |
474 | ObjectKind OK = classifyObject(ArgRegion, RD); |
475 | if (shouldBeTracked(OK)) { |
476 | |
477 | State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); |
478 | C.addTransition(State); |
479 | return; |
480 | } |
481 | (0) . __assert_fail ("!C.isDifferent() && \"Should not have made transitions on this path!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp", 481, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(!C.isDifferent() && "Should not have made transitions on this path!"); |
482 | } |
483 | |
484 | bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { |
485 | |
486 | if (const auto *ConversionDec = |
487 | dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { |
488 | const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); |
489 | if (!Tp) |
490 | return false; |
491 | if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) |
492 | return true; |
493 | } |
494 | |
495 | return (MethodDec && MethodDec->getDeclName().isIdentifier() && |
496 | (MethodDec->getName().lower() == "empty" || |
497 | MethodDec->getName().lower() == "isempty")); |
498 | } |
499 | |
500 | bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { |
501 | if (!MethodDec) |
502 | return false; |
503 | if (MethodDec->hasAttr<ReinitializesAttr>()) |
504 | return true; |
505 | if (MethodDec->getDeclName().isIdentifier()) { |
506 | std::string MethodName = MethodDec->getName().lower(); |
507 | |
508 | |
509 | if (MethodName == "assign" || MethodName == "clear" || |
510 | MethodName == "destroy" || MethodName == "reset" || |
511 | MethodName == "resize" || MethodName == "shrink") |
512 | return true; |
513 | } |
514 | return false; |
515 | } |
516 | |
517 | |
518 | |
519 | bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { |
520 | do { |
521 | const auto *CtxDec = LC->getDecl(); |
522 | auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); |
523 | auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); |
524 | auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); |
525 | if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || |
526 | (MethodDec && MethodDec->isOverloadedOperator() && |
527 | MethodDec->getOverloadedOperator() == OO_Equal) || |
528 | isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) |
529 | return true; |
530 | } while ((LC = LC->getParent())); |
531 | return false; |
532 | } |
533 | |
534 | bool MoveChecker::belongsTo(const CXXRecordDecl *RD, |
535 | const llvm::StringSet<> &Set) const { |
536 | const IdentifierInfo *II = RD->getIdentifier(); |
537 | return II && Set.count(II->getName()); |
538 | } |
539 | |
540 | MoveChecker::ObjectKind |
541 | MoveChecker::classifyObject(const MemRegion *MR, |
542 | const CXXRecordDecl *RD) const { |
543 | |
544 | |
545 | |
546 | MR = unwrapRValueReferenceIndirection(MR); |
547 | bool IsLocal = |
548 | MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace()); |
549 | |
550 | if (!RD || !RD->getDeclContext()->isStdNamespace()) |
551 | return { IsLocal, SK_NonStd }; |
552 | |
553 | if (belongsTo(RD, StdSmartPtrClasses)) |
554 | return { IsLocal, SK_SmartPtr }; |
555 | |
556 | if (belongsTo(RD, StdSafeClasses)) |
557 | return { IsLocal, SK_Safe }; |
558 | |
559 | return { IsLocal, SK_Unsafe }; |
560 | } |
561 | |
562 | void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, |
563 | const CXXRecordDecl *RD, MisuseKind MK) const { |
564 | |
565 | |
566 | if (const auto DR = |
567 | dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { |
568 | const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); |
569 | OS << " '" << RegionDecl->getNameAsString() << "'"; |
570 | } |
571 | |
572 | ObjectKind OK = classifyObject(MR, RD); |
573 | switch (OK.StdKind) { |
574 | case SK_NonStd: |
575 | case SK_Safe: |
576 | break; |
577 | case SK_SmartPtr: |
578 | if (MK != MK_Dereference) |
579 | break; |
580 | |
581 | |
582 | LLVM_FALLTHROUGH; |
583 | case SK_Unsafe: |
584 | OS << " of type '" << RD->getQualifiedNameAsString() << "'"; |
585 | break; |
586 | }; |
587 | } |
588 | |
589 | void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { |
590 | ProgramStateRef State = C.getState(); |
591 | |
592 | |
593 | |
594 | |
595 | |
596 | if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { |
597 | State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); |
598 | auto CtorDec = CC->getDecl(); |
599 | |
600 | if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { |
601 | const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); |
602 | const CXXRecordDecl *RD = CtorDec->getParent(); |
603 | MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; |
604 | modelUse(State, ArgRegion, RD, MK, C); |
605 | return; |
606 | } |
607 | } |
608 | |
609 | const auto IC = dyn_cast<CXXInstanceCall>(&Call); |
610 | if (!IC) |
611 | return; |
612 | |
613 | |
614 | if (isa<CXXDestructorCall>(IC)) |
615 | return; |
616 | |
617 | const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); |
618 | if (!ThisRegion) |
619 | return; |
620 | |
621 | |
622 | const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); |
623 | if (!MethodDecl) |
624 | return; |
625 | |
626 | |
627 | |
628 | ThisRegion = ThisRegion->getMostDerivedObjectRegion(); |
629 | |
630 | if (isStateResetMethod(MethodDecl)) { |
631 | State = removeFromState(State, ThisRegion); |
632 | C.addTransition(State); |
633 | return; |
634 | } |
635 | |
636 | if (isMoveSafeMethod(MethodDecl)) |
637 | return; |
638 | |
639 | |
640 | const CXXRecordDecl *RD = MethodDecl->getParent(); |
641 | |
642 | if (MethodDecl->isOverloadedOperator()) { |
643 | OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); |
644 | |
645 | if (OOK == OO_Equal) { |
646 | |
647 | |
648 | State = removeFromState(State, ThisRegion); |
649 | |
650 | if (MethodDecl->isCopyAssignmentOperator() || |
651 | MethodDecl->isMoveAssignmentOperator()) { |
652 | const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); |
653 | MisuseKind MK = |
654 | MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; |
655 | modelUse(State, ArgRegion, RD, MK, C); |
656 | return; |
657 | } |
658 | C.addTransition(State); |
659 | return; |
660 | } |
661 | |
662 | if (OOK == OO_Star || OOK == OO_Arrow) { |
663 | modelUse(State, ThisRegion, RD, MK_Dereference, C); |
664 | return; |
665 | } |
666 | } |
667 | |
668 | modelUse(State, ThisRegion, RD, MK_FunCall, C); |
669 | } |
670 | |
671 | void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, |
672 | CheckerContext &C) const { |
673 | ProgramStateRef State = C.getState(); |
674 | TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); |
675 | for (TrackedRegionMapTy::value_type E : TrackedRegions) { |
676 | const MemRegion *Region = E.first; |
677 | bool IsRegDead = !SymReaper.isLiveRegion(Region); |
678 | |
679 | |
680 | if (IsRegDead) { |
681 | State = State->remove<TrackedRegionMap>(Region); |
682 | } |
683 | } |
684 | C.addTransition(State); |
685 | } |
686 | |
687 | ProgramStateRef MoveChecker::checkRegionChanges( |
688 | ProgramStateRef State, const InvalidatedSymbols *Invalidated, |
689 | ArrayRef<const MemRegion *> RequestedRegions, |
690 | ArrayRef<const MemRegion *> InvalidatedRegions, |
691 | const LocationContext *LCtx, const CallEvent *Call) const { |
692 | if (Call) { |
693 | |
694 | |
695 | |
696 | |
697 | |
698 | const MemRegion *ThisRegion = nullptr; |
699 | if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) |
700 | ThisRegion = IC->getCXXThisVal().getAsRegion(); |
701 | |
702 | |
703 | |
704 | |
705 | for (const auto *Region : RequestedRegions) { |
706 | if (ThisRegion != Region) { |
707 | if (llvm::find(InvalidatedRegions, Region) != |
708 | std::end(InvalidatedRegions)) { |
709 | State = removeFromState(State, Region); |
710 | } |
711 | } |
712 | } |
713 | } else { |
714 | |
715 | |
716 | for (const auto *Region : InvalidatedRegions) |
717 | State = removeFromState(State, Region->getBaseRegion()); |
718 | } |
719 | |
720 | return State; |
721 | } |
722 | |
723 | void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, |
724 | const char *NL, const char *Sep) const { |
725 | |
726 | TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); |
727 | |
728 | if (!RS.isEmpty()) { |
729 | Out << Sep << "Moved-from objects :" << NL; |
730 | for (auto I: RS) { |
731 | I.first->dumpToStream(Out); |
732 | if (I.second.isMoved()) |
733 | Out << ": moved"; |
734 | else |
735 | Out << ": moved and reported"; |
736 | Out << NL; |
737 | } |
738 | } |
739 | } |
740 | void ento::registerMoveChecker(CheckerManager &mgr) { |
741 | MoveChecker *chk = mgr.registerChecker<MoveChecker>(); |
742 | chk->setAggressiveness( |
743 | mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn", |
744 | "KnownsAndLocals"), mgr); |
745 | } |
746 | |
747 | bool ento::shouldRegisterMoveChecker(const LangOptions &LO) { |
748 | return true; |
749 | } |
750 | |