diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -35,11 +35,19 @@ using namespace ento; namespace { + +enum class MakeUniqueKind { + Regular, // ie, std::make_unique + ForOverwrite, // ie, std::make_unique_for_overwrite + None // ie, is neither of the above two +}; + class SmartPtrModeling : public Checker { bool isBoolConversionMethod(const CallEvent &Call) const; + MakeUniqueKind isStdMakeUniqueCall(const CallEvent &Call) const; public: // Whether the checker should model for null dereferences of smart pointers. @@ -68,6 +76,7 @@ bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, const MemRegion *OtherSmartPtrRegion) const; void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; + void handleMakeUnique(const CallEvent &Call, CheckerContext &C, MakeUniqueKind Kind) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -175,8 +184,34 @@ return CD && CD->getConversionType()->isBooleanType(); } +MakeUniqueKind SmartPtrModeling::isStdMakeUniqueCall(const CallEvent &Call) const { + if (Call.getKind() != CallEventKind::CE_Function) + return MakeUniqueKind::None; + const auto *D = Call.getDecl(); + if (!D) + return MakeUniqueKind::None; + const auto *FTD = llvm::dyn_cast(D); + if (!FTD) + return MakeUniqueKind::None; + if (FTD->getDeclName().isIdentifier()) { + StringRef Name = FTD->getName(); + if (Name == "make_unique") + return MakeUniqueKind::Regular; + else if (Name == "make_unique_for_overwrite") + return MakeUniqueKind::ForOverwrite; + } + return MakeUniqueKind::None; +} + bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { + + MakeUniqueKind Kind = isStdMakeUniqueCall(Call); + if (Kind != MakeUniqueKind::None) { + handleMakeUnique(Call, C, Kind); + return true; + } + ProgramStateRef State = C.getState(); if (!smartptr::isStdSmartPtrCall(Call)) return false; @@ -214,7 +249,6 @@ if (const auto *CC = dyn_cast(&Call)) { if (CC->getDecl()->isCopyConstructor()) return false; - const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return false; @@ -272,6 +306,33 @@ return C.isDifferent(); } +void SmartPtrModeling::handleMakeUnique(const CallEvent &Call, + CheckerContext &C, MakeUniqueKind Kind) const { + assert(Kind != MakeUniqueKind::None && "Call is not to make_unique or make_unique_for_overwrite"); + + const auto *OriginExpr = Call.getOriginExpr(); + const auto *LocCtx = C.getLocationContext(); + + ProgramStateRef State = C.getState(); + + if (Kind == MakeUniqueKind::Regular) { + // With make unique it is not possible to make a null smart pointer. + // So we can set the SVal for Call.getOriginExpr() to be non-null + auto ExprVal = C.getSValBuilder().conjureSymbolVal( + OriginExpr, LocCtx, Call.getResultType(), C.blockCount()); + State = State->assume(ExprVal, true); + State = State->BindExpr(OriginExpr, LocCtx, ExprVal); + } else { + // Technically, we are creating an uninitialized unique_ptr. + // So the value inside is *not* null, but we might as well set it + // so that the user gets better warnings. + auto ExprVal = C.getSValBuilder().makeZeroVal(Call.getResultType()); + State = State->BindExpr(OriginExpr, LocCtx, ExprVal); + } + + C.addTransition(State); +} + void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState();