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> {
65 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
68 ID.AddPointer(Region);
71 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
82 mutable std::unique_ptr<BugType> BT;
86 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
102 State = State->remove<TrackedRegionMap>(Region);
103 for (
auto &
E : State->get<TrackedRegionMap>()) {
104 if (
E.first->isSubRegionOf(Region))
105 State = State->remove<TrackedRegionMap>(
E.first);
112 for (
auto &
E : State->get<TrackedRegionMap>()) {
119 std::shared_ptr<PathDiagnosticPiece>
120 MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
130 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
131 const RegionState *TrackedObjectPrev =
132 StatePrev->get<TrackedRegionMap>(Region);
135 if (TrackedObjectPrev && TrackedObject)
144 std::string ObjectName;
145 if (
const auto DecReg = Region->getAs<
DeclRegion>()) {
146 const auto *RegionDecl = dyn_cast<
NamedDecl>(DecReg->getDecl());
149 std::string InfoText;
150 if (ObjectName !=
"")
151 InfoText =
"'" + ObjectName +
"' became 'moved-from' here";
153 InfoText =
"Became 'moved-from' here";
158 return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText,
true);
161 const ExplodedNode *MisusedMovedObjectChecker::getMoveLocation(
169 if (!State->get<TrackedRegionMap>(Region))
180 bool isCopy =
false)
const {
183 BT.reset(
new BugType(
this,
"Usage of a 'moved-from' object",
184 "C++ move semantics"));
188 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
195 std::string ErrorMessage;
197 ErrorMessage =
"Copying a 'moved-from' object";
199 ErrorMessage =
"Method call on a 'moved-from' object";
201 const auto *RegionDecl = dyn_cast<
NamedDecl>(DecReg->getDecl());
206 llvm::make_unique<BugReport>(*BT, ErrorMessage, N, LocUsedForUniqueing,
208 R->addVisitor(llvm::make_unique<MovedBugVisitor>(Region));
217 void MisusedMovedObjectChecker::checkEndFunction(
CheckerContext &C)
const {
219 TrackedRegionMapTy Objects = State->get<TrackedRegionMap>();
220 if (Objects.isEmpty())
225 const auto LD = dyn_cast_or_null<FunctionDecl>(LC->getDecl());
228 llvm::SmallSet<const MemRegion *, 8> InvalidRegions;
230 for (
auto Param : LD->parameters()) {
231 auto Type = Param->getType().getTypePtrOrNull();
235 InvalidRegions.insert(State->getLValue(Param, LC).getAsRegion());
239 if (InvalidRegions.empty())
242 for (
const auto &
E : State->get<TrackedRegionMap>()) {
243 if (InvalidRegions.count(
E.first->getBaseRegion()))
244 State = State->remove<TrackedRegionMap>(
E.first);
250 void MisusedMovedObjectChecker::checkPostCall(
const CallEvent &Call,
257 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
263 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
267 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
270 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
273 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
278 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
280 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
281 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
287 AFC->getArgExpr(0)->isRValue())
291 if (State->get<TrackedRegionMap>(ArgRegion))
294 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
298 bool MisusedMovedObjectChecker::isMoveSafeMethod(
301 if (
const auto *ConversionDec =
302 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
303 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
311 (MethodDec->
getName().lower() ==
"empty" ||
312 MethodDec->
getName().lower() ==
"isempty"))
318 bool MisusedMovedObjectChecker::isStateResetMethod(
321 std::string MethodName = MethodDec->
getName().lower();
322 if (MethodName ==
"reset" || MethodName ==
"clear" ||
323 MethodName ==
"destroy")
331 bool MisusedMovedObjectChecker::isInMoveSafeContext(
334 const auto *CtxDec = LC->
getDecl();
335 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
336 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
337 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
338 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
339 (MethodDec && MethodDec->isOverloadedOperator() &&
340 MethodDec->getOverloadedOperator() == OO_Equal) ||
341 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
347 void MisusedMovedObjectChecker::checkPreCall(
const CallEvent &Call,
357 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
359 auto CtorDec = CC->getDecl();
361 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
362 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
363 const RegionState *ArgState = State->get<TrackedRegionMap>(ArgRegion);
364 if (ArgState && ArgState->isMoved()) {
365 if (!isInMoveSafeContext(LC)) {
366 N = reportBug(ArgRegion, Call, C,
true);
367 State = State->set<TrackedRegionMap>(ArgRegion,
368 RegionState::getReported());
380 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
381 if (dyn_cast_or_null<CXXDestructorDecl>(Call.
getDecl())) {
387 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
391 bool OperatorEq = MethodDecl->isOverloadedOperator() &&
392 MethodDecl->getOverloadedOperator() == OO_Equal;
397 if (MethodDecl->isCopyAssignmentOperator() ||
398 MethodDecl->isMoveAssignmentOperator()) {
399 const RegionState *ArgState =
400 State->get<TrackedRegionMap>(IC->getArgSVal(0).getAsRegion());
401 if (ArgState && ArgState->isMoved() && !isInMoveSafeContext(LC)) {
402 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
403 N = reportBug(ArgRegion, Call, C,
true);
405 State->set<TrackedRegionMap>(ArgRegion, RegionState::getReported());
413 if (isMoveSafeMethod(MethodDecl))
416 if (isStateResetMethod(MethodDecl)) {
417 State = State->remove<TrackedRegionMap>(ThisRegion);
423 const RegionState *ThisState = State->get<TrackedRegionMap>(ThisRegion);
424 if (!(ThisState && ThisState->isMoved()))
431 if (isInMoveSafeContext(LC))
434 N = reportBug(ThisRegion, Call, C);
435 State = State->set<TrackedRegionMap>(ThisRegion, RegionState::getReported());
439 void MisusedMovedObjectChecker::checkDeadSymbols(
SymbolReaper &SymReaper,
442 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
443 for (TrackedRegionMapTy::value_type
E : TrackedRegions) {
449 State = State->remove<TrackedRegionMap>(Region);
463 if (
const auto *IC = dyn_cast_or_null<CXXInstanceCall>(Call)) {
464 ThisRegion = IC->getCXXThisVal().getAsRegion();
468 E = ExplicitRegions.end();
470 const auto *Region = *
I;
471 if (ThisRegion != Region) {
479 void ento::registerMisusedMovedObjectChecker(
CheckerManager &mgr) {
StringRef getName() const
getName - Get the name of identifier for this declaration as a StringRef.
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).
bool isVoidPointerType() const
const RegionTy * getAs() const
The base class of the type hierarchy.
bool isBooleanType() const
Represents a C++ constructor within a class.
const MemRegion * getBaseRegion() const
Defines the clang::Expr interface and subclasses for C++ expressions.
bool isIdentifier() const
Predicate functions for querying what type of name this is.
bool isReferenceType() const
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
bool isLiveRegion(const MemRegion *region)
std::string getNameAsString() const
getNameAsString - Get a human-readable name for the declaration, even if it is one of the special kin...
detail::InMemoryDirectory::const_iterator I
const LocationContext * getLocationContext() 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 ProgramStateRef & getState() const
const ProgramStateRef & getState() const
Represents a call to any sort of function that might have a FunctionDecl.
DeclarationName getDeclName() const
getDeclName - Get the actual, stored name of the declaration, which may be a special name...
ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
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)
CHECKER * registerChecker()
Used to register checkers.
Represents a static or instance method of a struct/union/class.
const Decl * getDecl() const
A class responsible for cleaning up unused symbols.
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
const LocationContext * getParent() const
detail::InMemoryDirectory::const_iterator E
Represents an abstract call to a function or method along a particular path.
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
pred_iterator pred_begin()
SourceManager & getSourceManager()
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
NamedDecl - This represents a decl with a name.
This class provides an interface through which checkers can create individual bug reports...
SourceManager & getSourceManager()
const LocationContext * getLocationContext() const
bool isPointerType() const