;
33 enum Kind{ Moved, Reported } K;
34RegionState(Kind InK) : K(InK) {}
37 boolisReported()
const{
returnK == Reported; }
38 boolisMoved()
const{
returnK == Moved; }
40 staticRegionState getReported() {
returnRegionState(Reported); }
41 staticRegionState getMoved() {
returnRegionState(Moved); }
43 bool operator==(
constRegionState &
X)
const{
returnK ==
X.K; }
44 voidProfile(llvm::FoldingSetNodeID &ID)
const{
ID.AddInteger(K); }
50:
public Checker<check::PreCall, check::PostCall,
51check::DeadSymbols, check::RegionChanges> {
63 const char*NL,
const char*Sep)
const override;
66 enumMisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
67 enumStdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
69 enumAggressivenessKind {
72AK_KnownsAndLocals = 1,
77 static boolmisuseCausesCrash(MisuseKind MK) {
78 returnMK == MK_Dereference;
85StdObjectKind StdKind;
91 constllvm::StringSet<> StdSmartPtrClasses = {
102 constllvm::StringSet<> StdSafeClasses = {
116 boolshouldBeTracked(ObjectKind OK)
const{
130 return(Aggressiveness == AK_All) ||
131(Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
132OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
137 boolshouldWarnAbout(ObjectKind OK, MisuseKind MK)
const{
140 returnshouldBeTracked(OK) &&
141((Aggressiveness == AK_All) ||
142(Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
143OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
152 voidexplainObject(llvm::raw_ostream &OS,
const MemRegion*MR,
155 boolbelongsTo(
const CXXRecordDecl*RD,
constllvm::StringSet<> &
Set)
const;
159MovedBugVisitor(
constMoveChecker &Chk,
const MemRegion*R,
161: Chk(Chk), Region(R), RD(RD), MK(MK),
Found(
false) {}
163 voidProfile(llvm::FoldingSetNodeID &ID)
const override{
165 ID.AddPointer(&
X);
166 ID.AddPointer(Region);
178 constMoveChecker &Chk;
188AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
193llvm::StringSwitch<AggressivenessKind>(Str)
194.Case(
"KnownsOnly", AK_KnownsOnly)
195.Case(
"KnownsAndLocals", AK_KnownsAndLocals)
196.Case(
"All", AK_All)
197.Default(AK_Invalid);
199 if(Aggressiveness == AK_Invalid)
201 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
222 boolisStateResetMethod(
const CXXMethodDecl*MethodDec)
const;
223 boolisMoveSafeMethod(
const CXXMethodDecl*MethodDec)
const;
237 constRegionState *RS = State->get<TrackedRegionMap>(Region);
238 returnRS && (RS->isMoved() || RS->isReported());
249 for(
auto&
E: State->get<TrackedRegionMap>()) {
250 if(
E.first->isSubRegionOf(Region))
251State = 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 constRegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
286 constRegionState *TrackedObjectPrev =
287StatePrev->get<TrackedRegionMap>(Region);
290 if(TrackedObjectPrev && TrackedObject)
300llvm::raw_svector_ostream OS(Str);
302ObjectKind OK = Chk.classifyObject(Region, RD);
303 switch(OK.StdKind) {
305 if(MK == MK_Dereference) {
306OS <<
"Smart pointer";
307Chk.explainObject(OS, Region, RD, MK);
308OS <<
" is reset to null when moved from";
318Chk.explainObject(OS, Region, RD, MK);
323Chk.explainObject(OS, Region, RD, MK);
324OS <<
" is left in a valid but unspecified state after move";
331 returnstd::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
343 if(!State->get<TrackedRegionMap>(Region))
354assert(!
C.isDifferent() &&
"No transitions should have been made by now");
355 constRegionState *RS = State->get<TrackedRegionMap>(Region);
356ObjectKind 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);
388State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
389 C.addTransition(State, N);
395MisuseKind MK)
const{
396 if(
ExplodedNode*N = misuseCausesCrash(MK) ?
C.generateErrorNode()
397:
C.generateNonFatalErrorNode()) {
400 const ExplodedNode*MoveNode = getMoveLocation(N, Region,
C);
408llvm::raw_svector_ostream OS(Str);
411OS <<
"Method called on moved-from object";
412explainObject(OS, Region, RD, MK);
415OS <<
"Moved-from object";
416explainObject(OS, Region, RD, MK);
420OS <<
"Moved-from object";
421explainObject(OS, Region, RD, MK);
425OS <<
"Dereference of null smart pointer";
426explainObject(OS, Region, RD, MK);
430 autoR = std::make_unique<PathSensitiveBugReport>(
431BT, OS.str(), N, LocUsedForUniqueing,
433R->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 autoMethodDecl = 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 autoArgRegion = 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)
477AFC->getArgExpr(0)->isPRValue())
481 if(State->get<TrackedRegionMap>(ArgRegion))
485ObjectKind OK = classifyObject(ArgRegion, RD);
486 if(shouldBeTracked(OK)) {
488State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
489 C.addTransition(State);
492assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
495boolMoveChecker::isMoveSafeMethod(
const CXXMethodDecl*MethodDec)
const{
497 if(
const auto*ConversionDec =
498dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
499 const Type*Tp = ConversionDec->getConversionType().getTypePtrOrNull();
507(MethodDec->
getName().lower() ==
"empty"||
508MethodDec->
getName().lower() ==
"isempty"));
511boolMoveChecker::isStateResetMethod(
const CXXMethodDecl*MethodDec)
const{
514 if(MethodDec->
hasAttr<ReinitializesAttr>())
517std::string MethodName = MethodDec->
getName().lower();
520 if(MethodName ==
"assign"|| MethodName ==
"clear"||
521MethodName ==
"destroy"|| MethodName ==
"reset"||
522MethodName ==
"resize"|| MethodName ==
"shrink")
530boolMoveChecker::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()) ||
539isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
546 constllvm::StringSet<> &
Set)
const{
551MoveChecker::ObjectKind
552MoveChecker::classifyObject(
const MemRegion*MR,
559isa_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 };
574voidMoveChecker::explainObject(llvm::raw_ostream &OS,
const MemRegion*MR,
580 const auto*RegionDecl = cast<NamedDecl>(DR->getDecl());
581OS <<
" '"<< RegionDecl->getDeclName() <<
"'";
584ObjectKind OK = classifyObject(MR, RD);
585 switch(OK.StdKind) {
590 if(MK != MK_Dereference)
608 if(
const auto*CC = dyn_cast<CXXConstructorCall>(&
Call)) {
610 autoCtorDec = CC->getDecl();
612 if(CtorDec && CtorDec->isCopyOrMoveConstructor()) {
613 const MemRegion*ArgRegion = CC->getArgSVal(0).getAsRegion();
615MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
616modelUse(State, ArgRegion, RD, MK,
C);
621 const autoIC = dyn_cast<CXXInstanceCall>(&
Call);
625 const MemRegion*ThisRegion = IC->getCXXThisVal().getAsRegion();
630 const autoMethodDecl = 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() ||
663MethodDecl->isMoveAssignmentOperator()) {
664 const MemRegion*ArgRegion = IC->getArgSVal(0).getAsRegion();
666MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
667modelUse(State, ArgRegion, RD, MK,
C);
670 C.addTransition(State);
674 if(OOK == OO_Star || OOK == OO_Arrow) {
675modelUse(State, ThisRegion, RD, MK_Dereference,
C);
680modelUse(State, ThisRegion, RD, MK_FunCall,
C);
683voidMoveChecker::checkDeadSymbols(
SymbolReaper&SymReaper,
686TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
687 for(
auto E: TrackedRegions) {
693State = State->remove<TrackedRegionMap>(Region);
696 C.addTransition(State);
711 if(
const auto*IC = dyn_cast<CXXInstanceCall>(
Call))
712ThisRegion = IC->getCXXThisVal().getAsRegion();
717 for(
const auto*Region : RequestedRegions) {
718 if(ThisRegion != Region &&
719llvm::is_contained(InvalidatedRegions, Region))
725 for(
const auto*Region : InvalidatedRegions)
733 const char*NL,
const char*Sep)
const{
735TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
738Out << Sep <<
"Moved-from objects :"<< NL;
740I.first->dumpToStream(Out);
741 if(I.second.isMoved())
744Out <<
": moved and reported";
751chk->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)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4