25#include "llvm/ADT/StringSet.h"
33 enum Kind { Moved, Reported } K;
34 RegionState(Kind InK) : K(InK) {}
37 bool isReported()
const {
return K == Reported; }
38 bool isMoved()
const {
return K == Moved; }
40 static RegionState getReported() {
return RegionState(Reported); }
41 static RegionState getMoved() {
return RegionState(Moved); }
43 bool operator==(
const RegionState &
X)
const {
return K ==
X.K; }
44 void Profile(llvm::FoldingSetNodeID &ID)
const {
ID.AddInteger(K); }
50 :
public Checker<check::PreCall, check::PostCall,
51 check::DeadSymbols, check::RegionChanges> {
63 const char *NL,
const char *Sep)
const override;
66 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
67 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
69 enum AggressivenessKind {
72 AK_KnownsAndLocals = 1,
77 static bool misuseCausesCrash(MisuseKind MK) {
78 return MK == MK_Dereference;
85 StdObjectKind StdKind;
91 const llvm::StringSet<> StdSmartPtrClasses = {
102 const llvm::StringSet<> StdSafeClasses = {
116 bool shouldBeTracked(ObjectKind OK)
const {
130 return (Aggressiveness == AK_All) ||
131 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
132 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
137 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK)
const {
140 return shouldBeTracked(OK) &&
141 ((Aggressiveness == AK_All) ||
142 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
143 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
152 void explainObject(llvm::raw_ostream &OS,
const MemRegion *MR,
155 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &
Set)
const;
159 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
161 : Chk(Chk), Region(R), RD(RD), MK(MK),
Found(
false) {}
163 void Profile(llvm::FoldingSetNodeID &ID)
const override {
166 ID.AddPointer(Region);
178 const MoveChecker &Chk;
188 AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
193 llvm::StringSwitch<AggressivenessKind>(Str)
194 .Case(
"KnownsOnly", AK_KnownsOnly)
195 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
197 .Default(AK_Invalid);
199 if (Aggressiveness == AK_Invalid)
201 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
222 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
237 const RegionState *RS = State->get<TrackedRegionMap>(Region);
238 return RS && (RS->isMoved() || RS->isReported());
249 for (
auto &
E : State->get<TrackedRegionMap>()) {
250 if (
E.first->isSubRegionOf(Region))
251 State = State->remove<TrackedRegionMap>(
E.first);
258 for (
auto &
E : State->get<TrackedRegionMap>()) {
266 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
276MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
285 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
286 const RegionState *TrackedObjectPrev =
287 StatePrev->get<TrackedRegionMap>(Region);
290 if (TrackedObjectPrev && TrackedObject)
300 llvm::raw_svector_ostream OS(Str);
302 ObjectKind OK = Chk.classifyObject(Region, RD);
303 switch (OK.StdKind) {
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";
318 Chk.explainObject(OS, Region, RD, MK);
323 Chk.explainObject(OS, Region, RD, MK);
324 OS <<
" is left in a valid but unspecified state after move";
331 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
343 if (!State->get<TrackedRegionMap>(Region))
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);
360 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
363 if (!RS || !shouldWarnAbout(OK, MK)
364 || isInMoveSafeContext(
C.getLocationContext())) {
366 C.addTransition(State);
374 if (misuseCausesCrash(MK)) {
375 C.generateSink(State,
C.getPredecessor());
377 C.addTransition(State);
388 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
389 C.addTransition(State, N);
395 MisuseKind MK)
const {
396 if (
ExplodedNode *N = misuseCausesCrash(MK) ?
C.generateErrorNode()
397 :
C.generateNonFatalErrorNode()) {
400 const ExplodedNode *MoveNode = getMoveLocation(N, Region,
C);
408 llvm::raw_svector_ostream OS(Str);
411 OS <<
"Method called on moved-from object";
412 explainObject(OS, Region, RD, MK);
415 OS <<
"Moved-from object";
416 explainObject(OS, Region, RD, MK);
420 OS <<
"Moved-from object";
421 explainObject(OS, Region, RD, MK);
425 OS <<
"Dereference of null smart pointer";
426 explainObject(OS, Region, RD, MK);
430 auto R = std::make_unique<PathSensitiveBugReport>(
431 BT, OS.str(), N, LocUsedForUniqueing,
433 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
434 C.emitReport(std::move(R));
442 const auto *AFC = dyn_cast<AnyFunctionCall>(&
Call);
447 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
454 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
455 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
458 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
461 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
466 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&
Call);
467 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
470 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
471 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
477 AFC->getArgExpr(0)->isPRValue())
481 if (State->get<TrackedRegionMap>(ArgRegion))
485 ObjectKind OK = classifyObject(ArgRegion, RD);
486 if (shouldBeTracked(OK)) {
488 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
489 C.addTransition(State);
492 assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
495bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
497 if (
const auto *ConversionDec =
498 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
499 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
507 (MethodDec->
getName().lower() ==
"empty" ||
508 MethodDec->
getName().lower() ==
"isempty"));
511bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
514 if (MethodDec->
hasAttr<ReinitializesAttr>())
517 std::string MethodName = MethodDec->
getName().lower();
520 if (MethodName ==
"assign" || MethodName ==
"clear" ||
521 MethodName ==
"destroy" || MethodName ==
"reset" ||
522 MethodName ==
"resize" || MethodName ==
"shrink")
530bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
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()) ||
539 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
546 const llvm::StringSet<> &
Set)
const {
551MoveChecker::ObjectKind
552MoveChecker::classifyObject(
const MemRegion *MR,
559 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
563 return { IsLocal, SK_NonStd };
565 if (belongsTo(RD, StdSmartPtrClasses))
566 return { IsLocal, SK_SmartPtr };
568 if (belongsTo(RD, StdSafeClasses))
569 return { IsLocal, SK_Safe };
571 return { IsLocal, SK_Unsafe };
574void MoveChecker::explainObject(llvm::raw_ostream &OS,
const MemRegion *MR,
580 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
581 OS <<
" '" << RegionDecl->getDeclName() <<
"'";
584 ObjectKind OK = classifyObject(MR, RD);
585 switch (OK.StdKind) {
590 if (MK != MK_Dereference)
608 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&
Call)) {
610 auto CtorDec = CC->getDecl();
612 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
613 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
615 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
616 modelUse(State, ArgRegion, RD, MK,
C);
621 const auto IC = dyn_cast<CXXInstanceCall>(&
Call);
625 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
630 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
635 if (isa<CXXDestructorDecl>(MethodDecl))
642 if (isStateResetMethod(MethodDecl)) {
644 C.addTransition(State);
648 if (isMoveSafeMethod(MethodDecl))
654 if (MethodDecl->isOverloadedOperator()) {
657 if (OOK == OO_Equal) {
662 if (MethodDecl->isCopyAssignmentOperator() ||
663 MethodDecl->isMoveAssignmentOperator()) {
664 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
666 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
667 modelUse(State, ArgRegion, RD, MK,
C);
670 C.addTransition(State);
674 if (OOK == OO_Star || OOK == OO_Arrow) {
675 modelUse(State, ThisRegion, RD, MK_Dereference,
C);
680 modelUse(State, ThisRegion, RD, MK_FunCall,
C);
683void MoveChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
686 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
687 for (
auto E : TrackedRegions) {
693 State = State->remove<TrackedRegionMap>(Region);
696 C.addTransition(State);
711 if (
const auto *IC = dyn_cast<CXXInstanceCall>(
Call))
712 ThisRegion = IC->getCXXThisVal().getAsRegion();
717 for (
const auto *Region : RequestedRegions) {
718 if (ThisRegion != Region &&
719 llvm::is_contained(InvalidatedRegions, Region))
725 for (
const auto *Region : InvalidatedRegions)
733 const char *NL,
const char *Sep)
const {
735 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
738 Out << Sep <<
"Moved-from objects :" << NL;
740 I.first->dumpToStream(Out);
741 if (I.second.isMoved())
744 Out <<
": moved and reported";
751 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.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
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)