24#include "llvm/ADT/StringSet.h"
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); }
49 :
public Checker<check::PreCall, check::PostCall,
50 check::DeadSymbols, check::RegionChanges> {
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 {
71 AK_KnownsAndLocals = 1,
76 static bool misuseCausesCrash(MisuseKind MK) {
77 return MK == MK_Dereference;
84 StdObjectKind StdKind;
90 const llvm::StringSet<> StdSmartPtrClasses = {
101 const llvm::StringSet<> StdSafeClasses = {
115 bool shouldBeTracked(ObjectKind OK)
const {
129 return (Aggressiveness == AK_All) ||
130 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
131 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
136 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK)
const {
139 return shouldBeTracked(OK) &&
140 ((Aggressiveness == AK_All) ||
141 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
142 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
151 void explainObject(llvm::raw_ostream &OS,
const MemRegion *MR,
154 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &
Set)
const;
158 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
160 : Chk(Chk), Region(R), RD(RD), MK(MK),
Found(
false) {}
162 void Profile(llvm::FoldingSetNodeID &ID)
const override {
165 ID.AddPointer(Region);
177 const MoveChecker &Chk;
187 AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
192 llvm::StringSwitch<AggressivenessKind>(Str)
193 .Case(
"KnownsOnly", AK_KnownsOnly)
194 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
196 .Default(AK_Invalid);
198 if (Aggressiveness == AK_Invalid)
200 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
221 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
236 const RegionState *RS = State->get<TrackedRegionMap>(Region);
237 return RS && (RS->isMoved() || RS->isReported());
248 for (
auto &
E : State->get<TrackedRegionMap>()) {
249 if (
E.first->isSubRegionOf(Region))
250 State = State->remove<TrackedRegionMap>(
E.first);
257 for (
auto &
E : State->get<TrackedRegionMap>()) {
265 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
275MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
284 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
285 const RegionState *TrackedObjectPrev =
286 StatePrev->get<TrackedRegionMap>(Region);
289 if (TrackedObjectPrev && TrackedObject)
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";
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";
330 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
342 if (!State->get<TrackedRegionMap>(Region))
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);
359 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
362 if (!RS || !shouldWarnAbout(OK, MK)
363 || isInMoveSafeContext(
C.getLocationContext())) {
365 C.addTransition(State);
373 if (misuseCausesCrash(MK)) {
374 C.generateSink(State,
C.getPredecessor());
376 C.addTransition(State);
387 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
388 C.addTransition(State, N);
394 MisuseKind MK)
const {
395 if (
ExplodedNode *N = misuseCausesCrash(MK) ?
C.generateErrorNode()
396 :
C.generateNonFatalErrorNode()) {
399 const ExplodedNode *MoveNode = getMoveLocation(N, Region,
C);
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,
432 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
433 C.emitReport(std::move(R));
441 const auto *AFC = dyn_cast<AnyFunctionCall>(&
Call);
446 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
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();
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)
476 AFC->getArgExpr(0)->isPRValue())
480 if (State->get<TrackedRegionMap>(ArgRegion))
484 ObjectKind OK = classifyObject(ArgRegion, RD);
485 if (shouldBeTracked(OK)) {
487 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
488 C.addTransition(State);
491 assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
494bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
496 if (
const auto *ConversionDec =
497 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
498 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
506 (MethodDec->
getName().lower() ==
"empty" ||
507 MethodDec->
getName().lower() ==
"isempty"));
510bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
513 if (MethodDec->
hasAttr<ReinitializesAttr>())
516 std::string MethodName = MethodDec->
getName().lower();
519 if (MethodName ==
"assign" || MethodName ==
"clear" ||
520 MethodName ==
"destroy" || MethodName ==
"reset" ||
521 MethodName ==
"resize" || MethodName ==
"shrink")
529bool 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()) ||
538 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
545 const llvm::StringSet<> &
Set)
const {
550MoveChecker::ObjectKind
551MoveChecker::classifyObject(
const MemRegion *MR,
558 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
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 };
573void MoveChecker::explainObject(llvm::raw_ostream &OS,
const MemRegion *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)
607 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&
Call)) {
609 auto CtorDec = CC->getDecl();
611 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
612 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
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();
629 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
634 if (isa<CXXDestructorDecl>(MethodDecl))
641 if (isStateResetMethod(MethodDecl)) {
643 C.addTransition(State);
647 if (isMoveSafeMethod(MethodDecl))
653 if (MethodDecl->isOverloadedOperator()) {
656 if (OOK == OO_Equal) {
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);
682void MoveChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
685 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
686 for (
auto E : TrackedRegions) {
692 State = State->remove<TrackedRegionMap>(Region);
695 C.addTransition(State);
710 if (
const auto *IC = dyn_cast<CXXInstanceCall>(
Call))
711 ThisRegion = IC->getCXXThisVal().getAsRegion();
716 for (
const auto *Region : RequestedRegions) {
717 if (ThisRegion != Region &&
718 llvm::is_contained(InvalidatedRegions, Region))
724 for (
const auto *Region : InvalidatedRegions)
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";
750 chk->setAggressiveness(
Defines the clang::Expr interface and subclasses for C++ expressions.
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.
Represents a C++ struct/union/class.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
bool isStdNamespace() const
DeclContext * getDeclContext()
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+".
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
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.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
std::string getQualifiedNameAsString() const
Stmt - This represents one statement.
The base class of the type hierarchy.
bool isBooleanType() const
bool isRValueReferenceType() const
bool isVoidPointerType() const
const SourceManager & getSourceManager() const
BugReporterVisitors are used to add custom diagnostics along a path.
Represents an abstract call to a function or method along a particular path.
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
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.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
const RegionTy * getAs() const
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...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
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.
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.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)