24 using namespace clang;
31 enum Kind { Moved, Reported } K;
32 RegionState(
Kind InK) : K(InK) {}
35 bool isReported()
const {
return K == Reported; }
36 bool isMoved()
const {
return K == Moved; }
38 static RegionState getReported() {
return RegionState(Reported); }
39 static RegionState getMoved() {
return RegionState(Moved); }
41 bool operator==(
const RegionState &
X)
const {
return K == X.K; }
42 void Profile(llvm::FoldingSetNodeID &
ID)
const { ID.AddInteger(K); }
45 class MisusedMovedObjectChecker
46 :
public Checker<check::PreCall, check::PostCall, check::EndFunction,
47 check::DeadSymbols, check::RegionChanges> {
60 const char *NL,
const char *Sep)
const override;
63 enum MisuseKind {MK_FunCall, MK_Copy, MK_Move};
68 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
71 ID.AddPointer(Region);
74 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
85 mutable std::unique_ptr<BugType> BT;
89 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
104 for (
auto &E : State->get<TrackedRegionMap>()) {
105 if (E.first->isSubRegionOf(Region))
106 State = State->remove<TrackedRegionMap>(E.first);
113 for (
auto &E : State->get<TrackedRegionMap>()) {
120 std::shared_ptr<PathDiagnosticPiece>
121 MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
131 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
132 const RegionState *TrackedObjectPrev =
133 StatePrev->get<TrackedRegionMap>(Region);
136 if (TrackedObjectPrev && TrackedObject)
145 std::string ObjectName;
146 if (
const auto DecReg = Region->getAs<
DeclRegion>()) {
147 const auto *RegionDecl = dyn_cast<
NamedDecl>(DecReg->getDecl());
150 std::string InfoText;
151 if (ObjectName !=
"")
152 InfoText =
"'" + ObjectName +
"' became 'moved-from' here";
154 InfoText =
"Became 'moved-from' here";
159 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText,
true);
162 const ExplodedNode *MisusedMovedObjectChecker::getMoveLocation(
170 if (!State->get<TrackedRegionMap>(Region))
181 MisuseKind MK)
const {
184 BT.reset(
new BugType(
this,
"Usage of a 'moved-from' object",
185 "C++ move semantics"));
189 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
196 std::string ErrorMessage;
199 ErrorMessage =
"Method call on a 'moved-from' object";
202 ErrorMessage =
"Copying a 'moved-from' object";
205 ErrorMessage =
"Moving a 'moved-from' object";
209 const auto *RegionDecl = dyn_cast<
NamedDecl>(DecReg->getDecl());
214 llvm::make_unique<BugReport>(*BT, ErrorMessage, N, LocUsedForUniqueing,
216 R->addVisitor(llvm::make_unique<MovedBugVisitor>(Region));
225 void MisusedMovedObjectChecker::checkEndFunction(
const ReturnStmt *RS,
228 TrackedRegionMapTy Objects =
State->get<TrackedRegionMap>();
229 if (Objects.isEmpty())
234 const auto LD = dyn_cast_or_null<FunctionDecl>(LC->getDecl());
237 llvm::SmallSet<const MemRegion *, 8> InvalidRegions;
239 for (
auto Param : LD->parameters()) {
240 auto Type = Param->getType().getTypePtrOrNull();
244 InvalidRegions.insert(
State->getLValue(Param, LC).getAsRegion());
248 if (InvalidRegions.empty())
251 for (
const auto &E :
State->get<TrackedRegionMap>()) {
252 if (InvalidRegions.count(E.first->getBaseRegion()))
253 State =
State->remove<TrackedRegionMap>(E.first);
259 void MisusedMovedObjectChecker::checkPostCall(
const CallEvent &Call,
266 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
272 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
276 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
279 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
282 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
287 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
289 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
290 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
296 AFC->getArgExpr(0)->isRValue())
300 if (State->get<TrackedRegionMap>(ArgRegion))
303 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
307 bool MisusedMovedObjectChecker::isMoveSafeMethod(
310 if (
const auto *ConversionDec =
311 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
312 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
320 (MethodDec->
getName().lower() ==
"empty" ||
321 MethodDec->
getName().lower() ==
"isempty"))
327 bool MisusedMovedObjectChecker::isStateResetMethod(
330 std::string MethodName = MethodDec->
getName().lower();
331 if (MethodName ==
"reset" || MethodName ==
"clear" ||
332 MethodName ==
"destroy")
340 bool MisusedMovedObjectChecker::isInMoveSafeContext(
343 const auto *CtxDec = LC->
getDecl();
344 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
345 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
346 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
347 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
348 (MethodDec && MethodDec->isOverloadedOperator() &&
349 MethodDec->getOverloadedOperator() == OO_Equal) ||
350 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
356 void MisusedMovedObjectChecker::checkPreCall(
const CallEvent &Call,
366 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
368 auto CtorDec = CC->getDecl();
370 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
371 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
372 const RegionState *ArgState = State->get<TrackedRegionMap>(ArgRegion);
373 if (ArgState && ArgState->isMoved()) {
374 if (!isInMoveSafeContext(LC)) {
375 if(CtorDec->isMoveConstructor())
376 N = reportBug(ArgRegion, Call, C, MK_Move);
378 N = reportBug(ArgRegion, Call, C, MK_Copy);
379 State = State->set<TrackedRegionMap>(ArgRegion,
380 RegionState::getReported());
392 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
396 if (dyn_cast_or_null<CXXDestructorDecl>(Call.
getDecl())) {
402 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
406 bool OperatorEq = MethodDecl->isOverloadedOperator() &&
407 MethodDecl->getOverloadedOperator() == OO_Equal;
412 if (MethodDecl->isCopyAssignmentOperator() ||
413 MethodDecl->isMoveAssignmentOperator()) {
414 const RegionState *ArgState =
415 State->get<TrackedRegionMap>(IC->getArgSVal(0).getAsRegion());
416 if (ArgState && ArgState->isMoved() && !isInMoveSafeContext(LC)) {
417 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
418 if(MethodDecl->isMoveAssignmentOperator())
419 N = reportBug(ArgRegion, Call, C, MK_Move);
421 N = reportBug(ArgRegion, Call, C, MK_Copy);
423 State->set<TrackedRegionMap>(ArgRegion, RegionState::getReported());
435 dyn_cast<CXXBaseObjectRegion>(ThisRegion))
436 ThisRegion = BR->getSuperRegion();
438 if (isMoveSafeMethod(MethodDecl))
441 if (isStateResetMethod(MethodDecl)) {
448 const RegionState *ThisState = State->get<TrackedRegionMap>(ThisRegion);
449 if (!(ThisState && ThisState->isMoved()))
456 if (isInMoveSafeContext(LC))
459 N = reportBug(ThisRegion, Call, C, MK_FunCall);
460 State = State->set<TrackedRegionMap>(ThisRegion, RegionState::getReported());
464 void MisusedMovedObjectChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
467 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
468 for (TrackedRegionMapTy::value_type E : TrackedRegions) {
474 State = State->remove<TrackedRegionMap>(Region);
488 if (
const auto *IC = dyn_cast_or_null<CXXInstanceCall>(Call)) {
489 ThisRegion = IC->getCXXThisVal().getAsRegion();
493 E = ExplicitRegions.end();
495 const auto *Region = *I;
496 if (ThisRegion != Region) {
504 void MisusedMovedObjectChecker::printState(raw_ostream &Out,
507 const char *Sep)
const {
509 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
512 Out << Sep <<
"Moved-from objects :" << NL;
514 I.first->dumpToStream(Out);
515 if (I.second.isMoved())
518 Out <<
": moved and reported";
523 void ento::registerMisusedMovedObjectChecker(
CheckerManager &mgr) {
MemRegion - The root abstract class for all memory regions.
bool operator==(CanQual< T > x, CanQual< U > y)
Stmt - This represents one statement.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
The base class of the type hierarchy.
const ProgramStateRef & getState() const
Represents a C++ constructor within a class.
Defines the clang::Expr interface and subclasses for C++ expressions.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
bool isReferenceType() const
bool isLiveRegion(const MemRegion *region)
const LocationContext * getLocationContext() const
const LocationContext * getParent() const
Represents a non-static C++ member function call, no matter how it is written.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region)
const RegionTy * getAs() const
Represents a call to any sort of function that might have a FunctionDecl.
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
bool isIdentifier() const
Predicate functions for querying what type of name this is.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
CHECKER * registerChecker(AT... Args)
Used to register checkers.
bool isVoidPointerType() const
void emitReport(std::unique_ptr< BugReport > R)
Emit the diagnostics report.
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
static bool isAnyBaseRegionReported(ProgramStateRef State, const MemRegion *Region)
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Represents a static or instance method of a struct/union/class.
A class responsible for cleaning up unused symbols.
Dataflow Directional Tag Classes.
bool isBooleanType() const
Represents an abstract call to a function or method along a particular path.
const Decl * getDecl() const
const ProgramStateRef & getState() const
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
pred_iterator pred_begin()
SourceManager & getSourceManager()
const MemRegion * getBaseRegion() const
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
bool isPointerType() const
This represents a decl that may have a name.
This class provides an interface through which checkers can create individual bug reports...
const LocationContext * getLocationContext() const
SourceManager & getSourceManager()