aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:04 +0000
committerDimitry Andric <dim@FreeBSD.org>2023-02-11 12:38:11 +0000
commite3b557809604d036af6e00c60f012c2025b59a5e (patch)
tree8a11ba2269a3b669601e2fd41145b174008f4da8 /clang/lib/StaticAnalyzer
parent08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (diff)
downloadsrc-e3b557809604d036af6e00c60f012c2025b59a5e.tar.gz
src-e3b557809604d036af6e00c60f012c2025b59a5e.zip
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp46
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp22
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp211
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp35
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp11
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp76
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h62
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp72
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp315
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp270
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp38
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp12
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp21
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp136
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp21
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp76
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp17
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp38
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp16
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp635
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp241
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Taint.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp80
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp17
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp41
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp49
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h21
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/Yaml.h11
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp22
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp61
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp72
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp334
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallDescription.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp83
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp12
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerManager.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Core/CoreEngine.cpp15
-rw-r--r--clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Core/Environment.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp530
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp30
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp171
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp155
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/MemRegion.cpp55
-rw-r--r--clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/ProgramState.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp304
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp309
-rw-r--r--clang/lib/StaticAnalyzer/Core/SValBuilder.cpp34
-rw-r--r--clang/lib/StaticAnalyzer/Core/SVals.cpp16
-rw-r--r--clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp389
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp51
-rw-r--r--clang/lib/StaticAnalyzer/Core/Store.cpp15
-rw-r--r--clang/lib/StaticAnalyzer/Core/SymbolManager.cpp20
-rw-r--r--clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp1
104 files changed, 3456 insertions, 2136 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 6154eeb3419c..45783729e142 100644
--- a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -59,7 +60,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
if (D != P.getLocationContext()->getDecl())
continue;
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
const CFGBlock *CB = BE->getBlock();
reachable.insert(CB);
}
@@ -123,7 +124,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
if (Exit->empty())
continue;
const CFGElement &CE = Exit->front();
- if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
SmallString<128> bufI;
llvm::raw_svector_ostream outputI(bufI);
outputI << "(" << NameOfRootFunction << ")" <<
diff --git a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 5be5bcde4d6e..986b0add93df 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -23,6 +23,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -84,7 +85,7 @@ static SVal computeExtentBegin(SValBuilder &svalBuilder,
static std::pair<NonLoc, nonloc::ConcreteInt>
getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
SValBuilder &svalBuilder) {
- Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
llvm::APSInt constant =
@@ -143,7 +144,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
- if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
if (auto ConcreteNV = NV->getAs<nonloc::ConcreteInt>()) {
std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
getSimplifiedOffsets(rawOffset.getByteOffset(), *ConcreteNV,
@@ -155,7 +156,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
svalBuilder.getConditionType());
- Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
+ std::optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
if (!lowerBoundToCheck)
return;
@@ -194,7 +195,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
Size.castAs<NonLoc>(),
svalBuilder.getConditionType());
- Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
+ std::optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
if (!upperboundToCheck)
break;
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index ca76e2d83381..44166aaf5b85 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -33,6 +33,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -369,7 +370,7 @@ enum CFNumberType {
kCFNumberCGFloatType = 16
};
-static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
+static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
if (i < kCFNumberCharType)
@@ -390,7 +391,7 @@ static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
case kCFNumberCGFloatType:
// FIXME: We need a way to map from names to Type*.
default:
- return None;
+ return std::nullopt;
}
return Ctx.getTypeSize(T);
@@ -442,12 +443,13 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// FIXME: We really should allow ranges of valid theType values, and
// bifurcate the state appropriately.
- Optional<nonloc::ConcreteInt> V = dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
+ std::optional<nonloc::ConcreteInt> V =
+ dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
if (!V)
return;
uint64_t NumberKind = V->getValue().getLimitedValue();
- Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
+ std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
// FIXME: In some cases we can emit an error.
if (!OptCFNumberSize)
@@ -462,7 +464,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// FIXME: Eventually we should handle arbitrary locations. We can do this
// by having an enhanced memory model that does low-level typing.
- Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
if (!LV)
return;
@@ -531,10 +533,10 @@ namespace {
class CFRetainReleaseChecker : public Checker<check::PreCall> {
mutable APIMisuse BT{this, "null passed to CF memory management function"};
const CallDescriptionSet ModelledCalls = {
- {"CFRetain", 1},
- {"CFRelease", 1},
- {"CFMakeCollectable", 1},
- {"CFAutorelease", 1},
+ {{"CFRetain"}, 1},
+ {{"CFRelease"}, 1},
+ {{"CFMakeCollectable"}, 1},
+ {{"CFAutorelease"}, 1},
};
public:
@@ -554,7 +556,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
// Get the argument's value.
SVal ArgVal = Call.getArgSVal(0);
- Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
if (!DefArgVal)
return;
@@ -742,7 +744,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
return;
// Verify that all arguments have Objective-C types.
- Optional<ExplodedNode*> errorNode;
+ std::optional<ExplodedNode *> errorNode;
for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
QualType ArgTy = msg.getArgExpr(I)->getType();
@@ -769,7 +771,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
if (!errorNode)
errorNode = C.generateNonFatalErrorNode();
- if (!errorNode.value())
+ if (!*errorNode)
continue;
SmallString<128> sbuf;
@@ -786,8 +788,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
ArgTy.print(os, C.getLangOpts());
os << "'";
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(),
- errorNode.value());
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
R->addRange(msg.getArgSourceRange(I));
C.emitReport(std::move(R));
}
@@ -857,7 +859,8 @@ static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
return nullptr;
SVal CollectionVal = C.getSVal(FCS->getCollection());
- Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> KnownCollection =
+ CollectionVal.getAs<DefinedSVal>();
if (!KnownCollection)
return State;
@@ -889,7 +892,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C,
const Stmt *Element = FCS->getElement();
// FIXME: Copied from ExprEngineObjC.
- Optional<Loc> ElementLoc;
+ std::optional<Loc> ElementLoc;
if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
assert(ElemDecl->getInit() == nullptr);
@@ -928,8 +931,8 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
nonloc::SymbolVal(*CountS),
SvalBuilder.makeIntVal(0, (*CountS)->getType()),
SvalBuilder.getConditionType());
- Optional<DefinedSVal> CountGreaterThanZero =
- CountGreaterThanZeroVal.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> CountGreaterThanZero =
+ CountGreaterThanZeroVal.getAs<DefinedSVal>();
if (!CountGreaterThanZero) {
// The SValBuilder cannot construct a valid SVal for this condition.
// This means we cannot properly reason about it.
@@ -957,7 +960,7 @@ static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
return false;
ProgramPoint P = N->getLocation();
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
return BE->getSrc()->getLoopTarget() == FCS;
}
@@ -1092,7 +1095,7 @@ ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
PointerEscapeKind Kind) const {
SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
- // Remove the invalidated symbols form the collection count map.
+ // Remove the invalidated symbols from the collection count map.
for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
E = Escaped.end();
I != E; ++I) {
@@ -1174,7 +1177,8 @@ ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
ProgramStateRef State,
CheckerContext &C) const {
SVal Val = C.getSVal(NonNullExpr);
- if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
+ if (std::optional<DefinedOrUnknownSVal> DV =
+ Val.getAs<DefinedOrUnknownSVal>())
return State->assume(*DV, true);
return State;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 8416ab39e194..76f091562cd5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -64,19 +64,15 @@ public:
REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
- : IILockGuard(nullptr), IIUniqueLock(nullptr),
- LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
- FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
- PthreadLockFn("pthread_mutex_lock"),
- PthreadTryLockFn("pthread_mutex_trylock"),
- PthreadUnlockFn("pthread_mutex_unlock"),
- MtxLock("mtx_lock"),
- MtxTimedLock("mtx_timedlock"),
- MtxTryLock("mtx_trylock"),
- MtxUnlock("mtx_unlock"),
- ClassLockGuard("lock_guard"),
- ClassUniqueLock("unique_lock"),
- IdentifierInfoInitialized(false) {
+ : IILockGuard(nullptr), IIUniqueLock(nullptr), LockFn({"lock"}),
+ UnlockFn({"unlock"}), SleepFn({"sleep"}), GetcFn({"getc"}),
+ FgetsFn({"fgets"}), ReadFn({"read"}), RecvFn({"recv"}),
+ PthreadLockFn({"pthread_mutex_lock"}),
+ PthreadTryLockFn({"pthread_mutex_trylock"}),
+ PthreadUnlockFn({"pthread_mutex_unlock"}), MtxLock({"mtx_lock"}),
+ MtxTimedLock({"mtx_timedlock"}), MtxTryLock({"mtx_trylock"}),
+ MtxUnlock({"mtx_unlock"}), ClassLockGuard("lock_guard"),
+ ClassUniqueLock("unique_lock"), IdentifierInfoInitialized(false) {
// Initialize the bug type.
BlockInCritSectionBugType.reset(
new BugType(this, "Call to blocking function in critical section",
diff --git a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index dad25d6f853b..2d20e394681d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -74,7 +75,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
// Get the value of the right-hand side. We only care about values
// that are defined (UnknownVals and UndefinedVals are handled by other
// checkers).
- Optional<NonLoc> NV = val.getAs<NonLoc>();
+ std::optional<NonLoc> NV = val.getAs<NonLoc>();
if (!NV)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 9a6c013bcf66..12b948a65261 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -27,6 +27,7 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <functional>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -75,6 +76,16 @@ static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,
}
enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
+
+enum class CharKind { Regular = 0, Wide };
+constexpr CharKind CK_Regular = CharKind::Regular;
+constexpr CharKind CK_Wide = CharKind::Wide;
+
+static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
+ return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy
+ : Ctx.WideCharTy);
+}
+
class CStringChecker : public Checker< eval::Call,
check::PreStmt<DeclStmt>,
check::LiveSymbols,
@@ -124,35 +135,46 @@ public:
const CallExpr *)>;
CallDescriptionMap<FnCheck> Callbacks = {
- {{CDF_MaybeBuiltin, "memcpy", 3},
- std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)},
- {{CDF_MaybeBuiltin, "wmemcpy", 3},
- std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)},
- {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
- {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
- {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
- {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset},
- {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset},
- {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy},
- {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy},
- {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy},
- {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy},
- {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat},
- {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
- {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
- {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
- {{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength},
- {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
- {{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength},
- {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
- {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
- {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
- {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp},
- {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep},
- {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy},
- {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp},
- {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero},
- {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, {"memcpy"}, 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemcpy"}, 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"mempcpy"}, 3},
+ std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)},
+ {{CDF_None, {"wmempcpy"}, 3},
+ std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memmove"}, 3},
+ std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"wmemmove"}, 3},
+ std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)},
+ {{CDF_MaybeBuiltin, {"memset"}, 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, {"explicit_memset"}, 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, {"strcpy"}, 2}, &CStringChecker::evalStrcpy},
+ {{CDF_MaybeBuiltin, {"strncpy"}, 3}, &CStringChecker::evalStrncpy},
+ {{CDF_MaybeBuiltin, {"stpcpy"}, 2}, &CStringChecker::evalStpcpy},
+ {{CDF_MaybeBuiltin, {"strlcpy"}, 3}, &CStringChecker::evalStrlcpy},
+ {{CDF_MaybeBuiltin, {"strcat"}, 2}, &CStringChecker::evalStrcat},
+ {{CDF_MaybeBuiltin, {"strncat"}, 3}, &CStringChecker::evalStrncat},
+ {{CDF_MaybeBuiltin, {"strlcat"}, 3}, &CStringChecker::evalStrlcat},
+ {{CDF_MaybeBuiltin, {"strlen"}, 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, {"wcslen"}, 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, {"strnlen"}, 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, {"strcmp"}, 2}, &CStringChecker::evalStrcmp},
+ {{CDF_MaybeBuiltin, {"strncmp"}, 3}, &CStringChecker::evalStrncmp},
+ {{CDF_MaybeBuiltin, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp},
+ {{CDF_MaybeBuiltin, {"strncasecmp"}, 3},
+ &CStringChecker::evalStrncasecmp},
+ {{CDF_MaybeBuiltin, {"strsep"}, 2}, &CStringChecker::evalStrsep},
+ {{CDF_MaybeBuiltin, {"bcopy"}, 3}, &CStringChecker::evalBcopy},
+ {{CDF_MaybeBuiltin, {"bcmp"}, 3},
+ std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
+ {{CDF_MaybeBuiltin, {"bzero"}, 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, {"explicit_bzero"}, 2}, &CStringChecker::evalBzero},
};
// These require a bit of special handling.
@@ -160,16 +182,16 @@ public:
StdCopyBackward{{"std", "copy_backward"}, 3};
FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
- void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) const;
- void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
- void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const;
+ void evalMempcpy(CheckerContext &C, const CallExpr *CE, CharKind CK) const;
+ void evalMemmove(CheckerContext &C, const CallExpr *CE, CharKind CK) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest, SourceArgExpr Source,
- bool Restricted, bool IsMempcpy, bool IsWide) const;
+ bool Restricted, bool IsMempcpy, CharKind CK) const;
- void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemcmp(CheckerContext &C, const CallExpr *CE, CharKind CK) const;
void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
@@ -248,14 +270,16 @@ public:
AnyArgExpr Arg, SVal l) const;
ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
- AccessKind Access, bool IsWide = false) const;
+ AccessKind Access,
+ CharKind CK = CharKind::Regular) const;
ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
AccessKind Access,
- bool IsWide = false) const;
+ CharKind CK = CharKind::Regular) const;
ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
- AnyArgExpr Second, bool IsWide = false) const;
+ AnyArgExpr Second,
+ CharKind CK = CharKind::Regular) const;
void emitOverlapBug(CheckerContext &C,
ProgramStateRef state,
const Stmt *First,
@@ -295,7 +319,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
std::pair<ProgramStateRef , ProgramStateRef >
CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
QualType Ty) {
- Optional<DefinedSVal> val = V.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> val = V.getAs<DefinedSVal>();
if (!val)
return std::pair<ProgramStateRef , ProgramStateRef >(state, state);
@@ -339,7 +363,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
AccessKind Access,
- bool IsWide) const {
+ CharKind CK) const {
// If a previous check has failed, propagate the failure.
if (!state)
@@ -360,7 +384,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
// Get the index of the accessed element.
NonLoc Idx = ER->getIndex();
- if (!IsWide) {
+ if (CK == CharKind::Regular) {
if (ER->getValueType() != Ctx.CharTy)
return state;
} else {
@@ -417,7 +441,7 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef
CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
- AccessKind Access, bool IsWide) const {
+ AccessKind Access, CharKind CK) const {
// If a previous check has failed, propagate the failure.
if (!State)
return nullptr;
@@ -426,7 +450,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
ASTContext &Ctx = svalBuilder.getContext();
QualType SizeTy = Size.Expression->getType();
- QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
+ QualType PtrTy = getCharPtrType(Ctx, CK);
// Check that the first buffer is non-null.
SVal BufVal = C.getSVal(Buffer.Expression);
@@ -442,7 +466,7 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
// FIXME: This assumes the caller has already checked that the access length
// is positive. And that it's unsigned.
SVal LengthVal = C.getSVal(Size.Expression);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return State;
@@ -456,11 +480,11 @@ CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
// Check that the first buffer is sufficiently long.
SVal BufStart =
svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
- if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
+ if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
SVal BufEnd =
svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
- State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide);
+ State = CheckLocation(C, State, Buffer, BufEnd, Access, CK);
// If the buffer isn't large enough, abort.
if (!State)
@@ -475,7 +499,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
AnyArgExpr Second,
- bool IsWide) const {
+ CharKind CK) const {
if (!Filter.CheckCStringBufferOverlap)
return state;
@@ -499,11 +523,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
SVal firstVal = state->getSVal(First.Expression, LCtx);
SVal secondVal = state->getSVal(Second.Expression, LCtx);
- Optional<Loc> firstLoc = firstVal.getAs<Loc>();
+ std::optional<Loc> firstLoc = firstVal.getAs<Loc>();
if (!firstLoc)
return state;
- Optional<Loc> secondLoc = secondVal.getAs<Loc>();
+ std::optional<Loc> secondLoc = secondVal.getAs<Loc>();
if (!secondLoc)
return state;
@@ -526,7 +550,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
QualType cmpTy = svalBuilder.getConditionType();
SVal reverse =
svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);
- Optional<DefinedOrUnknownSVal> reverseTest =
+ std::optional<DefinedOrUnknownSVal> reverseTest =
reverse.getAs<DefinedOrUnknownSVal>();
if (!reverseTest)
return state;
@@ -547,31 +571,31 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Get the length, and make sure it too is known.
SVal LengthVal = state->getSVal(Size.Expression, LCtx);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return state;
// Convert the first buffer's start address to char*.
// Bail out if the cast fails.
ASTContext &Ctx = svalBuilder.getContext();
- QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
+ QualType CharPtrTy = getCharPtrType(Ctx, CK);
SVal FirstStart =
svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
- Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
+ std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
if (!FirstStartLoc)
return state;
// Compute the end of the first buffer. Bail out if THAT fails.
SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,
*Length, CharPtrTy);
- Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
+ std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
if (!FirstEndLoc)
return state;
// Is the end of the first buffer past the start of the second buffer?
SVal Overlap =
svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);
- Optional<DefinedOrUnknownSVal> OverlapTest =
+ std::optional<DefinedOrUnknownSVal> OverlapTest =
Overlap.getAs<DefinedOrUnknownSVal>();
if (!OverlapTest)
return state;
@@ -736,7 +760,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
left = right;
}
- if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
QualType cmpTy = svalBuilder.getConditionType();
// If left > max - right, we have an overflow.
SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
@@ -822,7 +846,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
C.blockCount());
if (!hypothetical) {
- if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
// In case of unbounded calls strlen etc bound the range to SIZE_MAX/4
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
@@ -848,7 +872,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// If we can't get a region, see if it's something we /know/ isn't a
// C string. In the context of locations, the only time we can issue such
// a warning is for labels.
- if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
+ if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -953,7 +977,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
SVal BufVal = state->getSVal(FirstBuf, LCtx);
SVal LengthVal = state->getSVal(Size, LCtx);
- Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return true; // cf top comment.
@@ -966,7 +990,7 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
// Check that the first buffer is sufficiently long.
SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
- Optional<Loc> BufLoc = BufStart.getAs<Loc>();
+ std::optional<Loc> BufLoc = BufStart.getAs<Loc>();
if (!BufLoc)
return true; // cf top comment.
@@ -1004,14 +1028,14 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
const Expr *E, SVal V,
bool IsSourceBuffer,
const Expr *Size) {
- Optional<Loc> L = V.getAs<Loc>();
+ std::optional<Loc> L = V.getAs<Loc>();
if (!L)
return state;
// FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
// some assumptions about the value that CFRefCount can't. Even so, it should
// probably be refactored.
- if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
const MemRegion *R = MR->getRegion()->StripCasts();
// Are we dealing with an ElementRegion? If so, we should be invalidating
@@ -1111,7 +1135,7 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
RegionOffset Offset = MR->getAsOffset();
const MemRegion *BR = Offset.getRegion();
- Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
+ std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
if (!SizeNL)
return false;
@@ -1190,7 +1214,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest,
SourceArgExpr Source, bool Restricted,
- bool IsMempcpy, bool IsWide) const {
+ bool IsMempcpy, CharKind CK) const {
CurrentFunctionDescription = "memory copy function";
// See if the size argument is zero.
@@ -1233,11 +1257,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
return;
// Ensure the accesses are valid and that the buffers do not overlap.
- state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide);
- state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide);
+ state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK);
+ state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK);
if (Restricted)
- state = CheckOverlap(C, state, Size, Dest, Source, IsWide);
+ state = CheckOverlap(C, state, Size, Dest, Source, CK);
if (!state)
return;
@@ -1248,7 +1272,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
// Get the byte after the last byte copied.
SValBuilder &SvalBuilder = C.getSValBuilder();
ASTContext &Ctx = SvalBuilder.getContext();
- QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType CharPtrTy = getCharPtrType(Ctx, CK);
SVal DestRegCharVal =
SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());
SVal lastElement = C.getSValBuilder().evalBinOp(
@@ -1288,7 +1312,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
}
void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE,
- bool IsWide) const {
+ CharKind CK) const {
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
DestinationArgExpr Dest = {CE->getArg(0), 0};
@@ -1299,11 +1323,11 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE,
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy,
- IsWide);
+ evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK);
}
-void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE,
+ CharKind CK) const {
// void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is a pointer to the byte following the last written byte.
DestinationArgExpr Dest = {CE->getArg(0), 0};
@@ -1313,10 +1337,11 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = true;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
- false);
+ CK);
}
-void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE,
+ CharKind CK) const {
// void *memmove(void *dst, const void *src, size_t n);
// The return value is the address of the destination buffer.
DestinationArgExpr Dest = {CE->getArg(0), 0};
@@ -1326,7 +1351,7 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
- false);
+ CK);
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
@@ -1338,10 +1363,11 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
- false);
+ CharKind::Regular);
}
-void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE,
+ CharKind CK) const {
// int memcmp(const void *s1, const void *s2, size_t n);
CurrentFunctionDescription = "memory comparison function";
@@ -1401,8 +1427,8 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
// If the two arguments might be different buffers, we have to check
// the size of both of them.
assert(NotSameBuffer);
- State = CheckBufferAccess(C, State, Right, Size, AccessKind::read);
- State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
+ State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK);
+ State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK);
if (State) {
// The return value is the comparison result, which we don't know.
SVal CmpV = Builder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
@@ -1481,8 +1507,8 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
const Expr *maxlenExpr = CE->getArg(1);
SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
- Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
- Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
+ std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
if (strLengthNL && maxlenValNL) {
ProgramStateRef stateStringTooLong, stateStringNotTooLong;
@@ -1630,11 +1656,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// Get the string length of the source.
SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);
- Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
// Get the string length of the destination buffer.
SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);
- Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
+ std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
// If the source isn't a valid C string, give up.
if (strLength.isUndef())
@@ -1672,7 +1698,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
lenVal =
svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());
- Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
+ std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
// If we know both values, we might be able to figure out how much
// we're copying.
@@ -1714,7 +1740,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
freeSpace =
svalBuilder.evalBinOp(state, BO_Sub, freeSpace,
svalBuilder.makeIntVal(1, sizeTy), sizeTy);
- Optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
+ std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
// While unlikely, it is possible that the subtraction is
// too complex to compute, let's check whether it succeeded.
@@ -1839,7 +1865,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
*dstStrLengthNL, sizeTy);
}
- Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
+ std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
// If we know both string lengths, we might know the final string length.
if (amountCopiedNL && dstStrLengthNL) {
@@ -1860,7 +1886,8 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
finalStrLength = getCStringLength(C, state, CE, DstVal, true);
assert(!finalStrLength.isUndef());
- if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> finalStrLengthNL =
+ finalStrLength.getAs<NonLoc>()) {
if (amountCopiedNL && appendK == ConcatFnKind::none) {
// we overwrite dst string with the src
// finalStrLength >= srcStrLength
@@ -1911,13 +1938,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If the destination is a MemRegion, try to check for a buffer overflow and
// record the new string length.
- if (Optional<loc::MemRegionVal> dstRegVal =
- DstVal.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> dstRegVal =
+ DstVal.getAs<loc::MemRegionVal>()) {
QualType ptrTy = Dst.Expression->getType();
// If we have an exact value on a bounded copy, use that to check for
// overflows, rather than our estimate about how much is actually copied.
- if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
SVal maxLastElement =
svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);
@@ -1927,7 +1954,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
}
// Then, if the final length is known...
- if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
*knownStrLength, ptrTy);
@@ -2119,7 +2146,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType());
// Constrain strcmp's result range based on the result of StringRef's
// comparison methods.
- BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT;
+ BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT;
SVal compareWithZero =
svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
svalBuilder.getConditionType());
@@ -2165,7 +2192,7 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
SValBuilder &SVB = C.getSValBuilder();
SVal Result;
- if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
+ if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
// Get the current value of the search string pointer, as a char*.
Result = State->getSVal(*SearchStrLoc, CharPtrTy);
diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index 45f9a82a9d0a..f02d20d45678 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -24,7 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
-#include "llvm/ADT/Optional.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -472,7 +472,7 @@ bool CastValueChecker::evalCall(const CallEvent &Call,
const CastCheck &Check = Lookup->first;
CallKind Kind = Lookup->second;
- Optional<DefinedOrUnknownSVal> DV;
+ std::optional<DefinedOrUnknownSVal> DV;
switch (Kind) {
case CallKind::Function: {
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 2d2ddcdf3890..3fcf6f435a43 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -45,6 +45,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -282,7 +283,7 @@ void ObjCDeallocChecker::checkBeginFunction(
continue;
SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
- Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ std::optional<Loc> LValLoc = LVal.getAs<Loc>();
if (!LValLoc)
continue;
@@ -953,7 +954,7 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
ProgramStateRef State = C.getState();
SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
- Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ std::optional<Loc> LValLoc = LVal.getAs<Loc>();
if (!LValLoc)
return nullptr;
@@ -1004,7 +1005,7 @@ bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
return false;
}
-/// Returns true if the ID is a class in which which is known to have
+/// Returns true if the ID is a class in which is known to have
/// a separate teardown lifecycle. In this case, -dealloc warnings
/// about missing releases should be suppressed.
bool ObjCDeallocChecker::classHasSeparateTeardown(
diff --git a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index ce8d6c879870..9ace1583eb53 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -43,7 +43,7 @@ class ChrootChecker : public Checker<eval::Call, check::PreCall> {
// This bug refers to possibly break out of a chroot() jail.
mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
- const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1};
+ const CallDescription Chroot{{"chroot"}, 1}, Chdir{{"chdir"}, 1};
public:
ChrootChecker() {}
diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
index 77a3218f55fb..67962f75f9bf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -72,26 +72,26 @@ public:
SVal) const;
CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
- {{"clear", 0}, &ContainerModeling::handleClear},
- {{"assign", 2}, &ContainerModeling::handleAssign},
- {{"push_back", 1}, &ContainerModeling::handlePushBack},
- {{"emplace_back", 1}, &ContainerModeling::handlePushBack},
- {{"pop_back", 0}, &ContainerModeling::handlePopBack},
- {{"push_front", 1}, &ContainerModeling::handlePushFront},
- {{"emplace_front", 1}, &ContainerModeling::handlePushFront},
- {{"pop_front", 0}, &ContainerModeling::handlePopFront},
+ {{{"clear"}, 0}, &ContainerModeling::handleClear},
+ {{{"assign"}, 2}, &ContainerModeling::handleAssign},
+ {{{"push_back"}, 1}, &ContainerModeling::handlePushBack},
+ {{{"emplace_back"}, 1}, &ContainerModeling::handlePushBack},
+ {{{"pop_back"}, 0}, &ContainerModeling::handlePopBack},
+ {{{"push_front"}, 1}, &ContainerModeling::handlePushFront},
+ {{{"emplace_front"}, 1}, &ContainerModeling::handlePushFront},
+ {{{"pop_front"}, 0}, &ContainerModeling::handlePopFront},
};
CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
- {{"insert", 2}, &ContainerModeling::handleInsert},
- {{"emplace", 2}, &ContainerModeling::handleInsert},
- {{"erase", 1}, &ContainerModeling::handleErase},
- {{"erase_after", 1}, &ContainerModeling::handleEraseAfter},
+ {{{"insert"}, 2}, &ContainerModeling::handleInsert},
+ {{{"emplace"}, 2}, &ContainerModeling::handleInsert},
+ {{{"erase"}, 1}, &ContainerModeling::handleErase},
+ {{{"erase_after"}, 1}, &ContainerModeling::handleEraseAfter},
};
CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
- {{"erase", 2}, &ContainerModeling::handleErase},
- {{"erase_after", 2}, &ContainerModeling::handleEraseAfter},
+ {{{"erase"}, 2}, &ContainerModeling::handleErase},
+ {{{"erase_after"}, 2}, &ContainerModeling::handleEraseAfter},
};
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 2102f9233bc1..01b662064d7b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -38,17 +38,17 @@ public:
llvm::DenseSet<const VarDecl *> &S;
bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
- SaveAndRestore<bool> inFinally(inEH, true);
+ SaveAndRestore inFinally(inEH, true);
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
}
bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
- SaveAndRestore<bool> inCatch(inEH, true);
+ SaveAndRestore inCatch(inEH, true);
return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
}
bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
- SaveAndRestore<bool> inCatch(inEH, true);
+ SaveAndRestore inCatch(inEH, true);
return TraverseStmt(S->getHandlerBlock());
}
@@ -103,8 +103,8 @@ void ReachableCode::computeReachableBlocks() {
static const Expr *
LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
while (Ex) {
- const BinaryOperator *BO =
- dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
+ Ex = Ex->IgnoreParenCasts();
+ const BinaryOperator *BO = dyn_cast<BinaryOperator>(Ex);
if (!BO)
break;
BinaryOperatorKind Op = BO->getOpcode();
@@ -240,7 +240,7 @@ public:
case DeadIncrement:
BugType = "Dead increment";
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case Standard:
if (!BugType) BugType = "Dead assignment";
os << "Value stored to '" << *V << "' is never read";
@@ -331,8 +331,7 @@ public:
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
const Expr *RHS =
- LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
- RHS = RHS->IgnoreParenCasts();
+ LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
QualType T = VD->getType();
if (T.isVolatileQualified())
@@ -415,8 +414,7 @@ public:
if (isConstant(E))
return;
- if (const DeclRefExpr *DRE =
- dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
// Special case: check for initialization from constant
// variables.
@@ -438,7 +436,7 @@ public:
PathDiagnosticLocation Loc =
PathDiagnosticLocation::create(V, BR.getSourceManager());
- Report(V, DeadInit, Loc, E->getSourceRange());
+ Report(V, DeadInit, Loc, V->getInit()->getSourceRange());
}
}
}
@@ -450,8 +448,9 @@ private:
bool isConstant(const InitListExpr *Candidate) const {
// We consider init list to be constant if each member of the list can be
// interpreted as constant.
- return llvm::all_of(Candidate->inits(),
- [this](const Expr *Init) { return isConstant(Init); });
+ return llvm::all_of(Candidate->inits(), [this](const Expr *Init) {
+ return isConstant(Init->IgnoreParenCasts());
+ });
}
/// Return true if the given expression can be interpreted as constant
@@ -461,7 +460,7 @@ private:
return true;
// We should also allow defensive initialization of structs, i.e. { 0 }
- if (const auto *ILE = dyn_cast<InitListExpr>(E->IgnoreParenCasts())) {
+ if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
return isConstant(ILE);
}
@@ -504,7 +503,7 @@ public:
// Treat local variables captured by reference in C++ lambdas as escaped.
void findLambdaReferenceCaptures(const LambdaExpr *LE) {
const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
- llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> CaptureFields;
FieldDecl *ThisCaptureField;
LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
@@ -512,14 +511,14 @@ public:
if (!C.capturesVariable())
continue;
- VarDecl *VD = C.getCapturedVar();
+ ValueDecl *VD = C.getCapturedVar();
const FieldDecl *FD = CaptureFields[VD];
- if (!FD)
+ if (!FD || !isa<VarDecl>(VD))
continue;
// If the capture field is a reference type, it is capture-by-reference.
if (FD->getType()->isReferenceType())
- Escaped.insert(VD);
+ Escaped.insert(cast<VarDecl>(VD));
}
}
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
index 47fd57c7db9b..832bb78c4803 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
@@ -41,9 +41,9 @@ class DebugContainerModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{"clang_analyzer_container_begin", 1},
+ {{{"clang_analyzer_container_begin"}, 1},
&DebugContainerModeling::analyzerContainerBegin},
- {{"clang_analyzer_container_end", 1},
+ {{{"clang_analyzer_container_end"}, 1},
&DebugContainerModeling::analyzerContainerEnd},
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
index 6add9a007a87..d05298b42c55 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
@@ -42,11 +42,11 @@ class DebugIteratorModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{"clang_analyzer_iterator_position", 1},
+ {{{"clang_analyzer_iterator_position"}, 1},
&DebugIteratorModeling::analyzerIteratorPosition},
- {{"clang_analyzer_iterator_container", 1},
+ {{{"clang_analyzer_iterator_container"}, 1},
&DebugIteratorModeling::analyzerIteratorContainer},
- {{"clang_analyzer_iterator_validity", 1},
+ {{{"clang_analyzer_iterator_validity"}, 1},
&DebugIteratorModeling::analyzerIteratorValidity},
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index bfd1d4c162ec..cc01e97d3fa2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -67,7 +68,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
return;
SVal Denom = C.getSVal(B->getRHS());
- Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
// Divide-by-undefined handled in the generic checking for uses of
// undefined values.
diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index aaf8cca32b60..6f26842e62c7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -31,6 +31,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -846,7 +847,7 @@ void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M,
return;
}
- Optional<ArrayRef<QualType>> TypeArgs =
+ std::optional<ArrayRef<QualType>> TypeArgs =
(*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
// This case might happen when there is an unspecialized override of a
// specialized method.
@@ -979,7 +980,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
if (!Method)
return;
- Optional<ArrayRef<QualType>> TypeArgs =
+ std::optional<ArrayRef<QualType>> TypeArgs =
(*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
if (!TypeArgs)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index e5088fb266bc..1077ceb6288e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -22,6 +22,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -108,7 +109,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
}
// Get the value of the expression to cast.
- const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
+ const std::optional<DefinedOrUnknownSVal> ValueToCast =
C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
// If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
index d8c3d7a4a6a6..265185e64107 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoChecker.cpp
@@ -22,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/STLExtras.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -132,7 +133,7 @@ void ErrnoChecker::generateErrnoNotCheckedBug(
void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
CheckerContext &C) const {
- Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
if (!ErrnoLoc)
return;
@@ -206,7 +207,7 @@ void ErrnoChecker::checkPreCall(const CallEvent &Call,
C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&
!isErrno(CallF)) {
if (getErrnoState(C.getState()) == MustBeChecked) {
- Optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());
assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");
generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),
ErrnoLoc->getAsRegion(), &Call);
@@ -219,7 +220,7 @@ ProgramStateRef ErrnoChecker::checkRegionChanges(
ArrayRef<const MemRegion *> ExplicitRegions,
ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
const CallEvent *Call) const {
- Optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
+ std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);
if (!ErrnoLoc)
return State;
const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();
@@ -227,12 +228,12 @@ ProgramStateRef ErrnoChecker::checkRegionChanges(
// If 'errno' is invalidated we can not know if it is checked or written into,
// allow read and write without bug reports.
if (llvm::is_contained(Regions, ErrnoRegion))
- return setErrnoStateIrrelevant(State);
+ return clearErrnoState(State);
// Always reset errno state when the system memory space is invalidated.
// The ErrnoRegion is not always found in the list in this case.
if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace()))
- return setErrnoStateIrrelevant(State);
+ return clearErrnoState(State);
return State;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
index 618f7e97f6e8..51f39c606d5c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
@@ -28,6 +28,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/STLExtras.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -57,11 +58,11 @@ public:
private:
// FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
- CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0},
- {"___errno", 0, 0},
- {"__errno", 0, 0},
- {"_errno", 0, 0},
- {"__error", 0, 0}};
+ CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0},
+ {{"___errno"}, 0, 0},
+ {{"__errno"}, 0, 0},
+ {{"_errno"}, 0, 0},
+ {{"__error"}, 0, 0}};
};
} // namespace
@@ -207,7 +208,7 @@ namespace clang {
namespace ento {
namespace errno_modeling {
-Optional<SVal> getErrnoValue(ProgramStateRef State) {
+std::optional<SVal> getErrnoValue(ProgramStateRef State) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return {};
@@ -239,19 +240,23 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
return State->set<ErrnoState>(EState);
}
-Optional<Loc> getErrnoLoc(ProgramStateRef State) {
+std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
if (!ErrnoR)
return {};
return loc::MemRegionVal{ErrnoR};
}
+ErrnoCheckState getErrnoState(ProgramStateRef State) {
+ return State->get<ErrnoState>();
+}
+
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
return State->set<ErrnoState>(EState);
}
-ErrnoCheckState getErrnoState(ProgramStateRef State) {
- return State->get<ErrnoState>();
+ProgramStateRef clearErrnoState(ProgramStateRef State) {
+ return setErrnoState(State, Irrelevant);
}
bool isErrno(const Decl *D) {
@@ -264,6 +269,12 @@ bool isErrno(const Decl *D) {
return false;
}
+const char *describeErrnoCheckState(ErrnoCheckState CS) {
+ assert(CS == errno_modeling::MustNotBeChecked &&
+ "Errno description not applicable.");
+ return "may be undefined after the call and should not be used";
+}
+
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
@@ -275,6 +286,53 @@ const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
});
}
+ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
+ CheckerContext &C) {
+ return setErrnoState(State, MustNotBeChecked);
+}
+
+ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
+ NonLoc ErrnoSym) {
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
+ DefinedOrUnknownSVal Cond =
+ SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
+ .castAs<DefinedOrUnknownSVal>();
+ State = State->assume(Cond, true);
+ if (!State)
+ return nullptr;
+ return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
+}
+
+ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
+ CheckerContext &C,
+ const Expr *InvalE) {
+ const MemRegion *ErrnoR = State->get<ErrnoRegion>();
+ if (!ErrnoR)
+ return State;
+ State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),
+ C.getLocationContext(), false);
+ if (!State)
+ return nullptr;
+ return setErrnoState(State, MustBeChecked);
+}
+
+const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) {
+ return getErrnoNoteTag(
+ C, (Twine("Assuming that function '") + Twine(Fn) +
+ Twine("' is successful, in this case the value 'errno' ") +
+ Twine(describeErrnoCheckState(MustNotBeChecked)))
+ .str());
+}
+
+const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C,
+ llvm::StringRef Fn) {
+ return getErrnoNoteTag(
+ C, (Twine("Function '") + Twine(Fn) +
+ Twine("' indicates failure only by setting of 'errno'"))
+ .str());
+}
+
} // namespace errno_modeling
} // namespace ento
} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
index 3757e25e1afe..2ca3979944e3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.h
@@ -16,35 +16,43 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include <optional>
namespace clang {
namespace ento {
namespace errno_modeling {
+/// Describe how reads and writes of \c errno are handled by the checker.
enum ErrnoCheckState : unsigned {
/// We do not know anything about 'errno'.
+ /// Read and write is always allowed.
Irrelevant = 0,
/// Value of 'errno' should be checked to find out if a previous function call
/// has failed.
+ /// When this state is set \c errno must be read by the program before a next
+ /// standard function call or other overwrite of \c errno follows, otherwise
+ /// a bug report is emitted.
MustBeChecked = 1,
/// Value of 'errno' is not allowed to be read, it can contain an unspecified
/// value.
+ /// When this state is set \c errno is not allowed to be read by the program
+ /// until it is overwritten or invalidated.
MustNotBeChecked = 2
};
/// Returns the value of 'errno', if 'errno' was found in the AST.
-llvm::Optional<SVal> getErrnoValue(ProgramStateRef State);
+std::optional<SVal> getErrnoValue(ProgramStateRef State);
/// Returns the errno check state, \c Errno_Irrelevant if 'errno' was not found
/// (this is not the only case for that value).
ErrnoCheckState getErrnoState(ProgramStateRef State);
/// Returns the location that points to the \c MemoryRegion where the 'errno'
-/// value is stored. Returns \c None if 'errno' was not found. Otherwise it
-/// always returns a valid memory region in the system global memory space.
-llvm::Optional<Loc> getErrnoLoc(ProgramStateRef State);
+/// value is stored. Returns \c std::nullopt if 'errno' was not found. Otherwise
+/// it always returns a valid memory region in the system global memory space.
+std::optional<Loc> getErrnoLoc(ProgramStateRef State);
/// Set value of 'errno' to any SVal, if possible.
/// The errno check state is set always when the 'errno' value is set.
@@ -60,6 +68,9 @@ ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
/// Set the errno check state, do not modify the errno value.
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState);
+/// Clear state of errno (make it irrelevant).
+ProgramStateRef clearErrnoState(ProgramStateRef State);
+
/// Determine if a `Decl` node related to 'errno'.
/// This is true if the declaration is the errno variable or a function
/// that returns a pointer to the 'errno' value (usually the 'errno' macro is
@@ -67,10 +78,53 @@ ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState);
/// declaration.
bool isErrno(const Decl *D);
+/// Produce a textual description about how \c errno is allowed to be used
+/// (in a \c ErrnoCheckState).
+/// The returned string is insertable into a longer warning message in the form
+/// "the value 'errno' <...>".
+/// Currently only the \c errno_modeling::MustNotBeChecked state is supported,
+/// others are not used by the clients.
+const char *describeErrnoCheckState(ErrnoCheckState CS);
+
/// Create a NoteTag that displays the message if the 'errno' memory region is
/// marked as interesting, and resets the interestingness.
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message);
+/// Set errno state for the common case when a standard function is successful.
+/// Set \c ErrnoCheckState to \c MustNotBeChecked (the \c errno value is not
+/// affected). At the state transition a note tag created by
+/// \c getNoteTagForStdSuccess can be used.
+ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C);
+
+/// Set errno state for the common case when a standard function fails.
+/// Set \c errno value to be not equal to zero and \c ErrnoCheckState to
+/// \c Irrelevant . The irrelevant errno state ensures that no related bug
+/// report is emitted later and no note tag is needed.
+/// \arg \c ErrnoSym Value to be used for \c errno and constrained to be
+/// non-zero.
+ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
+ NonLoc ErrnoSym);
+
+/// Set errno state for the common case when a standard function indicates
+/// failure only by \c errno. Sets \c ErrnoCheckState to \c MustBeChecked, and
+/// invalidates the errno region (clear of previous value).
+/// At the state transition a note tag created by
+/// \c getNoteTagForStdMustBeChecked can be used.
+/// \arg \c InvalE Expression that causes invalidation of \c errno.
+ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
+ CheckerContext &C, const Expr *InvalE);
+
+/// Generate the note tag that can be applied at the state generated by
+/// \c setErrnoForStdSuccess .
+/// \arg \c Fn Name of the (standard) function that is modeled.
+const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn);
+
+/// Generate the note tag that can be applied at the state generated by
+/// \c setErrnoStdMustBeChecked .
+/// \arg \c Fn Name of the (standard) function that is modeled.
+const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C,
+ llvm::StringRef Fn);
+
} // namespace errno_modeling
} // namespace ento
} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
index 924407ebe2d1..c46ebee0c94f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ErrnoTesterChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -69,13 +70,13 @@ private:
using EvalFn = std::function<void(CheckerContext &, const CallEvent &)>;
const CallDescriptionMap<EvalFn> TestCalls{
- {{"ErrnoTesterChecker_setErrno", 1}, &ErrnoTesterChecker::evalSetErrno},
- {{"ErrnoTesterChecker_getErrno", 0}, &ErrnoTesterChecker::evalGetErrno},
- {{"ErrnoTesterChecker_setErrnoIfError", 0},
+ {{{"ErrnoTesterChecker_setErrno"}, 1}, &ErrnoTesterChecker::evalSetErrno},
+ {{{"ErrnoTesterChecker_getErrno"}, 0}, &ErrnoTesterChecker::evalGetErrno},
+ {{{"ErrnoTesterChecker_setErrnoIfError"}, 0},
&ErrnoTesterChecker::evalSetErrnoIfError},
- {{"ErrnoTesterChecker_setErrnoIfErrorRange", 0},
+ {{{"ErrnoTesterChecker_setErrnoIfErrorRange"}, 0},
&ErrnoTesterChecker::evalSetErrnoIfErrorRange},
- {{"ErrnoTesterChecker_setErrnoCheckState", 0},
+ {{{"ErrnoTesterChecker_setErrnoCheckState"}, 0},
&ErrnoTesterChecker::evalSetErrnoCheckState}};
};
@@ -91,7 +92,7 @@ void ErrnoTesterChecker::evalGetErrno(CheckerContext &C,
const CallEvent &Call) {
ProgramStateRef State = C.getState();
- Optional<SVal> ErrnoVal = getErrnoValue(State);
+ std::optional<SVal> ErrnoVal = getErrnoValue(State);
assert(ErrnoVal && "Errno value should be available.");
State =
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), *ErrnoVal);
diff --git a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index ec1b0a70d7d3..355e9c2238a4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ScopedPrinter.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -58,9 +59,9 @@ class ExprInspectionChecker
// Optional parameter `ExprVal` for expression value to be marked interesting.
ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C,
- Optional<SVal> ExprVal = None) const;
+ std::optional<SVal> ExprVal = std::nullopt) const;
ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, ExplodedNode *N,
- Optional<SVal> ExprVal = None) const;
+ std::optional<SVal> ExprVal = std::nullopt) const;
template <typename T> void printAndReport(CheckerContext &C, T What) const;
const Expr *getArgExpr(const CallExpr *CE, CheckerContext &C) const;
@@ -115,7 +116,8 @@ bool ExprInspectionChecker::evalCall(const CallEvent &Call,
.Case("clang_analyzer_hashDump",
&ExprInspectionChecker::analyzerHashDump)
.Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
- .Case("clang_analyzer_express",
+ .Case("clang_analyzer_express", // This also marks the argument as
+ // interesting.
&ExprInspectionChecker::analyzerExpress)
.StartsWith("clang_analyzer_isTainted",
&ExprInspectionChecker::analyzerIsTainted)
@@ -160,17 +162,18 @@ static const char *getArgumentValueString(const CallExpr *CE,
}
}
-ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- CheckerContext &C,
- Optional<SVal> ExprVal) const {
+ExplodedNode *
+ExprInspectionChecker::reportBug(llvm::StringRef Msg, CheckerContext &C,
+ std::optional<SVal> ExprVal) const {
ExplodedNode *N = C.generateNonFatalErrorNode();
reportBug(Msg, C.getBugReporter(), N, ExprVal);
return N;
}
-ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
- BugReporter &BR, ExplodedNode *N,
- Optional<SVal> ExprVal) const {
+ExplodedNode *
+ExprInspectionChecker::reportBug(llvm::StringRef Msg, BugReporter &BR,
+ ExplodedNode *N,
+ std::optional<SVal> ExprVal) const {
if (!N)
return nullptr;
@@ -354,8 +357,7 @@ void ExprInspectionChecker::analyzerDumpElementCount(const CallExpr *CE,
if (const auto *TVR = MR->getAs<TypedValueRegion>()) {
ElementTy = TVR->getValueType();
} else {
- ElementTy =
- MR->castAs<SymbolicRegion>()->getSymbol()->getType()->getPointeeType();
+ ElementTy = MR->castAs<SymbolicRegion>()->getPointeeStaticType();
}
assert(!ElementTy->isPointerType());
@@ -466,58 +468,60 @@ void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
namespace {
class SymbolExpressor
- : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
+ : public SymExprVisitor<SymbolExpressor, std::optional<std::string>> {
ProgramStateRef State;
public:
SymbolExpressor(ProgramStateRef State) : State(State) {}
- Optional<std::string> lookup(const SymExpr *S) {
+ std::optional<std::string> lookup(const SymExpr *S) {
if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
const StringLiteral *SL = *SLPtr;
return std::string(SL->getBytes());
}
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymExpr(const SymExpr *S) { return lookup(S); }
+ std::optional<std::string> VisitSymExpr(const SymExpr *S) {
+ return lookup(S);
+ }
- Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str = Visit(S->getLHS()))
+ if (std::optional<std::string> Str = Visit(S->getLHS()))
return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
std::to_string(S->getRHS().getLimitedValue()) +
(S->getRHS().isUnsigned() ? "U" : ""))
.str();
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str1 = Visit(S->getLHS()))
- if (Optional<std::string> Str2 = Visit(S->getRHS()))
+ if (std::optional<std::string> Str1 = Visit(S->getLHS()))
+ if (std::optional<std::string> Str2 = Visit(S->getRHS()))
return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
" " + *Str2)
.str();
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitUnarySymExpr(const UnarySymExpr *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str = Visit(S->getOperand()))
+ if (std::optional<std::string> Str = Visit(S->getOperand()))
return (UnaryOperator::getOpcodeStr(S->getOpcode()) + *Str).str();
- return None;
+ return std::nullopt;
}
- Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
- if (Optional<std::string> Str = lookup(S))
+ std::optional<std::string> VisitSymbolCast(const SymbolCast *S) {
+ if (std::optional<std::string> Str = lookup(S))
return Str;
- if (Optional<std::string> Str = Visit(S->getOperand()))
+ if (std::optional<std::string> Str = Visit(S->getOperand()))
return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
- return None;
+ return std::nullopt;
}
};
} // namespace
@@ -531,14 +535,14 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
SVal ArgVal = C.getSVal(CE->getArg(0));
SymbolRef Sym = ArgVal.getAsSymbol();
if (!Sym) {
- reportBug("Not a symbol", C);
+ reportBug("Not a symbol", C, ArgVal);
return;
}
SymbolExpressor V(C.getState());
auto Str = V.Visit(Sym);
if (!Str) {
- reportBug("Unable to express", C);
+ reportBug("Unable to express", C, ArgVal);
return;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
index eb3b89ee6c5c..65ff1be8ec05 100644
--- a/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -101,6 +101,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/StringExtras.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -300,7 +301,7 @@ getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
}
} else {
assert(PtrToHandleLevel == 1);
- if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
+ if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
if (Sym) {
return {Sym};
diff --git a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
index dbfdff4d2a3b..43d2eeeb4b27 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -258,7 +259,7 @@ SVal GTestChecker::getAssertionResultSuccessFieldValue(
if (!SuccessField)
return UnknownVal();
- Optional<Loc> FieldLoc =
+ std::optional<Loc> FieldLoc =
State->getLValue(SuccessField, Instance).getAs<Loc>();
if (!FieldLoc)
return UnknownVal();
diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index f0a114801dda..f6e2f59d5697 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -30,6 +30,7 @@
#include <limits>
#include <memory>
+#include <optional>
#include <utility>
#define DEBUG_TYPE "taint-checker"
@@ -124,16 +125,17 @@ SVal getPointeeOf(const CheckerContext &C, Loc LValue) {
}
/// Given a pointer/reference argument, return the value it refers to.
-Optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) {
+std::optional<SVal> getPointeeOf(const CheckerContext &C, SVal Arg) {
if (auto LValue = Arg.getAs<Loc>())
return getPointeeOf(C, *LValue);
- return None;
+ return std::nullopt;
}
/// Given a pointer, return the SVal of its pointee or if it is tainted,
/// otherwise return the pointer's SVal if tainted.
/// Also considers stdin as a taint source.
-Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) {
+std::optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C,
+ SVal Arg) {
const ProgramStateRef State = C.getState();
if (auto Pointee = getPointeeOf(C, Arg))
@@ -147,7 +149,7 @@ Optional<SVal> getTaintedPointeeOrPointer(const CheckerContext &C, SVal Arg) {
if (isStdin(Arg, C.getASTContext()))
return Arg;
- return None;
+ return std::nullopt;
}
bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State,
@@ -161,7 +163,8 @@ bool isTaintedOrPointsToTainted(const Expr *E, const ProgramStateRef &State,
class ArgSet {
public:
ArgSet() = default;
- ArgSet(ArgVecTy &&DiscreteArgs, Optional<ArgIdxTy> VariadicIndex = None)
+ ArgSet(ArgVecTy &&DiscreteArgs,
+ std::optional<ArgIdxTy> VariadicIndex = std::nullopt)
: DiscreteArgs(std::move(DiscreteArgs)),
VariadicIndex(std::move(VariadicIndex)) {}
@@ -176,7 +179,7 @@ public:
private:
ArgVecTy DiscreteArgs;
- Optional<ArgIdxTy> VariadicIndex;
+ std::optional<ArgIdxTy> VariadicIndex;
};
/// A struct used to specify taint propagation rules for a function.
@@ -197,12 +200,12 @@ class GenericTaintRule {
ArgSet PropDstArgs;
/// A message that explains why the call is sensitive to taint.
- Optional<StringRef> SinkMsg;
+ std::optional<StringRef> SinkMsg;
GenericTaintRule() = default;
GenericTaintRule(ArgSet &&Sink, ArgSet &&Filter, ArgSet &&Src, ArgSet &&Dst,
- Optional<StringRef> SinkMsg = None)
+ std::optional<StringRef> SinkMsg = std::nullopt)
: SinkArgs(std::move(Sink)), FilterArgs(std::move(Filter)),
PropSrcArgs(std::move(Src)), PropDstArgs(std::move(Dst)),
SinkMsg(SinkMsg) {}
@@ -211,7 +214,7 @@ public:
/// Make a rule that reports a warning if taint reaches any of \p FilterArgs
/// arguments.
static GenericTaintRule Sink(ArgSet &&SinkArgs,
- Optional<StringRef> Msg = None) {
+ std::optional<StringRef> Msg = std::nullopt) {
return {std::move(SinkArgs), {}, {}, {}, Msg};
}
@@ -232,9 +235,9 @@ public:
}
/// Make a rule that taints all PropDstArgs if any of PropSrcArgs is tainted.
- static GenericTaintRule SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs,
- ArgSet &&DstArgs,
- Optional<StringRef> Msg = None) {
+ static GenericTaintRule
+ SinkProp(ArgSet &&SinkArgs, ArgSet &&SrcArgs, ArgSet &&DstArgs,
+ std::optional<StringRef> Msg = std::nullopt) {
return {
std::move(SinkArgs), {}, std::move(SrcArgs), std::move(DstArgs), Msg};
}
@@ -302,7 +305,7 @@ struct GenericTaintRuleParser {
TaintConfiguration &&Config) const;
private:
- using NamePartsTy = llvm::SmallVector<SmallString<32>, 2>;
+ using NamePartsTy = llvm::SmallVector<StringRef, 2>;
/// Validate part of the configuration, which contains a list of argument
/// indexes.
@@ -359,8 +362,8 @@ private:
// TODO: Remove separation to simplify matching logic once CallDescriptions
// are more expressive.
- mutable Optional<RuleLookupTy> StaticTaintRules;
- mutable Optional<RuleLookupTy> DynamicTaintRules;
+ mutable std::optional<RuleLookupTy> StaticTaintRules;
+ mutable std::optional<RuleLookupTy> DynamicTaintRules;
};
} // end of anonymous namespace
@@ -442,10 +445,8 @@ GenericTaintRuleParser::parseNameParts(const Config &C) {
if (!C.Scope.empty()) {
// If the Scope argument contains multiple "::" parts, those are considered
// namespace identifiers.
- llvm::SmallVector<StringRef, 2> NSParts;
- StringRef{C.Scope}.split(NSParts, "::", /*MaxSplit*/ -1,
+ StringRef{C.Scope}.split(NameParts, "::", /*MaxSplit*/ -1,
/*KeepEmpty*/ false);
- NameParts.append(NSParts.begin(), NSParts.end());
}
NameParts.emplace_back(C.Name);
return NameParts;
@@ -456,10 +457,7 @@ void GenericTaintRuleParser::consumeRulesFromConfig(const Config &C,
GenericTaintRule &&Rule,
RulesContTy &Rules) {
NamePartsTy NameParts = parseNameParts(C);
- llvm::SmallVector<const char *, 2> CallDescParts{NameParts.size()};
- llvm::transform(NameParts, CallDescParts.begin(),
- [](SmallString<32> &S) { return S.c_str(); });
- Rules.emplace_back(CallDescription(CallDescParts), std::move(Rule));
+ Rules.emplace_back(CallDescription(NameParts), std::move(Rule));
}
void GenericTaintRuleParser::parseConfig(const std::string &Option,
@@ -485,10 +483,12 @@ void GenericTaintRuleParser::parseConfig(const std::string &Option,
validateArgVector(Option, P.DstArgs);
bool IsSrcVariadic = P.VarType == TaintConfiguration::VariadicType::Src;
bool IsDstVariadic = P.VarType == TaintConfiguration::VariadicType::Dst;
- Optional<ArgIdxTy> JustVarIndex = P.VarIndex;
+ std::optional<ArgIdxTy> JustVarIndex = P.VarIndex;
- ArgSet SrcDesc(std::move(P.SrcArgs), IsSrcVariadic ? JustVarIndex : None);
- ArgSet DstDesc(std::move(P.DstArgs), IsDstVariadic ? JustVarIndex : None);
+ ArgSet SrcDesc(std::move(P.SrcArgs),
+ IsSrcVariadic ? JustVarIndex : std::nullopt);
+ ArgSet DstDesc(std::move(P.DstArgs),
+ IsDstVariadic ? JustVarIndex : std::nullopt);
consumeRulesFromConfig(
P, GenericTaintRule::Prop(std::move(SrcDesc), std::move(DstDesc)), Rules);
@@ -527,128 +527,128 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
RulesConstructionTy GlobalCRules{
// Sources
- {{"fdopen"}, TR::Source({{ReturnValueIndex}})},
- {{"fopen"}, TR::Source({{ReturnValueIndex}})},
- {{"freopen"}, TR::Source({{ReturnValueIndex}})},
- {{"getch"}, TR::Source({{ReturnValueIndex}})},
- {{"getchar"}, TR::Source({{ReturnValueIndex}})},
- {{"getchar_unlocked"}, TR::Source({{ReturnValueIndex}})},
- {{"gets"}, TR::Source({{0}, ReturnValueIndex})},
- {{"gets_s"}, TR::Source({{0}, ReturnValueIndex})},
- {{"scanf"}, TR::Source({{}, 1})},
- {{"scanf_s"}, TR::Source({{}, {1}})},
- {{"wgetch"}, TR::Source({{}, ReturnValueIndex})},
+ {{{"fdopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"fopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"freopen"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getch"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getchar"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getchar_unlocked"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"gets"}}, TR::Source({{0}, ReturnValueIndex})},
+ {{{"gets_s"}}, TR::Source({{0}, ReturnValueIndex})},
+ {{{"scanf"}}, TR::Source({{}, 1})},
+ {{{"scanf_s"}}, TR::Source({{}, {1}})},
+ {{{"wgetch"}}, TR::Source({{}, ReturnValueIndex})},
// Sometimes the line between taint sources and propagators is blurry.
// _IO_getc is choosen to be a source, but could also be a propagator.
// This way it is simpler, as modeling it as a propagator would require
// to model the possible sources of _IO_FILE * values, which the _IO_getc
// function takes as parameters.
- {{"_IO_getc"}, TR::Source({{ReturnValueIndex}})},
- {{"getcwd"}, TR::Source({{0, ReturnValueIndex}})},
- {{"getwd"}, TR::Source({{0, ReturnValueIndex}})},
- {{"readlink"}, TR::Source({{1, ReturnValueIndex}})},
- {{"readlinkat"}, TR::Source({{2, ReturnValueIndex}})},
- {{"get_current_dir_name"}, TR::Source({{ReturnValueIndex}})},
- {{"gethostname"}, TR::Source({{0}})},
- {{"getnameinfo"}, TR::Source({{2, 4}})},
- {{"getseuserbyname"}, TR::Source({{1, 2}})},
- {{"getgroups"}, TR::Source({{1, ReturnValueIndex}})},
- {{"getlogin"}, TR::Source({{ReturnValueIndex}})},
- {{"getlogin_r"}, TR::Source({{0}})},
+ {{{"_IO_getc"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getcwd"}}, TR::Source({{0, ReturnValueIndex}})},
+ {{{"getwd"}}, TR::Source({{0, ReturnValueIndex}})},
+ {{{"readlink"}}, TR::Source({{1, ReturnValueIndex}})},
+ {{{"readlinkat"}}, TR::Source({{2, ReturnValueIndex}})},
+ {{{"get_current_dir_name"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"gethostname"}}, TR::Source({{0}})},
+ {{{"getnameinfo"}}, TR::Source({{2, 4}})},
+ {{{"getseuserbyname"}}, TR::Source({{1, 2}})},
+ {{{"getgroups"}}, TR::Source({{1, ReturnValueIndex}})},
+ {{{"getlogin"}}, TR::Source({{ReturnValueIndex}})},
+ {{{"getlogin_r"}}, TR::Source({{0}})},
// Props
- {{"atoi"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"atol"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"atoll"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"fgetc"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"fgetln"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"fgets"}, TR::Prop({{2}}, {{0, ReturnValueIndex}})},
- {{"fscanf"}, TR::Prop({{0}}, {{}, 2})},
- {{"fscanf_s"}, TR::Prop({{0}}, {{}, {2}})},
- {{"sscanf"}, TR::Prop({{0}}, {{}, 2})},
-
- {{"getc"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"getc_unlocked"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"getdelim"}, TR::Prop({{3}}, {{0}})},
- {{"getline"}, TR::Prop({{2}}, {{0}})},
- {{"getw"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"pread"}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})},
- {{"read"}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})},
- {{"strchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"tolower"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"toupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"fread"}, TR::Prop({{3}}, {{0, ReturnValueIndex}})},
- {{"recv"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
- {{"recvfrom"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
-
- {{"ttyname"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"ttyname_r"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
-
- {{"basename"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"dirname"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"fnmatch"}, TR::Prop({{1}}, {{ReturnValueIndex}})},
- {{"memchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"memrchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"rawmemchr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
-
- {{"mbtowc"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{"wctomb"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{"wcwidth"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
-
- {{"memcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
- {{"memcpy"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{"memmove"}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"atoi"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"atol"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"atoll"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgetc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgetln"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fgets"}}, TR::Prop({{2}}, {{0, ReturnValueIndex}})},
+ {{{"fscanf"}}, TR::Prop({{0}}, {{}, 2})},
+ {{{"fscanf_s"}}, TR::Prop({{0}}, {{}, {2}})},
+ {{{"sscanf"}}, TR::Prop({{0}}, {{}, 2})},
+
+ {{{"getc"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"getc_unlocked"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"getdelim"}}, TR::Prop({{3}}, {{0}})},
+ {{{"getline"}}, TR::Prop({{2}}, {{0}})},
+ {{{"getw"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"pread"}}, TR::Prop({{0, 1, 2, 3}}, {{1, ReturnValueIndex}})},
+ {{{"read"}}, TR::Prop({{0, 2}}, {{1, ReturnValueIndex}})},
+ {{{"strchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"tolower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"toupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fread"}}, TR::Prop({{3}}, {{0, ReturnValueIndex}})},
+ {{{"recv"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"recvfrom"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"ttyname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"ttyname_r"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"basename"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"dirname"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"fnmatch"}}, TR::Prop({{1}}, {{ReturnValueIndex}})},
+ {{{"memchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"memrchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"rawmemchr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"mbtowc"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"wctomb"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"wcwidth"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+
+ {{{"memcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"memcpy"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
+ {{{"memmove"}}, TR::Prop({{1}}, {{0, ReturnValueIndex}})},
// If memmem was called with a tainted needle and the search was
// successful, that would mean that the value pointed by the return value
// has the same content as the needle. If we choose to go by the policy of
// content equivalence implies taintedness equivalence, that would mean
// haystack should be considered a propagation source argument.
- {{"memmem"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"memmem"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
// The comment for memmem above also applies to strstr.
- {{"strstr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strcasestr"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strstr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strcasestr"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strchrnul"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strchrnul"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"index"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"rindex"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"index"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"rindex"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
// FIXME: In case of arrays, only the first element of the array gets
// tainted.
- {{"qsort"}, TR::Prop({{0}}, {{0}})},
- {{"qsort_r"}, TR::Prop({{0}}, {{0}})},
-
- {{"strcmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
- {{"strcasecmp"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
- {{"strncmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
- {{"strncasecmp"}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
- {{"strspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
- {{"strcspn"}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
- {{"strpbrk"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strndup"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strndupa"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strnlen"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"strtol"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
- {{"strtoll"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
- {{"strtoul"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
- {{"strtoull"}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
-
- {{"isalnum"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isalpha"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isascii"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isblank"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"iscntrl"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isgraph"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"islower"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isprint"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"ispunct"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isspace"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isupper"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{"isxdigit"}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"qsort"}}, TR::Prop({{0}}, {{0}})},
+ {{{"qsort_r"}}, TR::Prop({{0}}, {{0}})},
+
+ {{{"strcmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strcasecmp"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strncmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
+ {{{"strncasecmp"}}, TR::Prop({{0, 1, 2}}, {{ReturnValueIndex}})},
+ {{{"strspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strcspn"}}, TR::Prop({{0, 1}}, {{ReturnValueIndex}})},
+ {{{"strpbrk"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strndup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strndupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strnlen"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"strtol"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoll"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoul"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+ {{{"strtoull"}}, TR::Prop({{0}}, {{1, ReturnValueIndex}})},
+
+ {{{"isalnum"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isalpha"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isascii"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isblank"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"iscntrl"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isgraph"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"islower"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isprint"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"ispunct"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isspace"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isupper"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{{"isxdigit"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
{{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrncat)}},
TR::Prop({{1, 2}}, {{0, ReturnValueIndex}})},
@@ -656,37 +656,40 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
TR::Prop({{1, 2}}, {{0}})},
{{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrlcat)}},
TR::Prop({{1, 2}}, {{0}})},
- {{CDF_MaybeBuiltin, {"snprintf"}},
+ {{CDF_MaybeBuiltin, {{"snprintf"}}},
TR::Prop({{1}, 3}, {{0, ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"sprintf"}},
+ {{CDF_MaybeBuiltin, {{"sprintf"}}},
TR::Prop({{1}, 2}, {{0, ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"strcpy"}},
+ {{CDF_MaybeBuiltin, {{"strcpy"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"stpcpy"}},
+ {{CDF_MaybeBuiltin, {{"stpcpy"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"strcat"}},
+ {{CDF_MaybeBuiltin, {{"strcat"}}},
TR::Prop({{1}}, {{0, ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"strdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"strdupa"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
- {{CDF_MaybeBuiltin, {"wcsdup"}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"strdupa"}}},
+ TR::Prop({{0}}, {{ReturnValueIndex}})},
+ {{CDF_MaybeBuiltin, {{"wcsdup"}}}, TR::Prop({{0}}, {{ReturnValueIndex}})},
// Sinks
- {{"system"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"popen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execl"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execle"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execlp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execvp"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execvP"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"execve"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{"dlopen"}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
- {{CDF_MaybeBuiltin, {"malloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
- {{CDF_MaybeBuiltin, {"calloc"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
- {{CDF_MaybeBuiltin, {"alloca"}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
- {{CDF_MaybeBuiltin, {"memccpy"}}, TR::Sink({{3}}, MsgTaintedBufferSize)},
- {{CDF_MaybeBuiltin, {"realloc"}}, TR::Sink({{1}}, MsgTaintedBufferSize)},
- {{{"setproctitle"}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
- {{{"setproctitle_fast"}},
+ {{{"system"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"popen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execl"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execle"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execlp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execvp"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execvP"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"execve"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{{"dlopen"}}, TR::Sink({{0}}, MsgSanitizeSystemArgs)},
+ {{CDF_MaybeBuiltin, {{"malloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"calloc"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"alloca"}}}, TR::Sink({{0}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"memccpy"}}},
+ TR::Sink({{3}}, MsgTaintedBufferSize)},
+ {{CDF_MaybeBuiltin, {{"realloc"}}},
+ TR::Sink({{1}}, MsgTaintedBufferSize)},
+ {{{{"setproctitle"}}}, TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
+ {{{{"setproctitle_fast"}}},
TR::Sink({{0}, 1}, MsgUncontrolledFormatString)},
// SinkProps
@@ -702,7 +705,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
{{CDF_MaybeBuiltin, {BI.getName(Builtin::BIstrndup)}},
TR::SinkProp({{1}}, {{0, 1}}, {{ReturnValueIndex}},
MsgTaintedBufferSize)},
- {{CDF_MaybeBuiltin, {"bcopy"}},
+ {{CDF_MaybeBuiltin, {{"bcopy"}}},
TR::SinkProp({{2}}, {{0, 2}}, {{1}}, MsgTaintedBufferSize)}};
// `getenv` returns taint only in untrusted environments.
@@ -710,7 +713,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
// void setproctitle_init(int argc, char *argv[], char *envp[])
GlobalCRules.push_back(
{{{"setproctitle_init"}}, TR::Sink({{1, 2}}, MsgCustomSink)});
- GlobalCRules.push_back({{"getenv"}, TR::Source({{ReturnValueIndex}})});
+ GlobalCRules.push_back({{{"getenv"}}, TR::Source({{ReturnValueIndex}})});
}
StaticTaintRules.emplace(std::make_move_iterator(GlobalCRules.begin()),
@@ -723,7 +726,7 @@ void GenericTaintChecker::initTaintRules(CheckerContext &C) const {
std::string Option{"Config"};
StringRef ConfigFile =
Mgr->getAnalyzerOptions().getCheckerStringOption(this, Option);
- llvm::Optional<TaintConfiguration> Config =
+ std::optional<TaintConfiguration> Config =
getConfiguration<TaintConfiguration>(*Mgr, this, Option, ConfigFile);
if (!Config) {
// We don't have external taint config, no parsing required.
@@ -899,7 +902,7 @@ bool GenericTaintRule::UntrustedEnv(CheckerContext &C) {
bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
CheckerContext &C) const {
assert(E);
- Optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))};
+ std::optional<SVal> TaintedSVal{getTaintedPointeeOrPointer(C, C.getSVal(E))};
if (!TaintedSVal)
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index ce334b18386e..b3f2d7f4d268 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -379,8 +379,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
IvarToPropMapTy IvarToPopertyMap;
ObjCInterfaceDecl::PropertyMap PropMap;
- ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
- InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
+ InterfaceD->collectPropertiesToImplement(PropMap);
for (ObjCInterfaceDecl::PropertyMap::iterator
I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 9870746cbe0c..bca10ec96cea 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -29,6 +29,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/Support/Unicode.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -1004,7 +1005,7 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Satisfied)
return nullptr;
- Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>();
+ std::optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>();
if (!Point)
return nullptr;
@@ -1141,7 +1142,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);
}
- llvm::Optional<llvm::MemoryBufferRef> BF =
+ std::optional<llvm::MemoryBufferRef> BF =
Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL);
if (!BF)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index 139bc0e99d78..153a0a51e980 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -30,6 +30,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -86,7 +87,7 @@ class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>,
#undef CALL
};
- CallDescription OsRefRetain{"os_ref_retain", 1};
+ CallDescription OsRefRetain{{"os_ref_retain"}, 1};
void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const;
@@ -157,7 +158,7 @@ static bool isInMIGCall(CheckerContext &C) {
const Decl *D = SFC->getDecl();
- if (Optional<AnyCall> AC = AnyCall::forDecl(D)) {
+ if (std::optional<AnyCall> AC = AnyCall::forDecl(D)) {
// Even though there's a Sema warning when the return type of an annotated
// function is not a kern_return_t, this warning isn't an error, so we need
// an extra check here.
diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index 635eb00e4ca9..c1b85ace3e2d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -209,7 +210,7 @@ static SymbolRef getAsPointeeSymbol(const Expr *Expr,
ProgramStateRef State = C.getState();
SVal ArgV = C.getSVal(Expr);
- if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
StoreManager& SM = C.getStoreManager();
SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
if (sym)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index adedc9c30fad..f05cd9227b65 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -86,6 +86,7 @@
#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <functional>
+#include <optional>
#include <utility>
using namespace clang;
@@ -220,10 +221,10 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C);
/// Update the RefState to reflect the new memory allocation.
/// The optional \p RetVal parameter specifies the newly allocated pointer
/// value; if unspecified, the value of expression \p E is used.
-static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
- ProgramStateRef State,
- AllocationFamily Family,
- Optional<SVal> RetVal = None);
+static ProgramStateRef
+MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
+ AllocationFamily Family,
+ std::optional<SVal> RetVal = std::nullopt);
//===----------------------------------------------------------------------===//
// The modeling of memory reallocation.
@@ -391,10 +392,10 @@ private:
const CallEvent &Call, CheckerContext &C)>;
const CallDescriptionMap<CheckFn> FreeingMemFnMap{
- {{"free", 1}, &MallocChecker::checkFree},
- {{"if_freenameindex", 1}, &MallocChecker::checkIfFreeNameIndex},
- {{"kfree", 1}, &MallocChecker::checkFree},
- {{"g_free", 1}, &MallocChecker::checkFree},
+ {{{"free"}, 1}, &MallocChecker::checkFree},
+ {{{"if_freenameindex"}, 1}, &MallocChecker::checkIfFreeNameIndex},
+ {{{"kfree"}, 1}, &MallocChecker::checkFree},
+ {{{"g_free"}, 1}, &MallocChecker::checkFree},
};
bool isFreeingCall(const CallEvent &Call) const;
@@ -403,61 +404,60 @@ private:
friend class NoOwnershipChangeVisitor;
CallDescriptionMap<CheckFn> AllocatingMemFnMap{
- {{"alloca", 1}, &MallocChecker::checkAlloca},
- {{"_alloca", 1}, &MallocChecker::checkAlloca},
- {{"malloc", 1}, &MallocChecker::checkBasicAlloc},
- {{"malloc", 3}, &MallocChecker::checkKernelMalloc},
- {{"calloc", 2}, &MallocChecker::checkCalloc},
- {{"valloc", 1}, &MallocChecker::checkBasicAlloc},
- {{CDF_MaybeBuiltin, "strndup", 2}, &MallocChecker::checkStrdup},
- {{CDF_MaybeBuiltin, "strdup", 1}, &MallocChecker::checkStrdup},
- {{"_strdup", 1}, &MallocChecker::checkStrdup},
- {{"kmalloc", 2}, &MallocChecker::checkKernelMalloc},
- {{"if_nameindex", 1}, &MallocChecker::checkIfNameIndex},
- {{CDF_MaybeBuiltin, "wcsdup", 1}, &MallocChecker::checkStrdup},
- {{CDF_MaybeBuiltin, "_wcsdup", 1}, &MallocChecker::checkStrdup},
- {{"g_malloc", 1}, &MallocChecker::checkBasicAlloc},
- {{"g_malloc0", 1}, &MallocChecker::checkGMalloc0},
- {{"g_try_malloc", 1}, &MallocChecker::checkBasicAlloc},
- {{"g_try_malloc0", 1}, &MallocChecker::checkGMalloc0},
- {{"g_memdup", 2}, &MallocChecker::checkGMemdup},
- {{"g_malloc_n", 2}, &MallocChecker::checkGMallocN},
- {{"g_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
- {{"g_try_malloc_n", 2}, &MallocChecker::checkGMallocN},
- {{"g_try_malloc0_n", 2}, &MallocChecker::checkGMallocN0},
+ {{{"alloca"}, 1}, &MallocChecker::checkAlloca},
+ {{{"_alloca"}, 1}, &MallocChecker::checkAlloca},
+ {{{"malloc"}, 1}, &MallocChecker::checkBasicAlloc},
+ {{{"malloc"}, 3}, &MallocChecker::checkKernelMalloc},
+ {{{"calloc"}, 2}, &MallocChecker::checkCalloc},
+ {{{"valloc"}, 1}, &MallocChecker::checkBasicAlloc},
+ {{CDF_MaybeBuiltin, {"strndup"}, 2}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, {"strdup"}, 1}, &MallocChecker::checkStrdup},
+ {{{"_strdup"}, 1}, &MallocChecker::checkStrdup},
+ {{{"kmalloc"}, 2}, &MallocChecker::checkKernelMalloc},
+ {{{"if_nameindex"}, 1}, &MallocChecker::checkIfNameIndex},
+ {{CDF_MaybeBuiltin, {"wcsdup"}, 1}, &MallocChecker::checkStrdup},
+ {{CDF_MaybeBuiltin, {"_wcsdup"}, 1}, &MallocChecker::checkStrdup},
+ {{{"g_malloc"}, 1}, &MallocChecker::checkBasicAlloc},
+ {{{"g_malloc0"}, 1}, &MallocChecker::checkGMalloc0},
+ {{{"g_try_malloc"}, 1}, &MallocChecker::checkBasicAlloc},
+ {{{"g_try_malloc0"}, 1}, &MallocChecker::checkGMalloc0},
+ {{{"g_memdup"}, 2}, &MallocChecker::checkGMemdup},
+ {{{"g_malloc_n"}, 2}, &MallocChecker::checkGMallocN},
+ {{{"g_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0},
+ {{{"g_try_malloc_n"}, 2}, &MallocChecker::checkGMallocN},
+ {{{"g_try_malloc0_n"}, 2}, &MallocChecker::checkGMallocN0},
};
CallDescriptionMap<CheckFn> ReallocatingMemFnMap{
- {{"realloc", 2},
+ {{{"realloc"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
- {{"reallocf", 2},
+ {{{"reallocf"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)},
- {{"g_realloc", 2},
+ {{{"g_realloc"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
- {{"g_try_realloc", 2},
+ {{{"g_try_realloc"}, 2},
std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)},
- {{"g_realloc_n", 3}, &MallocChecker::checkReallocN},
- {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN},
+ {{{"g_realloc_n"}, 3}, &MallocChecker::checkReallocN},
+ {{{"g_try_realloc_n"}, 3}, &MallocChecker::checkReallocN},
};
bool isMemCall(const CallEvent &Call) const;
// TODO: Remove mutable by moving the initializtaion to the registry function.
- mutable Optional<uint64_t> KernelZeroFlagVal;
+ mutable std::optional<uint64_t> KernelZeroFlagVal;
- using KernelZeroSizePtrValueTy = Optional<int>;
+ using KernelZeroSizePtrValueTy = std::optional<int>;
/// Store the value of macro called `ZERO_SIZE_PTR`.
/// The value is initialized at first use, before first use the outer
/// Optional is empty, afterwards it contains another Optional that indicates
/// if the macro value could be determined, and if yes the value itself.
- mutable Optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
+ mutable std::optional<KernelZeroSizePtrValueTy> KernelZeroSizePtrValue;
/// Process C++ operator new()'s allocation, which is the part of C++
/// new-expression that goes before the constructor.
- LLVM_NODISCARD
- ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call,
- CheckerContext &C,
- AllocationFamily Family) const;
+ [[nodiscard]] ProgramStateRef
+ processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C,
+ AllocationFamily Family) const;
/// Perform a zero-allocation check.
///
@@ -467,11 +467,10 @@ private:
/// 0.
/// \param [in] RetVal Specifies the newly allocated pointer value;
/// if unspecified, the value of expression \p E is used.
- LLVM_NODISCARD
- static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call,
- const unsigned IndexOfSizeArg,
- ProgramStateRef State,
- Optional<SVal> RetVal = None);
+ [[nodiscard]] static ProgramStateRef
+ ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg,
+ ProgramStateRef State,
+ std::optional<SVal> RetVal = std::nullopt);
/// Model functions with the ownership_returns attribute.
///
@@ -489,10 +488,9 @@ private:
/// \param [in] Att The ownership_returns attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
- const OwnershipAttr *Att,
- ProgramStateRef State) const;
+ [[nodiscard]] ProgramStateRef
+ MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call,
+ const OwnershipAttr *Att, ProgramStateRef State) const;
/// Models memory allocation.
///
@@ -503,11 +501,9 @@ private:
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
- const Expr *SizeEx, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family);
+ [[nodiscard]] static ProgramStateRef
+ MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,
+ SVal Init, ProgramStateRef State, AllocationFamily Family);
/// Models memory allocation.
///
@@ -518,16 +514,13 @@ private:
/// malloc leaves it undefined.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call,
- SVal Size, SVal Init,
- ProgramStateRef State,
- AllocationFamily Family);
+ [[nodiscard]] static ProgramStateRef
+ MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init,
+ ProgramStateRef State, AllocationFamily Family);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
- LLVM_NODISCARD
- llvm::Optional<ProgramStateRef>
+ [[nodiscard]] std::optional<ProgramStateRef>
performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const;
@@ -548,10 +541,10 @@ private:
/// \param [in] Att The ownership_takes or ownership_holds attribute.
/// \param [in] State The \c ProgramState right before allocation.
/// \returns The ProgramState right after deallocation.
- LLVM_NODISCARD
- ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call,
- const OwnershipAttr *Att,
- ProgramStateRef State) const;
+ [[nodiscard]] ProgramStateRef FreeMemAttr(CheckerContext &C,
+ const CallEvent &Call,
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const;
/// Models memory deallocation.
///
@@ -572,12 +565,10 @@ private:
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
- LLVM_NODISCARD
- ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call,
- ProgramStateRef State, unsigned Num, bool Hold,
- bool &IsKnownToBeAllocated,
- AllocationFamily Family,
- bool ReturnsNullOnFailure = false) const;
+ [[nodiscard]] ProgramStateRef
+ FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State,
+ unsigned Num, bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
/// Models memory deallocation.
///
@@ -598,12 +589,10 @@ private:
/// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
/// we're modeling returns with Null on failure.
/// \returns The ProgramState right after deallocation.
- LLVM_NODISCARD
- ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
- const CallEvent &Call, ProgramStateRef State,
- bool Hold, bool &IsKnownToBeAllocated,
- AllocationFamily Family,
- bool ReturnsNullOnFailure = false) const;
+ [[nodiscard]] ProgramStateRef
+ FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call,
+ ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated,
+ AllocationFamily Family, bool ReturnsNullOnFailure = false) const;
// TODO: Needs some refactoring, as all other deallocation modeling
// functions are suffering from out parameters and messy code due to how
@@ -618,29 +607,27 @@ private:
/// \param [in] SuffixWithN Whether the reallocation function we're modeling
/// has an '_n' suffix, such as g_realloc_n.
/// \returns The ProgramState right after reallocation.
- LLVM_NODISCARD
- ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call,
- bool ShouldFreeOnFail, ProgramStateRef State,
- AllocationFamily Family,
- bool SuffixWithN = false) const;
+ [[nodiscard]] ProgramStateRef
+ ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail,
+ ProgramStateRef State, AllocationFamily Family,
+ bool SuffixWithN = false) const;
/// Evaluates the buffer size that needs to be allocated.
///
/// \param [in] Blocks The amount of blocks that needs to be allocated.
/// \param [in] BlockBytes The size of a block.
/// \returns The symbolic value of \p Blocks * \p BlockBytes.
- LLVM_NODISCARD
- static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
- const Expr *BlockBytes);
+ [[nodiscard]] static SVal evalMulForBufferSize(CheckerContext &C,
+ const Expr *Blocks,
+ const Expr *BlockBytes);
/// Models zero initialized array allocation.
///
/// \param [in] Call The expression that reallocated memory
/// \param [in] State The \c ProgramState right before reallocation.
/// \returns The ProgramState right after allocation.
- LLVM_NODISCARD
- static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call,
- ProgramStateRef State);
+ [[nodiscard]] static ProgramStateRef
+ CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State);
/// See if deallocation happens in a suspicious context. If so, escape the
/// pointers that otherwise would have been deallocated and return true.
@@ -673,12 +660,11 @@ private:
SymbolRef &EscapingSymbol) const;
/// Implementation of the checkPointerEscape callbacks.
- LLVM_NODISCARD
- ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool IsConstPointerEscape) const;
+ [[nodiscard]] ProgramStateRef
+ checkPointerEscapeAux(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind,
+ bool IsConstPointerEscape) const;
// Implementation of the checkPreStmt and checkEndFunction callbacks.
void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const;
@@ -687,11 +673,11 @@ private:
/// Tells if a given family/call/symbol is tracked by the current checker.
/// Sets CheckKind to the kind of the checker responsible for this
/// family/call/symbol.
- Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
- bool IsALeakCheck = false) const;
+ std::optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck = false) const;
- Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
- bool IsALeakCheck = false) const;
+ std::optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck = false) const;
///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
@@ -870,7 +856,7 @@ protected:
// If a variable is dead (is not referenced directly or indirectly after
// some point), it will be removed from the Store before the end of its
// actual lifetime.
- // This means that that if the ownership status didn't change, CurrOwners
+ // This means that if the ownership status didn't change, CurrOwners
// must be a superset of, but not necessarily equal to ExitOwners.
return !llvm::set_is_subset(ExitOwners, CurrOwners);
}
@@ -1132,7 +1118,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const {
return Func && Func->hasAttr<OwnershipAttr>();
}
-llvm::Optional<ProgramStateRef>
+std::optional<ProgramStateRef>
MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
const ProgramStateRef &State) const {
// 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
@@ -1170,33 +1156,32 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
// Fall back to normal malloc behavior on platforms where we don't
// know M_ZERO.
- return None;
+ return std::nullopt;
}
// We treat the last argument as the flags argument, and callers fall-back to
// normal malloc on a None return. This works for the FreeBSD kernel malloc
// as well as Linux kmalloc.
if (Call.getNumArgs() < 2)
- return None;
+ return std::nullopt;
const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1);
const SVal V = C.getSVal(FlagsEx);
if (!isa<NonLoc>(V)) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
- return None;
+ return std::nullopt;
}
NonLoc Flags = V.castAs<NonLoc>();
- NonLoc ZeroFlag =
- C.getSValBuilder()
- .makeIntVal(KernelZeroFlagVal.value(), FlagsEx->getType())
- .castAs<NonLoc>();
+ NonLoc ZeroFlag = C.getSValBuilder()
+ .makeIntVal(*KernelZeroFlagVal, FlagsEx->getType())
+ .castAs<NonLoc>();
SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
Flags, ZeroFlag,
FlagsEx->getType());
if (MaskedFlagsUC.isUnknownOrUndef())
- return None;
+ return std::nullopt;
DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
// Check if maskedFlags is non-zero.
@@ -1210,7 +1195,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C,
AF_Malloc);
}
- return None;
+ return std::nullopt;
}
SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
@@ -1236,10 +1221,10 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call,
void MallocChecker::checkKernelMalloc(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- llvm::Optional<ProgramStateRef> MaybeState =
+ std::optional<ProgramStateRef> MaybeState =
performKernelMalloc(Call, C, State);
if (MaybeState)
- State = MaybeState.value();
+ State = *MaybeState;
else
State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State,
AF_Malloc);
@@ -1506,7 +1491,7 @@ void MallocChecker::checkPostCall(const CallEvent &Call,
// Performs a 0-sized allocations check.
ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State,
- Optional<SVal> RetVal) {
+ std::optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -1658,7 +1643,7 @@ static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
FirstSlot == "initWithCharactersNoCopy";
}
-static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
+static std::optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
Selector S = Call.getSelector();
// FIXME: We should not rely on fully-constrained symbols being folded.
@@ -1666,7 +1651,7 @@ static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
if (S.getNameForSlot(i).equals("freeWhenDone"))
return !Call.getArgSVal(i).isZeroConstant();
- return None;
+ return std::nullopt;
}
void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
@@ -1677,7 +1662,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
if (!isKnownDeallocObjCMethodName(Call))
return;
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
+ if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
if (!*FreeWhenDone)
return;
@@ -1749,6 +1734,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
// Fill the region with the initialization value.
State = State->bindDefaultInitial(RetVal, Init, LCtx);
+ // If Size is somehow undefined at this point, this line prevents a crash.
+ if (Size.isUndef())
+ Size = UnknownVal();
+
// Set the region's extent.
State = setDynamicExtent(State, RetVal.getAsRegion(),
Size.castAs<DefinedOrUnknownSVal>(), svalBuilder);
@@ -1759,7 +1748,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
ProgramStateRef State,
AllocationFamily Family,
- Optional<SVal> RetVal) {
+ std::optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -2074,7 +2063,7 @@ ProgramStateRef MallocChecker::FreeMemAux(
RefState::getReleased(Family, ParentExpr));
}
-Optional<MallocChecker::CheckKind>
+std::optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(AllocationFamily Family,
bool IsALeakCheck) const {
switch (Family) {
@@ -2083,7 +2072,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
case AF_IfNameIndex: {
if (ChecksEnabled[CK_MallocChecker])
return CK_MallocChecker;
- return None;
+ return std::nullopt;
}
case AF_CXXNew:
case AF_CXXNewArray: {
@@ -2095,12 +2084,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
if (ChecksEnabled[CK_NewDeleteChecker])
return CK_NewDeleteChecker;
}
- return None;
+ return std::nullopt;
}
case AF_InnerBuffer: {
if (ChecksEnabled[CK_InnerPointerChecker])
return CK_InnerPointerChecker;
- return None;
+ return std::nullopt;
}
case AF_None: {
llvm_unreachable("no family");
@@ -2109,7 +2098,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
llvm_unreachable("unhandled family");
}
-Optional<MallocChecker::CheckKind>
+std::optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
bool IsALeakCheck) const {
if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym))
@@ -2121,11 +2110,13 @@ MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
}
bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
- if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> IntVal =
+ V.getAs<nonloc::ConcreteInt>())
os << "an integer (" << IntVal->getValue() << ")";
- else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>())
+ else if (std::optional<loc::ConcreteInt> ConstAddr =
+ V.getAs<loc::ConcreteInt>())
os << "a constant address (" << ConstAddr->getValue() << ")";
- else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
+ else if (std::optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
os << "the address of the label '" << Label->getLabel()->getName() << "'";
else
return false;
@@ -2217,7 +2208,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind)
return;
@@ -2258,7 +2249,7 @@ void MallocChecker::HandleNonHeapDealloc(CheckerContext &C, SVal ArgVal,
void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal,
SourceRange Range) const {
- Optional<MallocChecker::CheckKind> CheckKind;
+ std::optional<MallocChecker::CheckKind> CheckKind;
if (ChecksEnabled[CK_MallocChecker])
CheckKind = CK_MallocChecker;
@@ -2350,7 +2341,7 @@ void MallocChecker::HandleOffsetFree(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind)
return;
@@ -2407,7 +2398,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind)
return;
@@ -2446,7 +2437,7 @@ void MallocChecker::HandleDoubleFree(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind)
return;
@@ -2476,7 +2467,7 @@ void MallocChecker::HandleDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind)
return;
@@ -2503,7 +2494,7 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
if (!CheckKind)
return;
@@ -2536,7 +2527,7 @@ void MallocChecker::HandleFunctionPtrFree(CheckerContext &C, SVal ArgVal,
return;
}
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ std::optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
if (!CheckKind)
return;
@@ -2747,8 +2738,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
if (Family == AF_Alloca)
return;
- Optional<MallocChecker::CheckKind>
- CheckKind = getCheckIfTracked(Family, true);
+ std::optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(Family, true);
if (!CheckKind)
return;
@@ -3151,7 +3142,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// about, we can't be sure that the object will use free() to deallocate the
// memory, so we can't model it explicitly. The best we can do is use it to
// decide whether the pointer escapes.
- if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
+ if (std::optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
return *FreeWhenDone;
// If the first selector piece ends with "NoCopy", and there is no
@@ -3570,7 +3561,8 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
const RefState *RefS = State->get<RegionState>(I.getKey());
AllocationFamily Family = RefS->getAllocationFamily();
- Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ std::optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(Family);
if (!CheckKind)
CheckKind = getCheckIfTracked(Family, true);
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index a6e8fcd425d5..5266df2ae6a6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -308,26 +309,27 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
CFGBlock *block = *it;
for (CFGBlock::iterator bi = block->begin(), be = block->end();
bi != be; ++bi) {
- if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) {
- if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
- // Get the callee.
- const FunctionDecl *FD = TheCall->getDirectCallee();
-
- if (!FD)
- continue;
-
- // Get the name of the callee. If it's a builtin, strip off the prefix.
- IdentifierInfo *FnInfo = FD->getIdentifier();
- if (!FnInfo)
- continue;
-
- if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) {
- if (TheCall->getNumArgs() == 1)
- CheckMallocArgument(PossibleMallocOverflows, TheCall,
- mgr.getASTContext());
+ if (std::optional<CFGStmt> CS = bi->getAs<CFGStmt>()) {
+ if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
+ // Get the callee.
+ const FunctionDecl *FD = TheCall->getDirectCallee();
+
+ if (!FD)
+ continue;
+
+ // Get the name of the callee. If it's a builtin, strip off the
+ // prefix.
+ IdentifierInfo *FnInfo = FD->getIdentifier();
+ if (!FnInfo)
+ continue;
+
+ if (FnInfo->isStr("malloc") || FnInfo->isStr("_MALLOC")) {
+ if (TheCall->getNumArgs() == 1)
+ CheckMallocArgument(PossibleMallocOverflows, TheCall,
+ mgr.getASTContext());
+ }
}
}
- }
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
index 1960873599f7..2020dc7cc791 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MismatchedIteratorChecker.cpp
@@ -176,8 +176,10 @@ void MismatchedIteratorChecker::checkPreCall(const CallEvent &Call,
const auto *Param = Func->getParamDecl(J);
const auto *ParamType =
Param->getType()->getAs<SubstTemplateTypeParmType>();
- if (!ParamType ||
- ParamType->getReplacedParameter()->getDecl() != TPDecl)
+ if (!ParamType)
+ continue;
+ const TemplateTypeParmDecl *D = ParamType->getReplacedParameter();
+ if (D != TPDecl)
continue;
if (LHS.isUndef()) {
LHS = Call.getArgSVal(J);
diff --git a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index 1906ca5c8f55..0b3d635a50a3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -33,7 +33,7 @@ class MmapWriteExecChecker : public Checker<check::PreCall> {
static int ProtRead;
mutable std::unique_ptr<BugType> BT;
public:
- MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
+ MmapWriteExecChecker() : MmapFn({"mmap"}, 6), MprotectFn({"mprotect"}, 3) {}
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
int ProtExecOv;
int ProtReadOv;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index e78c130a9c22..c8ddf3b2c14f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -309,7 +309,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
// If it's not a dereference, we don't care if it was reset to null
// or that it is even a smart pointer.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case SK_NonStd:
case SK_Safe:
OS << "Object";
@@ -587,7 +587,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
break;
// We only care about the type if it's a dereference.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case SK_Unsafe:
OS << " of type '" << RD->getQualifiedNameAsString() << "'";
break;
@@ -618,10 +618,6 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (!IC)
return;
- // Calling a destructor on a moved object is fine.
- if (isa<CXXDestructorCall>(IC))
- return;
-
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisRegion)
return;
@@ -631,6 +627,10 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
if (!MethodDecl)
return;
+ // Calling a destructor on a moved object is fine.
+ if (isa<CXXDestructorDecl>(MethodDecl))
+ return;
+
// We want to investigate the whole object, not only sub-object of a parent
// class in which the encountered method defined.
ThisRegion = ThisRegion->getMostDerivedObjectRegion();
diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index fea35d03cb81..59741dde1eea 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -118,7 +119,7 @@ void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
II = &D->getASTContext().Idents.get("CFErrorRef");
bool hasCFError = false;
- for (auto I : D->parameters()) {
+ for (auto *I : D->parameters()) {
if (IsCFError(I->getType(), II)) {
hasCFError = true;
break;
@@ -197,7 +198,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
const StackFrameContext * SFC = C.getStackFrame();
- if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const VarRegion *VR = R->getAs<VarRegion>())
if (const StackArgumentsSpaceRegion *
diff --git a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index af208e867318..17c3cb4e9e04 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -44,9 +44,11 @@ void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE,
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl()))
BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
- const Expr *Callee = CE.getOriginExpr();
- if (!BuildSinks && Callee)
- BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
+ if (const CallExpr *CExpr = dyn_cast_or_null<CallExpr>(CE.getOriginExpr());
+ CExpr && !BuildSinks) {
+ if (const Expr *C = CExpr->getCallee())
+ BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn();
+ }
if (!BuildSinks && CE.isGlobalCFunction()) {
if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index fb6afd0fdabc..fd47e19cb786 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -136,10 +136,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (!DV)
continue;
- assert(!HasRefTypeParam || isa<Loc>(DV.value()));
+ assert(!HasRefTypeParam || isa<Loc>(*DV));
// Process the case when the argument is not a location.
- if (ExpectedToBeNonNull && !isa<Loc>(DV.value())) {
+ if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {
// If the argument is a union type, we want to handle a potential
// transparent_union GCC extension.
if (!ArgE)
diff --git a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index c5437b16c688..72c6a869d225 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -26,6 +26,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -77,7 +78,8 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
if (isGlobalConstString(location)) {
SVal V = State->getSVal(location.castAs<Loc>());
- Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>();
+ std::optional<DefinedOrUnknownSVal> Constr =
+ V.getAs<DefinedOrUnknownSVal>();
if (Constr) {
@@ -91,7 +93,7 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
/// \param V loaded lvalue.
/// \return whether @c val is a string-like const global.
bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
- Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
if (!RegionVal)
return false;
auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());
@@ -109,17 +111,20 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
// Look through the typedefs.
while (const Type *T = Ty.getTypePtr()) {
- if (const auto *TT = dyn_cast<TypedefType>(T)) {
+ if (const auto *AT = dyn_cast<AttributedType>(T)) {
+ if (AT->getAttrKind() == attr::TypeNonNull)
+ return true;
+ Ty = AT->getModifiedType();
+ } else if (const auto *ET = dyn_cast<ElaboratedType>(T)) {
+ const auto *TT = dyn_cast<TypedefType>(ET->getNamedType());
+ if (!TT)
+ return false;
Ty = TT->getDecl()->getUnderlyingType();
// It is sufficient for any intermediate typedef
// to be classified const.
HasConst = HasConst || Ty.isConstQualified();
if (isNonnullType(Ty) && HasConst)
return true;
- } else if (const auto *AT = dyn_cast<AttributedType>(T)) {
- if (AT->getAttrKind() == attr::TypeNonNull)
- return true;
- Ty = AT->getModifiedType();
} else {
return false;
}
@@ -136,7 +141,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {
if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {
return T->getInterfaceDecl() &&
T->getInterfaceDecl()->getIdentifier() == NSStringII;
- } else if (auto *T = dyn_cast<TypedefType>(Ty)) {
+ } else if (auto *T = Ty->getAs<TypedefType>()) {
IdentifierInfo* II = T->getDecl()->getIdentifier();
return II == CFStringRefII || II == CFBooleanRefII || II == CFNullRefII;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index 1d8835f6b474..da8529f4ea81 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -80,7 +80,7 @@ enum class ErrorKind : int {
class NullabilityChecker
: public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
check::PostCall, check::PostStmt<ExplicitCastExpr>,
- check::PostObjCMessage, check::DeadSymbols,
+ check::PostObjCMessage, check::DeadSymbols, eval::Assume,
check::Location, check::Event<ImplicitNullDerefEvent>> {
public:
@@ -102,6 +102,8 @@ public:
void checkEvent(ImplicitNullDerefEvent Event) const;
void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
CheckerContext &C) const;
+ ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const;
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
const char *Sep) const override;
@@ -129,7 +131,7 @@ public:
// When set to false no nullability information will be tracked in
// NullabilityMap. It is possible to catch errors like passing a null pointer
// to a callee that expects nonnull argument without the information that is
- // stroed in the NullabilityMap. This is an optimization.
+ // stored in the NullabilityMap. This is an optimization.
bool NeedTracking = false;
private:
@@ -230,10 +232,41 @@ bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
}
+// For the purpose of tracking historical property accesses, the key for lookup
+// is an object pointer (could be an instance or a class) paired with the unique
+// identifier for the property being invoked on that object.
+using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>;
+
+// Metadata associated with the return value from a recorded property access.
+struct ConstrainedPropertyVal {
+ // This will reference the conjured return SVal for some call
+ // of the form [object property]
+ DefinedOrUnknownSVal Value;
+
+ // If the SVal has been determined to be nonnull, that is recorded here
+ bool isConstrainedNonnull;
+
+ ConstrainedPropertyVal(DefinedOrUnknownSVal SV)
+ : Value(SV), isConstrainedNonnull(false) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ Value.Profile(ID);
+ ID.AddInteger(isConstrainedNonnull ? 1 : 0);
+ }
+};
+
+bool operator==(const ConstrainedPropertyVal &Lhs,
+ const ConstrainedPropertyVal &Rhs) {
+ return Lhs.Value == Rhs.Value &&
+ Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull;
+}
+
} // end anonymous namespace
REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *,
NullabilityState)
+REGISTER_MAP_WITH_PROGRAMSTATE(PropertyAccessesMap, ObjectPropPair,
+ ConstrainedPropertyVal)
// We say "the nullability type invariant is violated" when a location with a
// non-null type contains NULL or a function with a non-null return type returns
@@ -285,8 +318,11 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
const MemRegion *Region = RegionSVal->getRegion();
if (CheckSuperRegion) {
- if (auto FieldReg = Region->getAs<FieldRegion>())
+ if (const SubRegion *FieldReg = Region->getAs<FieldRegion>()) {
+ if (const auto *ER = dyn_cast<ElementRegion>(FieldReg->getSuperRegion()))
+ FieldReg = ER;
return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion());
+ }
if (auto ElementReg = Region->getAs<ElementRegion>())
return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
}
@@ -464,6 +500,19 @@ void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
State = State->remove<NullabilityMap>(I->first);
}
}
+
+ // When an object goes out of scope, we can free the history associated
+ // with any property accesses on that object
+ PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>();
+ for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(),
+ E = PropertyAccesses.end();
+ I != E; ++I) {
+ const MemRegion *ReceiverRegion = I->first.first;
+ if (!SR.isLiveRegion(ReceiverRegion)) {
+ State = State->remove<PropertyAccessesMap>(I->first);
+ }
+ }
+
// When one of the nonnull arguments are constrained to be null, nullability
// preconditions are violated. It is not enough to check this only when we
// actually report an error, because at that time interesting symbols might be
@@ -851,6 +900,32 @@ static Nullability getReceiverNullability(const ObjCMethodCall &M,
return Nullability::Unspecified;
}
+// The return value of a property access is typically a temporary value which
+// will not be tracked in a persistent manner by the analyzer. We use
+// evalAssume() in order to immediately record constraints on those temporaries
+// at the time they are imposed (e.g. by a nil-check conditional).
+ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const {
+ PropertyAccessesMapTy PropertyAccesses = State->get<PropertyAccessesMap>();
+ for (PropertyAccessesMapTy::iterator I = PropertyAccesses.begin(),
+ E = PropertyAccesses.end();
+ I != E; ++I) {
+ if (!I->second.isConstrainedNonnull) {
+ ConditionTruthVal IsNonNull = State->isNonNull(I->second.Value);
+ if (IsNonNull.isConstrainedTrue()) {
+ ConstrainedPropertyVal Replacement = I->second;
+ Replacement.isConstrainedNonnull = true;
+ State = State->set<PropertyAccessesMap>(I->first, Replacement);
+ } else if (IsNonNull.isConstrainedFalse()) {
+ // Space optimization: no point in tracking constrained-null cases
+ State = State->remove<PropertyAccessesMap>(I->first);
+ }
+ }
+ }
+
+ return State;
+}
+
/// Calculate the nullability of the result of a message expr based on the
/// nullability of the receiver, the nullability of the return value, and the
/// constraints.
@@ -907,7 +982,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// this class of methods reduced the emitted diagnostics by about 30% on
// some projects (and all of that was false positives).
if (Name.contains("String")) {
- for (auto Param : M.parameters()) {
+ for (auto *Param : M.parameters()) {
if (Param->getName() == "encoding") {
State = State->set<NullabilityMap>(ReturnRegion,
Nullability::Contradicted);
@@ -947,12 +1022,53 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// No tracked information. Use static type information for return value.
Nullability RetNullability = getNullabilityAnnotation(RetType);
- // Properties might be computed. For this reason the static analyzer creates a
- // new symbol each time an unknown property is read. To avoid false pozitives
- // do not treat unknown properties as nullable, even when they explicitly
- // marked nullable.
- if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined)
- RetNullability = Nullability::Nonnull;
+ // Properties might be computed, which means the property value could
+ // theoretically change between calls even in commonly-observed cases like
+ // this:
+ //
+ // if (foo.prop) { // ok, it's nonnull here...
+ // [bar doStuffWithNonnullVal:foo.prop]; // ...but what about
+ // here?
+ // }
+ //
+ // If the property is nullable-annotated, a naive analysis would lead to many
+ // false positives despite the presence of probably-correct nil-checks. To
+ // reduce the false positive rate, we maintain a history of the most recently
+ // observed property value. For each property access, if the prior value has
+ // been constrained to be not nil then we will conservatively assume that the
+ // next access can be inferred as nonnull.
+ if (RetNullability != Nullability::Nonnull &&
+ M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) {
+ bool LookupResolved = false;
+ if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) {
+ if (IdentifierInfo *Ident = M.getSelector().getIdentifierInfoForSlot(0)) {
+ LookupResolved = true;
+ ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident);
+ const ConstrainedPropertyVal *PrevPropVal =
+ State->get<PropertyAccessesMap>(Key);
+ if (PrevPropVal && PrevPropVal->isConstrainedNonnull) {
+ RetNullability = Nullability::Nonnull;
+ } else {
+ // If a previous property access was constrained as nonnull, we hold
+ // on to that constraint (effectively inferring that all subsequent
+ // accesses on that code path can be inferred as nonnull). If the
+ // previous property access was *not* constrained as nonnull, then
+ // let's throw it away in favor of keeping the SVal associated with
+ // this more recent access.
+ if (auto ReturnSVal =
+ M.getReturnValue().getAs<DefinedOrUnknownSVal>()) {
+ State = State->set<PropertyAccessesMap>(
+ Key, ConstrainedPropertyVal(*ReturnSVal));
+ }
+ }
+ }
+ }
+
+ if (!LookupResolved) {
+ // Fallback: err on the side of suppressing the false positive.
+ RetNullability = Nullability::Nonnull;
+ }
+ }
Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
if (ComputedNullab == Nullability::Nullable) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
index 3e9fc696f8e6..f217520d8f4a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -196,12 +196,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
AnalysisManager &AM,
BugReporter &BR) const {
// Currently this matches CoreFoundation opaque pointer typedefs.
- auto CSuspiciousNumberObjectExprM =
- expr(ignoringParenImpCasts(
- expr(hasType(
- typedefType(hasDeclaration(anyOf(
- typedefDecl(hasName("CFNumberRef")),
- typedefDecl(hasName("CFBooleanRef")))))))
+ auto CSuspiciousNumberObjectExprM = expr(ignoringParenImpCasts(
+ expr(hasType(elaboratedType(namesType(typedefType(
+ hasDeclaration(anyOf(typedefDecl(hasName("CFNumberRef")),
+ typedefDecl(hasName("CFBooleanRef")))))))))
.bind("c_object")));
// Currently this matches XNU kernel number-object pointers.
@@ -240,8 +238,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
// The .bind here is in order to compose the error message more accurately.
auto ObjCSuspiciousScalarBooleanTypeM =
- qualType(typedefType(hasDeclaration(
- typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
+ qualType(elaboratedType(namesType(
+ typedefType(hasDeclaration(typedefDecl(hasName("BOOL")))))))
+ .bind("objc_bool_type");
// The .bind here is in order to compose the error message more accurately.
auto SuspiciousScalarBooleanTypeM =
@@ -253,9 +252,9 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
// for storing pointers.
auto SuspiciousScalarNumberTypeM =
qualType(hasCanonicalType(isInteger()),
- unless(typedefType(hasDeclaration(
- typedefDecl(matchesName("^::u?intptr_t$"))))))
- .bind("int_type");
+ unless(elaboratedType(namesType(typedefType(hasDeclaration(
+ typedefDecl(matchesName("^::u?intptr_t$"))))))))
+ .bind("int_type");
auto SuspiciousScalarTypeM =
qualType(anyOf(SuspiciousScalarBooleanTypeM,
diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
index 6688278a7a33..2b008d1c775a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -72,7 +72,7 @@ class WalkAST : public StmtVisitor<WalkAST> {
public:
WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
: BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
- PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
+ PtrWidth(ASTC.getTargetInfo().getPointerWidth(LangAS::Default)) {}
// Statement visitor methods.
void VisitChildren(Stmt *S);
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index e877dd119ff6..929bd6bc3eb3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -87,7 +87,7 @@ private:
CheckerKind CheckKind) const;
CallDescriptionMap<FnCheck> PThreadCallbacks = {
// Init.
- {{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
+ {{{"pthread_mutex_init"}, 2}, &PthreadLockChecker::InitAnyLock},
// TODO: pthread_rwlock_init(2 arguments).
// TODO: lck_mtx_init(3 arguments).
// TODO: lck_mtx_alloc_init(2 arguments) => returns the mutex.
@@ -95,74 +95,74 @@ private:
// TODO: lck_rw_alloc_init(2 arguments) => returns the mutex.
// Acquire.
- {{"pthread_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"pthread_rwlock_rdlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"pthread_rwlock_wrlock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"lck_mtx_lock", 1}, &PthreadLockChecker::AcquireXNULock},
- {{"lck_rw_lock_exclusive", 1}, &PthreadLockChecker::AcquireXNULock},
- {{"lck_rw_lock_shared", 1}, &PthreadLockChecker::AcquireXNULock},
+ {{{"pthread_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"pthread_rwlock_rdlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"pthread_rwlock_wrlock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"lck_mtx_lock"}, 1}, &PthreadLockChecker::AcquireXNULock},
+ {{{"lck_rw_lock_exclusive"}, 1}, &PthreadLockChecker::AcquireXNULock},
+ {{{"lck_rw_lock_shared"}, 1}, &PthreadLockChecker::AcquireXNULock},
// Try.
- {{"pthread_mutex_trylock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"pthread_rwlock_tryrdlock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"pthread_rwlock_trywrlock", 1}, &PthreadLockChecker::TryPthreadLock},
- {{"lck_mtx_try_lock", 1}, &PthreadLockChecker::TryXNULock},
- {{"lck_rw_try_lock_exclusive", 1}, &PthreadLockChecker::TryXNULock},
- {{"lck_rw_try_lock_shared", 1}, &PthreadLockChecker::TryXNULock},
+ {{{"pthread_mutex_trylock"}, 1}, &PthreadLockChecker::TryPthreadLock},
+ {{{"pthread_rwlock_tryrdlock"}, 1}, &PthreadLockChecker::TryPthreadLock},
+ {{{"pthread_rwlock_trywrlock"}, 1}, &PthreadLockChecker::TryPthreadLock},
+ {{{"lck_mtx_try_lock"}, 1}, &PthreadLockChecker::TryXNULock},
+ {{{"lck_rw_try_lock_exclusive"}, 1}, &PthreadLockChecker::TryXNULock},
+ {{{"lck_rw_try_lock_shared"}, 1}, &PthreadLockChecker::TryXNULock},
// Release.
- {{"pthread_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"pthread_rwlock_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_unlock_exclusive", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_unlock_shared", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"lck_rw_done", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"pthread_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"pthread_rwlock_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"lck_mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"lck_rw_unlock_exclusive"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"lck_rw_unlock_shared"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"lck_rw_done"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
// Destroy.
- {{"pthread_mutex_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
- {{"lck_mtx_destroy", 2}, &PthreadLockChecker::DestroyXNULock},
+ {{{"pthread_mutex_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{{"lck_mtx_destroy"}, 2}, &PthreadLockChecker::DestroyXNULock},
// TODO: pthread_rwlock_destroy(1 argument).
// TODO: lck_rw_destroy(2 arguments).
};
CallDescriptionMap<FnCheck> FuchsiaCallbacks = {
// Init.
- {{"spin_lock_init", 1}, &PthreadLockChecker::InitAnyLock},
+ {{{"spin_lock_init"}, 1}, &PthreadLockChecker::InitAnyLock},
// Acquire.
- {{"spin_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"spin_lock_save", 3}, &PthreadLockChecker::AcquirePthreadLock},
- {{"sync_mutex_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
- {{"sync_mutex_lock_with_waiter", 1},
+ {{{"spin_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"spin_lock_save"}, 3}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"sync_mutex_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"sync_mutex_lock_with_waiter"}, 1},
&PthreadLockChecker::AcquirePthreadLock},
// Try.
- {{"spin_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
- {{"sync_mutex_trylock", 1}, &PthreadLockChecker::TryFuchsiaLock},
- {{"sync_mutex_timedlock", 2}, &PthreadLockChecker::TryFuchsiaLock},
+ {{{"spin_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{{"sync_mutex_trylock"}, 1}, &PthreadLockChecker::TryFuchsiaLock},
+ {{{"sync_mutex_timedlock"}, 2}, &PthreadLockChecker::TryFuchsiaLock},
// Release.
- {{"spin_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
- {{"spin_unlock_restore", 3}, &PthreadLockChecker::ReleaseAnyLock},
- {{"sync_mutex_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"spin_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"spin_unlock_restore"}, 3}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"sync_mutex_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
};
CallDescriptionMap<FnCheck> C11Callbacks = {
// Init.
- {{"mtx_init", 2}, &PthreadLockChecker::InitAnyLock},
+ {{{"mtx_init"}, 2}, &PthreadLockChecker::InitAnyLock},
// Acquire.
- {{"mtx_lock", 1}, &PthreadLockChecker::AcquirePthreadLock},
+ {{{"mtx_lock"}, 1}, &PthreadLockChecker::AcquirePthreadLock},
// Try.
- {{"mtx_trylock", 1}, &PthreadLockChecker::TryC11Lock},
- {{"mtx_timedlock", 2}, &PthreadLockChecker::TryC11Lock},
+ {{{"mtx_trylock"}, 1}, &PthreadLockChecker::TryC11Lock},
+ {{{"mtx_timedlock"}, 2}, &PthreadLockChecker::TryC11Lock},
// Release.
- {{"mtx_unlock", 1}, &PthreadLockChecker::ReleaseAnyLock},
+ {{{"mtx_unlock"}, 1}, &PthreadLockChecker::ReleaseAnyLock},
// Destroy
- {{"mtx_destroy", 1}, &PthreadLockChecker::DestroyPthreadLock},
+ {{{"mtx_destroy"}, 1}, &PthreadLockChecker::DestroyPthreadLock},
};
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index bee7c468812c..01c71d91d1a1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -14,6 +14,7 @@
#include "RetainCountChecker.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -284,7 +285,7 @@ void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
CheckerContext &C) const {
- Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
+ std::optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
if (!IVarLoc)
return;
@@ -412,15 +413,15 @@ static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
return RetTy;
}
-static Optional<RefVal> refValFromRetEffect(RetEffect RE,
- QualType ResultTy) {
+static std::optional<RefVal> refValFromRetEffect(RetEffect RE,
+ QualType ResultTy) {
if (RE.isOwned()) {
return RefVal::makeOwned(RE.getObjKind(), ResultTy);
} else if (RE.notOwned()) {
return RefVal::makeNotOwned(RE.getObjKind(), ResultTy);
}
- return None;
+ return std::nullopt;
}
static bool isPointerToObject(QualType QT) {
@@ -692,7 +693,7 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
assert(Ex);
ResultTy = GetReturnType(Ex, C.getASTContext());
}
- if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
+ if (std::optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
state = setRefBinding(state, Sym, *updatedRefVal);
}
@@ -767,7 +768,7 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
break;
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case DoNothing:
return state;
@@ -907,7 +908,7 @@ bool RetainCountChecker::evalCall(const CallEvent &Call,
const LocationContext *LCtx = C.getLocationContext();
using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
- Optional<BehaviorSummary> BSmr =
+ std::optional<BehaviorSummary> BSmr =
SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
// See if it's one of the specific functions we know how to eval.
@@ -1336,7 +1337,7 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
RetainSummaryManager &SmrMgr = getSummaryManager(Ctx);
const LocationContext *LCtx = Ctx.getLocationContext();
const Decl *D = LCtx->getDecl();
- Optional<AnyCall> C = AnyCall::forDecl(D);
+ std::optional<AnyCall> C = AnyCall::forDecl(D);
if (!C || SmrMgr.isTrustedReferenceCountImplementation(D))
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 5109ae668686..e11e509f159d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -15,6 +15,7 @@
#include "RetainCountChecker.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -165,13 +166,12 @@ static bool shouldGenerateNote(llvm::raw_string_ostream &os,
/// Finds argument index of the out paramter in the call @c S
/// corresponding to the symbol @c Sym.
-/// If none found, returns None.
-static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
- const LocationContext *LCtx,
- SymbolRef &Sym,
- Optional<CallEventRef<>> CE) {
+/// If none found, returns std::nullopt.
+static std::optional<unsigned>
+findArgIdxOfSymbol(ProgramStateRef CurrSt, const LocationContext *LCtx,
+ SymbolRef &Sym, std::optional<CallEventRef<>> CE) {
if (!CE)
- return None;
+ return std::nullopt;
for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
@@ -179,25 +179,25 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
return Idx;
- return None;
+ return std::nullopt;
}
-static Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
+static std::optional<std::string> findMetaClassAlloc(const Expr *Callee) {
if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
if (ME->getMemberDecl()->getNameAsString() != "alloc")
- return None;
+ return std::nullopt;
const Expr *This = ME->getBase()->IgnoreParenImpCasts();
if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
const ValueDecl *VD = DRE->getDecl();
if (VD->getNameAsString() != "metaClass")
- return None;
+ return std::nullopt;
if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
return RD->getNameAsString();
}
}
- return None;
+ return std::nullopt;
}
static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
@@ -250,7 +250,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
}
}
- Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
+ std::optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
// If index is not found, we assume that the symbol was returned.
@@ -602,16 +602,17 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
return std::move(P);
}
-static Optional<std::string> describeRegion(const MemRegion *MR) {
+static std::optional<std::string> describeRegion(const MemRegion *MR) {
if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
return std::string(VR->getDecl()->getName());
// Once we support more storage locations for bindings,
// this would need to be improved.
- return None;
+ return std::nullopt;
}
using Bindings = llvm::SmallVector<std::pair<const MemRegion *, SVal>, 4>;
+namespace {
class VarBindingsCollector : public StoreManager::BindingsHandler {
SymbolRef Sym;
Bindings &Result;
@@ -632,6 +633,7 @@ public:
return true;
}
};
+} // namespace
Bindings getAllVarBindingsForSymbol(ProgramStateManager &Manager,
const ExplodedNode *Node, SymbolRef Sym) {
@@ -728,7 +730,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
const LocationContext *InterestingMethodContext = nullptr;
if (InitMethodContext) {
const ProgramPoint AllocPP = AllocationNode->getLocation();
- if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
if (ME->getMethodFamily() == OMF_alloc)
InterestingMethodContext = InitMethodContext;
@@ -771,7 +773,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << "Object leaked: ";
- Optional<std::string> RegionDescription = describeRegion(LastBinding);
+ std::optional<std::string> RegionDescription = describeRegion(LastBinding);
if (RegionDescription) {
os << "object allocated and stored into '" << *RegionDescription << '\'';
} else {
@@ -917,7 +919,7 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) {
llvm::raw_string_ostream os(Description);
os << "Potential leak of an object";
- Optional<std::string> RegionDescription =
+ std::optional<std::string> RegionDescription =
describeRegion(AllocBindingToReport);
if (RegionDescription) {
os << " stored into '" << *RegionDescription << '\'';
@@ -969,7 +971,7 @@ void RefLeakReport::findBindingToReport(CheckerContext &Ctx,
// Let's pick one of them at random (if there is something to pick from).
AllocBindingToReport = AllVarBindings[0].first;
- // Because 'AllocBindingToReport' is not the the same as
+ // Because 'AllocBindingToReport' is not the same as
// 'AllocFirstBinding', we need to explain how the leaking object
// got from one to another.
//
diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
index cf97439a468d..c3112ebe4e79 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -17,8 +17,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -70,11 +70,11 @@ static std::string getName(const CallEvent &Call) {
// The predefinitions ('CDM') could break due to the ever growing code base.
// Check for the expected invariants and see whether they apply.
-static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
- CheckerContext &C) {
+static std::optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
+ CheckerContext &C) {
auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
if (!ReturnDV)
- return None;
+ return std::nullopt;
if (ExpectedValue)
return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
@@ -90,7 +90,8 @@ void ReturnValueChecker::checkPostCall(const CallEvent &Call,
SVal ReturnV = Call.getReturnValue();
bool ExpectedValue = *RawExpectedValue;
- Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ std::optional<bool> IsInvariantBreak =
+ isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
@@ -137,7 +138,8 @@ void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
bool ExpectedValue = *RawExpectedValue;
- Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ std::optional<bool> IsInvariantBreak =
+ isInvariantBreak(ExpectedValue, ReturnV, C);
if (!IsInvariantBreak)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 8c87a548fd91..9251c895614c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -90,7 +90,7 @@ public:
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
SimpleStreamChecker::SimpleStreamChecker()
- : OpenFn("fopen"), CloseFn("fclose", 1) {
+ : OpenFn({"fopen"}), CloseFn({"fclose"}, 1) {
// Initialize the bug types.
DoubleCloseBugType.reset(
new BugType(this, "Double fclose", "Unix Stream API Error"));
diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index 92eef20d2daa..5689a63f8dd8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -33,6 +33,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/ErrorHandling.h"
+#include <optional>
#include <string>
using namespace clang;
@@ -85,10 +86,10 @@ private:
using SmartPtrMethodHandlerFn =
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
- {{"reset"}, &SmartPtrModeling::handleReset},
- {{"release"}, &SmartPtrModeling::handleRelease},
- {{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
- {{"get"}, &SmartPtrModeling::handleGet}};
+ {{{"reset"}}, &SmartPtrModeling::handleReset},
+ {{{"release"}}, &SmartPtrModeling::handleRelease},
+ {{{"swap"}, 1}, &SmartPtrModeling::handleSwapMethod},
+ {{{"get"}}, &SmartPtrModeling::handleGet}};
const CallDescription StdSwapCall{{"std", "swap"}, 2};
const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
const CallDescription StdMakeUniqueForOverwriteCall{
@@ -298,8 +299,9 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
if (!ModelSmartPtrDereference)
return false;
-
- const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction();
+
+ const std::optional<SVal> ThisRegionOpt =
+ Call.getReturnValueUnderConstruction();
if (!ThisRegionOpt)
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index e6cea0fbff8c..c4b7411e9401 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -61,7 +61,6 @@ private:
ASTContext &Ctx);
static SmallVector<const MemRegion *, 4>
getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
- static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
};
} // namespace
@@ -110,13 +109,6 @@ SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
return range;
}
-bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
- CheckerContext &C) {
- assert(R && "MemRegion should not be null");
- return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
- isa<BlockDataRegion>(R);
-}
-
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
CheckerContext &C) {
const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
@@ -214,7 +206,7 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
void StackAddrEscapeChecker::checkReturnedBlockCaptures(
const BlockDataRegion &B, CheckerContext &C) const {
for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
- if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
+ if (isNotInCurrentFrame(Region, C))
continue;
ExplodedNode *N = C.generateNonFatalErrorNode();
if (!N)
@@ -267,8 +259,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
checkReturnedBlockCaptures(*B, C);
- if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
- isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
+ if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C))
return;
// Returning a record by value is fine. (In this case, the returned
@@ -348,8 +339,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
// Check the globals for the same.
if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
return true;
- if (VR && VR->hasStackStorage() && !isArcManagedBlock(VR, Ctx) &&
- !isNotInCurrentFrame(VR, Ctx))
+ if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))
V.emplace_back(Region, VR);
return true;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 5897e5096461..49b3db560843 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -52,24 +52,12 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include <optional>
#include <string>
using namespace clang;
using namespace clang::ento;
-/// Produce a textual description of the state of \c errno (this describes the
-/// way how it is allowed to be used).
-/// The returned string is insertable into a longer warning message (in the form
-/// "the value 'errno' <...>").
-/// Currently only the \c errno_modeling::MustNotBeChecked state is supported.
-/// But later other kind of errno state may be needed if functions with special
-/// handling of \c errno are added.
-static const char *describeErrnoCheckState(errno_modeling::ErrnoCheckState CS) {
- assert(CS == errno_modeling::MustNotBeChecked &&
- "Errno description not applicable.");
- return "may be undefined after the call and should not be used";
-}
-
namespace {
class StdLibraryFunctionsChecker
: public Checker<check::PreCall, check::PostCall, eval::Call> {
@@ -147,9 +135,18 @@ class StdLibraryFunctionsChecker
virtual StringRef getName() const = 0;
+ // Represents that in which context do we require a description of the
+ // constraint.
+ enum class DescriptionKind {
+ // The constraint is violated.
+ Violation,
+ // We assume that the constraint is satisfied.
+ Assumption
+ };
+
// Give a description that explains the constraint to the user. Used when
// the bug is reported.
- virtual std::string describe(ProgramStateRef State,
+ virtual std::string describe(DescriptionKind DK, ProgramStateRef State,
const Summary &Summary) const {
// There are some descendant classes that are not used as argument
// constraints, e.g. ComparisonConstraint. In that case we can safely
@@ -187,7 +184,7 @@ class StdLibraryFunctionsChecker
RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges)
: ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {}
- std::string describe(ProgramStateRef State,
+ std::string describe(DescriptionKind DK, ProgramStateRef State,
const Summary &Summary) const override;
const IntRangeVector &getRanges() const { return Ranges; }
@@ -257,7 +254,9 @@ class StdLibraryFunctionsChecker
bool CannotBeNull = true;
public:
- std::string describe(ProgramStateRef State,
+ NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true)
+ : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {}
+ std::string describe(DescriptionKind DK, ProgramStateRef State,
const Summary &Summary) const override;
StringRef getName() const override { return "NonNull"; }
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
@@ -300,13 +299,13 @@ class StdLibraryFunctionsChecker
// // Here, ptr is the buffer, and its minimum size is `size * nmemb`.
class BufferSizeConstraint : public ValueConstraint {
// The concrete value which is the minimum size for the buffer.
- llvm::Optional<llvm::APSInt> ConcreteSize;
+ std::optional<llvm::APSInt> ConcreteSize;
// The argument which holds the size of the buffer.
- llvm::Optional<ArgNo> SizeArgN;
+ std::optional<ArgNo> SizeArgN;
// The argument which is a multiplier to size. This is set in case of
// `fread` like functions where the size is computed as a multiplication of
// two arguments.
- llvm::Optional<ArgNo> SizeMultiplierArgN;
+ std::optional<ArgNo> SizeMultiplierArgN;
// The operator we use in apply. This is negated in negate().
BinaryOperator::Opcode Op = BO_LE;
@@ -329,7 +328,7 @@ class StdLibraryFunctionsChecker
return Result;
}
- std::string describe(ProgramStateRef State,
+ std::string describe(DescriptionKind DK, ProgramStateRef State,
const Summary &Summary) const override;
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
@@ -392,45 +391,67 @@ class StdLibraryFunctionsChecker
using ConstraintSet = std::vector<ValueConstraintPtr>;
/// Define how a function affects the system variable 'errno'.
- /// This works together with the ErrnoModeling and ErrnoChecker classes.
+ /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes.
+ /// Currently 3 use cases exist: success, failure, irrelevant.
+ /// In the future the failure case can be customized to set \c errno to a
+ /// more specific constraint (for example > 0), or new case can be added
+ /// for functions which require check of \c errno in both success and failure
+ /// case.
class ErrnoConstraintBase {
public:
/// Apply specific state changes related to the errno variable.
virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const = 0;
- /// Get a description about what is applied to 'errno' and how is it allowed
- /// to be used. If ErrnoChecker generates a bug then this message is
- /// displayed as a note at the function call.
- /// It may return empty string if no note tag is to be added.
- virtual std::string describe(StringRef FunctionName) const { return ""; }
+ /// Get a NoteTag about the changes made to 'errno' and the possible bug.
+ /// It may return \c nullptr (if no bug report from \c ErrnoChecker is
+ /// expected).
+ virtual const NoteTag *describe(CheckerContext &C,
+ StringRef FunctionName) const {
+ return nullptr;
+ }
virtual ~ErrnoConstraintBase() {}
protected:
- /// Many of the descendant classes use this value.
- const errno_modeling::ErrnoCheckState CheckState;
-
- ErrnoConstraintBase(errno_modeling::ErrnoCheckState CS) : CheckState(CS) {}
+ ErrnoConstraintBase() = default;
/// This is used for conjure symbol for errno to differentiate from the
/// original call expression (same expression is used for the errno symbol).
static int Tag;
};
- /// Set value of 'errno' to be related to 0 in a specified way, with a
- /// specified "errno check state". For example with \c BO_GT 'errno' is
- /// constrained to be greater than 0. Use this for failure cases of functions.
- class ZeroRelatedErrnoConstraint : public ErrnoConstraintBase {
- BinaryOperatorKind Op;
+ /// Reset errno constraints to irrelevant.
+ /// This is applicable to functions that may change 'errno' and are not
+ /// modeled elsewhere.
+ class ResetErrnoConstraint : public ErrnoConstraintBase {
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant);
+ }
+ };
+ /// Do not change errno constraints.
+ /// This is applicable to functions that are modeled in another checker
+ /// and the already set errno constraints should not be changed in the
+ /// post-call event.
+ class NoErrnoConstraint : public ErrnoConstraintBase {
public:
- ZeroRelatedErrnoConstraint(clang::BinaryOperatorKind OpK,
- errno_modeling::ErrnoCheckState CS)
- : ErrnoConstraintBase(CS), Op(OpK) {
- assert(BinaryOperator::isComparisonOp(OpK));
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const Summary &Summary,
+ CheckerContext &C) const override {
+ return State;
}
+ };
+ /// Set errno constraint at failure cases of standard functions.
+ /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked
+ /// by the program. \c ErrnoChecker does not emit a bug report after such a
+ /// function call.
+ class FailureErrnoConstraint : public ErrnoConstraintBase {
+ public:
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const override {
@@ -440,62 +461,40 @@ class StdLibraryFunctionsChecker
C.getLocationContext(), C.getASTContext().IntTy,
C.blockCount())
.castAs<NonLoc>();
- NonLoc ZeroVal =
- SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
- DefinedOrUnknownSVal Cond =
- SVB.evalBinOp(State, Op, ErrnoSVal, ZeroVal, SVB.getConditionType())
- .castAs<DefinedOrUnknownSVal>();
- State = State->assume(Cond, true);
- if (!State)
- return State;
- return errno_modeling::setErrnoValue(State, C.getLocationContext(),
- ErrnoSVal, CheckState);
- }
-
- std::string describe(StringRef FunctionName) const override {
- if (CheckState == errno_modeling::Irrelevant)
- return "";
- return (Twine("Assuming that function '") + FunctionName.str() +
- "' fails, in this case the value 'errno' becomes " +
- BinaryOperator::getOpcodeStr(Op).str() + " 0 and " +
- describeErrnoCheckState(CheckState))
- .str();
+ return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal);
}
};
- /// Applies the constraints to 'errno' for a common case when a standard
- /// function is successful. The value of 'errno' after the call is not
- /// specified by the standard (it may change or not). The \c ErrnoChecker can
- /// generate a bug if 'errno' is read afterwards.
+ /// Set errno constraint at success cases of standard functions.
+ /// Success case: 'errno' is not allowed to be used.
+ /// \c ErrnoChecker can emit bug report after such a function call if errno
+ /// is used.
class SuccessErrnoConstraint : public ErrnoConstraintBase {
public:
- SuccessErrnoConstraint()
- : ErrnoConstraintBase(errno_modeling::MustNotBeChecked) {}
-
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const override {
- return errno_modeling::setErrnoState(State, CheckState);
+ return errno_modeling::setErrnoForStdSuccess(State, C);
}
- std::string describe(StringRef FunctionName) const override {
- return (Twine("Assuming that function '") + FunctionName.str() +
- "' is successful, in this case the value 'errno' " +
- describeErrnoCheckState(CheckState))
- .str();
+ const NoteTag *describe(CheckerContext &C,
+ StringRef FunctionName) const override {
+ return errno_modeling::getNoteTagForStdSuccess(C, FunctionName);
}
};
- /// Set errno constraints if use of 'errno' is completely irrelevant to the
- /// modeled function or modeling is not possible.
- class NoErrnoConstraint : public ErrnoConstraintBase {
+ class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase {
public:
- NoErrnoConstraint() : ErrnoConstraintBase(errno_modeling::Irrelevant) {}
-
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const override {
- return errno_modeling::setErrnoState(State, CheckState);
+ return errno_modeling::setErrnoStdMustBeChecked(State, C,
+ Call.getOriginExpr());
+ }
+
+ const NoteTag *describe(CheckerContext &C,
+ StringRef FunctionName) const override {
+ return errno_modeling::getNoteTagForStdMustBeChecked(C, FunctionName);
}
};
@@ -542,8 +541,8 @@ class StdLibraryFunctionsChecker
StringRef getNote() const { return Note; }
};
- using ArgTypes = std::vector<Optional<QualType>>;
- using RetType = Optional<QualType>;
+ using ArgTypes = std::vector<std::optional<QualType>>;
+ using RetType = std::optional<QualType>;
// A placeholder type, we use it whenever we do not care about the concrete
// type in a Signature.
@@ -565,7 +564,7 @@ class StdLibraryFunctionsChecker
// Construct a signature from optional types. If any of the optional types
// are not set then the signature will be invalid.
Signature(ArgTypes ArgTys, RetType RetTy) {
- for (Optional<QualType> Arg : ArgTys) {
+ for (std::optional<QualType> Arg : ArgTys) {
if (!Arg) {
Invalid = true;
return;
@@ -718,10 +717,10 @@ public:
bool ShouldAssumeControlledEnvironment = false;
private:
- Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
- CheckerContext &C) const;
- Optional<Summary> findFunctionSummary(const CallEvent &Call,
- CheckerContext &C) const;
+ std::optional<Summary> findFunctionSummary(const FunctionDecl *FD,
+ CheckerContext &C) const;
+ std::optional<Summary> findFunctionSummary(const CallEvent &Call,
+ CheckerContext &C) const;
void initFunctionSummaries(CheckerContext &C) const;
@@ -746,9 +745,12 @@ private:
// Highlight the range of the argument that was violated.
R->addRange(Call.getArgSourceRange(VC->getArgNo()));
- // Describe the argument constraint in a note.
- R->addNote(VC->describe(C.getState(), Summary), R->getLocation(),
- Call.getArgSourceRange(VC->getArgNo()));
+ // Describe the argument constraint violation in a note.
+ std::string Descr = VC->describe(
+ ValueConstraint::DescriptionKind::Violation, C.getState(), Summary);
+ // Capitalize the first letter b/c we want a full sentence.
+ Descr[0] = toupper(Descr[0]);
+ R->addNote(Descr, R->getLocation(), Call.getArgSourceRange(VC->getArgNo()));
C.emitReport(std::move(R));
}
@@ -758,10 +760,11 @@ private:
/// Usually if a failure return value exists for function, that function
/// needs different cases for success and failure with different errno
/// constraints (and different return value constraints).
- const NoErrnoConstraint ErrnoIrrelevant;
- const SuccessErrnoConstraint ErrnoMustNotBeChecked;
- const ZeroRelatedErrnoConstraint ErrnoNEZeroIrrelevant{
- clang::BinaryOperatorKind::BO_NE, errno_modeling::Irrelevant};
+ const NoErrnoConstraint ErrnoUnchanged{};
+ const ResetErrnoConstraint ErrnoIrrelevant{};
+ const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{};
+ const SuccessErrnoConstraint ErrnoMustNotBeChecked{};
+ const FailureErrnoConstraint ErrnoNEZeroIrrelevant{};
};
int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0;
@@ -778,24 +781,26 @@ static BasicValueFactory &getBVF(ProgramStateRef State) {
}
std::string StdLibraryFunctionsChecker::NotNullConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
SmallString<48> Result;
- Result += "The ";
+ const auto Violation = ValueConstraint::DescriptionKind::Violation;
+ Result += "the ";
Result += getArgDesc(ArgN);
- Result += " should not be NULL";
+ Result += DK == Violation ? " should not be NULL" : " is not NULL";
return Result.c_str();
}
std::string StdLibraryFunctionsChecker::RangeConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
BasicValueFactory &BVF = getBVF(State);
QualType T = Summary.getArgType(getArgNo());
SmallString<48> Result;
- Result += "The ";
+ const auto Violation = ValueConstraint::DescriptionKind::Violation;
+ Result += "the ";
Result += getArgDesc(ArgN);
- Result += " should be ";
+ Result += DK == Violation ? " should be " : " is ";
// Range kind as a string.
Kind == OutOfRange ? Result += "out of" : Result += "within";
@@ -827,16 +832,18 @@ StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) {
SmallString<8> Result;
Result += std::to_string(ArgN + 1);
Result += llvm::getOrdinalSuffix(ArgN + 1);
- Result += " arg";
+ Result += " argument";
return Result;
}
std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe(
- ProgramStateRef State, const Summary &Summary) const {
+ DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const {
SmallString<96> Result;
- Result += "The size of the ";
+ const auto Violation = ValueConstraint::DescriptionKind::Violation;
+ Result += "the size of the ";
Result += getArgDesc(ArgN);
- Result += " should be equal to or less than the value of ";
+ Result += DK == Violation ? " should be " : " is ";
+ Result += "equal to or greater than the value of ";
if (ConcreteSize) {
ConcreteSize->toString(Result);
} else if (SizeArgN) {
@@ -962,7 +969,7 @@ ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply(
void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return;
@@ -970,31 +977,43 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
ProgramStateRef State = C.getState();
ProgramStateRef NewState = State;
+ ExplodedNode *NewNode = C.getPredecessor();
for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) {
ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C);
ProgramStateRef FailureSt =
Constraint->negate()->apply(NewState, Call, Summary, C);
// The argument constraint is not satisfied.
if (FailureSt && !SuccessSt) {
- if (ExplodedNode *N = C.generateErrorNode(NewState))
+ if (ExplodedNode *N = C.generateErrorNode(NewState, NewNode))
reportBug(Call, N, Constraint.get(), Summary, C);
break;
- } else {
- // We will apply the constraint even if we cannot reason about the
- // argument. This means both SuccessSt and FailureSt can be true. If we
- // weren't applying the constraint that would mean that symbolic
- // execution continues on a code whose behaviour is undefined.
- assert(SuccessSt);
- NewState = SuccessSt;
+ }
+ // We will apply the constraint even if we cannot reason about the
+ // argument. This means both SuccessSt and FailureSt can be true. If we
+ // weren't applying the constraint that would mean that symbolic
+ // execution continues on a code whose behaviour is undefined.
+ assert(SuccessSt);
+ NewState = SuccessSt;
+ if (NewState != State) {
+ SmallString<64> Msg;
+ Msg += "Assuming ";
+ Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption,
+ NewState, Summary);
+ const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo());
+ NewNode = C.addTransition(
+ NewState, NewNode,
+ C.getNoteTag([Msg = std::move(Msg), ArgSVal](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
+ if (BR.isInteresting(ArgSVal))
+ OS << Msg;
+ }));
}
}
- if (NewState && NewState != State)
- C.addTransition(NewState);
}
void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return;
@@ -1017,13 +1036,10 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
if (NewState && NewState != State) {
if (Case.getNote().empty()) {
- std::string Note;
+ const NoteTag *NT = nullptr;
if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
- Note = Case.getErrnoConstraint().describe(D->getNameAsString());
- if (Note.empty())
- C.addTransition(NewState);
- else
- C.addTransition(NewState, errno_modeling::getErrnoNoteTag(C, Note));
+ NT = Case.getErrnoConstraint().describe(C, D->getNameAsString());
+ C.addTransition(NewState, NT);
} else {
StringRef Note = Case.getNote();
const NoteTag *Tag = C.getNoteTag(
@@ -1036,13 +1052,23 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
/*IsPrunable=*/true);
C.addTransition(NewState, Tag);
}
+ } else if (NewState == State) {
+ // It is possible that the function was evaluated in a checker callback
+ // where the state constraints are already applied, then no change happens
+ // here to the state (if the ErrnoConstraint did not change it either).
+ // If the evaluated function requires a NoteTag for errno change, it is
+ // added here.
+ if (const auto *D = dyn_cast_or_null<FunctionDecl>(Call.getDecl()))
+ if (const NoteTag *NT =
+ Case.getErrnoConstraint().describe(C, D->getNameAsString()))
+ C.addTransition(NewState, NT);
}
}
}
bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- Optional<Summary> FoundSummary = findFunctionSummary(Call, C);
+ std::optional<Summary> FoundSummary = findFunctionSummary(Call, C);
if (!FoundSummary)
return false;
@@ -1109,26 +1135,26 @@ bool StdLibraryFunctionsChecker::Signature::matches(
return true;
}
-Optional<StdLibraryFunctionsChecker::Summary>
+std::optional<StdLibraryFunctionsChecker::Summary>
StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
CheckerContext &C) const {
if (!FD)
- return None;
+ return std::nullopt;
initFunctionSummaries(C);
auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl());
if (FSMI == FunctionSummaryMap.end())
- return None;
+ return std::nullopt;
return FSMI->second;
}
-Optional<StdLibraryFunctionsChecker::Summary>
+std::optional<StdLibraryFunctionsChecker::Summary>
StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call,
CheckerContext &C) const {
const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
- return None;
+ return std::nullopt;
return findFunctionSummary(FD, C);
}
@@ -1149,11 +1175,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
LookupType(const ASTContext &ACtx) : ACtx(ACtx) {}
// Find the type. If not found then the optional is not set.
- llvm::Optional<QualType> operator()(StringRef Name) {
+ std::optional<QualType> operator()(StringRef Name) {
IdentifierInfo &II = ACtx.Idents.get(Name);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
if (LookupRes.empty())
- return None;
+ return std::nullopt;
// Prioritze typedef declarations.
// This is needed in case of C struct typedefs. E.g.:
@@ -1171,7 +1197,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
for (Decl *D : LookupRes)
if (auto *TD = dyn_cast<TypeDecl>(D))
return ACtx.getTypeDeclType(TD).getCanonicalType();
- return None;
+ return std::nullopt;
}
} lookupTy(ACtx);
@@ -1185,10 +1211,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
QualType operator()(QualType Ty) {
return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty;
}
- Optional<QualType> operator()(Optional<QualType> Ty) {
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
if (Ty)
return operator()(*Ty);
- return None;
+ return std::nullopt;
}
} getRestrictTy(ACtx);
class GetPointerTy {
@@ -1197,16 +1223,16 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
public:
GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {}
QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); }
- Optional<QualType> operator()(Optional<QualType> Ty) {
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
if (Ty)
return operator()(*Ty);
- return None;
+ return std::nullopt;
}
} getPointerTy(ACtx);
class {
public:
- Optional<QualType> operator()(Optional<QualType> Ty) {
- return Ty ? Optional<QualType>(Ty->withConst()) : None;
+ std::optional<QualType> operator()(std::optional<QualType> Ty) {
+ return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt;
}
QualType operator()(QualType Ty) { return Ty.withConst(); }
} getConstTy;
@@ -1215,14 +1241,14 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
public:
GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {}
- Optional<RangeInt> operator()(QualType Ty) {
+ std::optional<RangeInt> operator()(QualType Ty) {
return BVF.getMaxValue(Ty).getLimitedValue();
}
- Optional<RangeInt> operator()(Optional<QualType> Ty) {
+ std::optional<RangeInt> operator()(std::optional<QualType> Ty) {
if (Ty) {
return operator()(*Ty);
}
- return None;
+ return std::nullopt;
}
} getMaxValue(BVF);
@@ -1278,7 +1304,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// The platform dependent value of EOF.
// Try our best to parse this from the Preprocessor, otherwise fallback to -1.
const auto EOFv = [&C]() -> RangeInt {
- if (const llvm::Optional<int> OptInt =
+ if (const std::optional<int> OptInt =
tryExpandAsInteger("EOF", C.getPreprocessor()))
return *OptInt;
return -1;
@@ -1351,13 +1377,13 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto operator()(RangeInt b, RangeInt e) {
return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
}
- auto operator()(RangeInt b, Optional<RangeInt> e) {
+ auto operator()(RangeInt b, std::optional<RangeInt> e) {
if (e)
return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}};
return IntRangeVector{};
}
auto operator()(std::pair<RangeInt, RangeInt> i0,
- std::pair<RangeInt, Optional<RangeInt>> i1) {
+ std::pair<RangeInt, std::optional<RangeInt>> i1) {
if (i1.second)
return IntRangeVector{i0, {i1.first, *(i1.second)}};
return IntRangeVector{i0};
@@ -1370,10 +1396,18 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
auto NotNull = [&](ArgNo ArgN) {
return std::make_shared<NotNullConstraint>(ArgN);
};
+ auto IsNull = [&](ArgNo ArgN) {
+ return std::make_shared<NotNullConstraint>(ArgN, false);
+ };
- Optional<QualType> FileTy = lookupTy("FILE");
- Optional<QualType> FilePtrTy = getPointerTy(FileTy);
- Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
+ std::optional<QualType> FileTy = lookupTy("FILE");
+ std::optional<QualType> FilePtrTy = getPointerTy(FileTy);
+ std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
+
+ std::optional<QualType> FPosTTy = lookupTy("fpos_t");
+ std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy);
+ std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy));
+ std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy);
// We are finally ready to define specifications for all supported functions.
//
@@ -1599,11 +1633,23 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// read()-like functions that never return more than buffer size.
auto FreadSummary =
Summary(NoEvalCall)
- .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
+ ArgumentCondition(2U, WithinRange, Range(1, SizeMax)),
+ ReturnValueCondition(BO_LT, ArgNo(2)),
ReturnValueCondition(WithinRange, Range(0, SizeMax))},
- ErrnoIrrelevant)
+ ErrnoNEZeroIrrelevant)
+ .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)),
+ ReturnValueCondition(BO_EQ, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(0, SizeMax))},
+ ErrnoMustNotBeChecked)
+ .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)),
+ ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoMustNotBeChecked)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(3)))
+ // FIXME: It should be allowed to have a null buffer if any of
+ // args 1 or 2 are zero. Remove NotNull check of arg 0, add a check
+ // for non-null buffer if non-zero size to BufferSizeConstraint?
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
/*BufSizeMultiplier=*/ArgNo(2)));
@@ -1622,8 +1668,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{SizeTy}),
FreadSummary);
- Optional<QualType> Ssize_tTy = lookupTy("ssize_t");
- Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
+ std::optional<QualType> Ssize_tTy = lookupTy("ssize_t");
+ std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
auto ReadSummary =
Summary(NoEvalCall)
@@ -1689,6 +1735,142 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
}
if (ModelPOSIX) {
+ const auto ReturnsZeroOrMinusOne =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))};
+ const auto ReturnsZero =
+ ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))};
+ const auto ReturnsMinusOne =
+ ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))};
+ const auto ReturnsNonnegative =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))};
+ const auto ReturnsNonZero =
+ ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))};
+ const auto ReturnsFileDescriptor =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
+ const auto &ReturnsValidFileDescriptor = ReturnsNonnegative;
+
+ // FILE *fopen(const char *restrict pathname, const char *restrict mode);
+ addToFunctionSummaryMap(
+ "fopen",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy},
+ RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // FILE *tmpfile(void);
+ addToFunctionSummaryMap("tmpfile",
+ Signature(ArgTypes{}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({NotNull(Ret)}, ErrnoMustNotBeChecked)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant));
+
+ // FILE *freopen(const char *restrict pathname, const char *restrict mode,
+ // FILE *restrict stream);
+ addToFunctionSummaryMap(
+ "freopen",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy,
+ FilePtrRestrictTy},
+ RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(BO_EQ, ArgNo(2))},
+ ErrnoMustNotBeChecked)
+ .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
+
+ // int fclose(FILE *stream);
+ addToFunctionSummaryMap(
+ "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))},
+ ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fseek(FILE *stream, long offset, int whence);
+ // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use
+ // these for condition of arg 2.
+ // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2).
+ addToFunctionSummaryMap(
+ "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoMustNotBeChecked)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}})));
+
+ // int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The fgetpos() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "fgetpos",
+ Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoUnchanged)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int fsetpos(FILE *stream, const fpos_t *pos);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The fsetpos() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "fsetpos",
+ Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZero, ErrnoUnchanged)
+ .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // long ftell(FILE *stream);
+ // From 'The Open Group Base Specifications Issue 7, 2018 edition':
+ // "The ftell() function shall not change the setting of errno if
+ // successful."
+ addToFunctionSummaryMap(
+ "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(1, LongMax))},
+ ErrnoUnchanged)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fileno(FILE *stream);
+ addToFunctionSummaryMap(
+ "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked)
+ .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void rewind(FILE *stream);
+ // This function indicates error only by setting of 'errno'.
+ addToFunctionSummaryMap("rewind",
+ Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}),
+ Summary(NoEvalCall)
+ .Case({}, ErrnoMustBeChecked)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // void clearerr(FILE *stream);
+ addToFunctionSummaryMap(
+ "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int feof(FILE *stream);
+ addToFunctionSummaryMap(
+ "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int ferror(FILE *stream);
+ addToFunctionSummaryMap(
+ "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// long a64l(const char *str64);
addToFunctionSummaryMap(
@@ -1702,18 +1884,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(ArgumentCondition(
0, WithinRange, Range(0, LongMax))));
- const auto ReturnsZeroOrMinusOne =
- ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))};
- const auto ReturnsZero =
- ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))};
- const auto ReturnsMinusOne =
- ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))};
- const auto ReturnsNonnegative =
- ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))};
- const auto ReturnsFileDescriptor =
- ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
- const auto &ReturnsValidFileDescriptor = ReturnsNonnegative;
-
// int access(const char *pathname, int amode);
addToFunctionSummaryMap(
"access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
@@ -1777,7 +1947,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(ArgumentCondition(
0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Off_tTy = lookupTy("off_t");
+ std::optional<QualType> Off_tTy = lookupTy("off_t");
// int truncate(const char *path, off_t length);
addToFunctionSummaryMap(
@@ -1819,7 +1989,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Mode_tTy = lookupTy("mode_t");
+ std::optional<QualType> Mode_tTy = lookupTy("mode_t");
// int creat(const char *pathname, mode_t mode);
addToFunctionSummaryMap(
@@ -1836,8 +2006,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
- Optional<QualType> DirTy = lookupTy("DIR");
- Optional<QualType> DirPtrTy = getPointerTy(DirTy);
+ std::optional<QualType> DirTy = lookupTy("DIR");
+ std::optional<QualType> DirPtrTy = getPointerTy(DirTy);
// int dirfd(DIR *dirp);
addToFunctionSummaryMap(
@@ -1920,7 +2090,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Dev_tTy = lookupTy("dev_t");
+ std::optional<QualType> Dev_tTy = lookupTy("dev_t");
// int mknod(const char *pathname, mode_t mode, dev_t dev);
addToFunctionSummaryMap(
@@ -1969,8 +2139,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Uid_tTy = lookupTy("uid_t");
- Optional<QualType> Gid_tTy = lookupTy("gid_t");
+ std::optional<QualType> Uid_tTy = lookupTy("uid_t");
+ std::optional<QualType> Gid_tTy = lookupTy("gid_t");
// int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
// int flags);
@@ -2069,9 +2239,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructStatTy = lookupTy("stat");
- Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy);
- Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy);
+ std::optional<QualType> StructStatTy = lookupTy("stat");
+ std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy);
+ std::optional<QualType> StructStatPtrRestrictTy =
+ getRestrictTy(StructStatPtrTy);
// int fstat(int fd, struct stat *statbuf);
addToFunctionSummaryMap(
@@ -2200,14 +2371,6 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- // int fileno(FILE *stream);
- addToFunctionSummaryMap(
- "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
- Summary(NoEvalCall)
- .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked)
- .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
- .ArgConstraint(NotNull(ArgNo(0))));
-
// int fseeko(FILE *stream, off_t offset, int whence);
addToFunctionSummaryMap(
"fseeko",
@@ -2233,7 +2396,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(4, WithinRange, Range(-1, IntMax))));
- Optional<QualType> Off64_tTy = lookupTy("off64_t");
+ std::optional<QualType> Off64_tTy = lookupTy("off64_t");
// void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
// off64_t offset);
// FIXME: Improve for errno modeling.
@@ -2358,18 +2521,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(NotNull(ArgNo(2))));
- Optional<QualType> StructSockaddrTy = lookupTy("sockaddr");
- Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy);
- Optional<QualType> ConstStructSockaddrPtrTy =
+ std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr");
+ std::optional<QualType> StructSockaddrPtrTy =
+ getPointerTy(StructSockaddrTy);
+ std::optional<QualType> ConstStructSockaddrPtrTy =
getPointerTy(getConstTy(StructSockaddrTy));
- Optional<QualType> StructSockaddrPtrRestrictTy =
+ std::optional<QualType> StructSockaddrPtrRestrictTy =
getRestrictTy(StructSockaddrPtrTy);
- Optional<QualType> ConstStructSockaddrPtrRestrictTy =
+ std::optional<QualType> ConstStructSockaddrPtrRestrictTy =
getRestrictTy(ConstStructSockaddrPtrTy);
- Optional<QualType> Socklen_tTy = lookupTy("socklen_t");
- Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy);
- Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy);
- Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy);
+ std::optional<QualType> Socklen_tTy = lookupTy("socklen_t");
+ std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy);
+ std::optional<QualType> Socklen_tPtrRestrictTy =
+ getRestrictTy(Socklen_tPtrTy);
+ std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy);
// In 'socket.h' of some libc implementations with C99, sockaddr parameter
// is a transparent union of the underlying sockaddr_ family of pointers
@@ -2568,9 +2733,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
/*BufSize=*/ArgNo(2))));
- Optional<QualType> StructMsghdrTy = lookupTy("msghdr");
- Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy);
- Optional<QualType> ConstStructMsghdrPtrTy =
+ std::optional<QualType> StructMsghdrTy = lookupTy("msghdr");
+ std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy);
+ std::optional<QualType> ConstStructMsghdrPtrTy =
getPointerTy(getConstTy(StructMsghdrTy));
// ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
@@ -2676,8 +2841,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(
ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax))));
- Optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
- Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
+ std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
+ std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
// int utime(const char *filename, struct utimbuf *buf);
addToFunctionSummaryMap(
@@ -2688,9 +2853,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> StructTimespecTy = lookupTy("timespec");
- Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy);
- Optional<QualType> ConstStructTimespecPtrTy =
+ std::optional<QualType> StructTimespecTy = lookupTy("timespec");
+ std::optional<QualType> StructTimespecPtrTy =
+ getPointerTy(StructTimespecTy);
+ std::optional<QualType> ConstStructTimespecPtrTy =
getPointerTy(getConstTy(StructTimespecTy));
// int futimens(int fd, const struct timespec times[2]);
@@ -2714,8 +2880,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructTimevalTy = lookupTy("timeval");
- Optional<QualType> ConstStructTimevalPtrTy =
+ std::optional<QualType> StructTimevalTy = lookupTy("timeval");
+ std::optional<QualType> ConstStructTimevalPtrTy =
getPointerTy(getConstTy(StructTimevalTy));
// int utimes(const char *filename, const struct timeval times[2]);
@@ -2738,17 +2904,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> Time_tTy = lookupTy("time_t");
- Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy));
- Optional<QualType> ConstTime_tPtrRestrictTy =
+ std::optional<QualType> Time_tTy = lookupTy("time_t");
+ std::optional<QualType> ConstTime_tPtrTy =
+ getPointerTy(getConstTy(Time_tTy));
+ std::optional<QualType> ConstTime_tPtrRestrictTy =
getRestrictTy(ConstTime_tPtrTy);
- Optional<QualType> StructTmTy = lookupTy("tm");
- Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
- Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy);
- Optional<QualType> ConstStructTmPtrTy =
+ std::optional<QualType> StructTmTy = lookupTy("tm");
+ std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
+ std::optional<QualType> StructTmPtrRestrictTy =
+ getRestrictTy(StructTmPtrTy);
+ std::optional<QualType> ConstStructTmPtrTy =
getPointerTy(getConstTy(StructTmTy));
- Optional<QualType> ConstStructTmPtrRestrictTy =
+ std::optional<QualType> ConstStructTmPtrRestrictTy =
getRestrictTy(ConstStructTmPtrTy);
// struct tm * localtime(const time_t *tp);
@@ -2804,7 +2972,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- Optional<QualType> Clockid_tTy = lookupTy("clockid_t");
+ std::optional<QualType> Clockid_tTy = lookupTy("clockid_t");
// int clock_gettime(clockid_t clock_id, struct timespec *tp);
addToFunctionSummaryMap(
@@ -2815,8 +2983,9 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructItimervalTy = lookupTy("itimerval");
- Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy);
+ std::optional<QualType> StructItimervalTy = lookupTy("itimerval");
+ std::optional<QualType> StructItimervalPtrTy =
+ getPointerTy(StructItimervalTy);
// int getitimer(int which, struct itimerval *curr_value);
addToFunctionSummaryMap(
@@ -2827,25 +2996,30 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant)
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t");
- Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy);
- Optional<QualType> Pthread_tTy = lookupTy("pthread_t");
- Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy);
- Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy);
- Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t");
- Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy);
- Optional<QualType> Pthread_mutex_tPtrRestrictTy =
+ std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t");
+ std::optional<QualType> Pthread_cond_tPtrTy =
+ getPointerTy(Pthread_cond_tTy);
+ std::optional<QualType> Pthread_tTy = lookupTy("pthread_t");
+ std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy);
+ std::optional<QualType> Pthread_tPtrRestrictTy =
+ getRestrictTy(Pthread_tPtrTy);
+ std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t");
+ std::optional<QualType> Pthread_mutex_tPtrTy =
+ getPointerTy(Pthread_mutex_tTy);
+ std::optional<QualType> Pthread_mutex_tPtrRestrictTy =
getRestrictTy(Pthread_mutex_tPtrTy);
- Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t");
- Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy);
- Optional<QualType> ConstPthread_attr_tPtrTy =
+ std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t");
+ std::optional<QualType> Pthread_attr_tPtrTy =
+ getPointerTy(Pthread_attr_tTy);
+ std::optional<QualType> ConstPthread_attr_tPtrTy =
getPointerTy(getConstTy(Pthread_attr_tTy));
- Optional<QualType> ConstPthread_attr_tPtrRestrictTy =
+ std::optional<QualType> ConstPthread_attr_tPtrRestrictTy =
getRestrictTy(ConstPthread_attr_tPtrTy);
- Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t");
- Optional<QualType> ConstPthread_mutexattr_tPtrTy =
+ std::optional<QualType> Pthread_mutexattr_tTy =
+ lookupTy("pthread_mutexattr_t");
+ std::optional<QualType> ConstPthread_mutexattr_tPtrTy =
getPointerTy(getConstTy(Pthread_mutexattr_tTy));
- Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy =
+ std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy =
getRestrictTy(ConstPthread_mutexattr_tPtrTy);
QualType PthreadStartRoutineTy = getPointerTy(
@@ -2929,6 +3103,10 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// Test range values.
addToFunctionSummaryMap(
+ "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0))));
+ addToFunctionSummaryMap(
"__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}),
Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1))));
@@ -2996,6 +3174,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
"__test_restrict_param_2"},
Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}),
Summary(EvalCallAsPure));
+
+ // Test the application of cases.
+ addToFunctionSummaryMap(
+ "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(0))},
+ ErrnoIrrelevant, "Function returns 0")
+ .Case({ReturnValueCondition(WithinRange, SingleValue(1))},
+ ErrnoIrrelevant, "Function returns 1"));
}
SummariesInitialized = true;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 1aa665f0ef45..3f61dd823940 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -17,10 +17,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include <functional>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -85,10 +87,10 @@ const StreamErrorState ErrorFError{false, false, true};
/// Full state information about a stream pointer.
struct StreamState {
/// The last file operation called in the stream.
+ /// Can be nullptr.
const FnDescription *LastOperation;
/// State of a stream symbol.
- /// FIXME: We need maybe an "escaped" state later.
enum KindTy {
Opened, /// Stream is opened.
Closed, /// Closed stream (an invalid stream pointer after it was closed).
@@ -202,13 +204,12 @@ ProgramStateRef bindAndAssumeTrue(ProgramStateRef State, CheckerContext &C,
ProgramStateRef bindInt(uint64_t Value, ProgramStateRef State,
CheckerContext &C, const CallExpr *CE) {
State = State->BindExpr(CE, C.getLocationContext(),
- C.getSValBuilder().makeIntVal(Value, false));
+ C.getSValBuilder().makeIntVal(Value, CE->getType()));
return State;
}
class StreamChecker : public Checker<check::PreCall, eval::Call,
check::DeadSymbols, check::PointerEscape> {
- BugType BT_FileNull{this, "NULL stream pointer", "Stream handling error"};
BugType BT_UseAfterClose{this, "Closed stream", "Stream handling error"};
BugType BT_UseAfterOpenFailed{this, "Invalid stream",
"Stream handling error"};
@@ -236,48 +237,55 @@ public:
private:
CallDescriptionMap<FnDescription> FnDescriptions = {
- {{"fopen"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
- {{"freopen", 3},
+ {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{{"freopen"}, 3},
{&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}},
- {{"tmpfile"}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
- {{"fclose", 1},
+ {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}},
+ {{{"fclose"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}},
- {{"fread", 4},
+ {{{"fread"}, 4},
{&StreamChecker::preFread,
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, true), 3}},
- {{"fwrite", 4},
+ {{{"fwrite"}, 4},
{&StreamChecker::preFwrite,
std::bind(&StreamChecker::evalFreadFwrite, _1, _2, _3, _4, false), 3}},
- {{"fseek", 3}, {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
- {{"ftell", 1}, {&StreamChecker::preDefault, nullptr, 0}},
- {{"rewind", 1}, {&StreamChecker::preDefault, nullptr, 0}},
- {{"fgetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
- {{"fsetpos", 2}, {&StreamChecker::preDefault, nullptr, 0}},
- {{"clearerr", 1},
+ {{{"fseek"}, 3},
+ {&StreamChecker::preFseek, &StreamChecker::evalFseek, 0}},
+ {{{"ftell"}, 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalFtell, 0}},
+ {{{"rewind"}, 1},
+ {&StreamChecker::preDefault, &StreamChecker::evalRewind, 0}},
+ {{{"fgetpos"}, 2},
+ {&StreamChecker::preDefault, &StreamChecker::evalFgetpos, 0}},
+ {{{"fsetpos"}, 2},
+ {&StreamChecker::preDefault, &StreamChecker::evalFsetpos, 0}},
+ {{{"clearerr"}, 1},
{&StreamChecker::preDefault, &StreamChecker::evalClearerr, 0}},
- {{"feof", 1},
+ {{{"feof"}, 1},
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFEof),
0}},
- {{"ferror", 1},
+ {{{"ferror"}, 1},
{&StreamChecker::preDefault,
std::bind(&StreamChecker::evalFeofFerror, _1, _2, _3, _4, ErrorFError),
0}},
- {{"fileno", 1}, {&StreamChecker::preDefault, nullptr, 0}},
+ {{{"fileno"}, 1}, {&StreamChecker::preDefault, nullptr, 0}},
};
CallDescriptionMap<FnDescription> FnTestDescriptions = {
- {{"StreamTesterChecker_make_feof_stream", 1},
+ {{{"StreamTesterChecker_make_feof_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4, ErrorFEof),
0}},
- {{"StreamTesterChecker_make_ferror_stream", 1},
+ {{{"StreamTesterChecker_make_ferror_stream"}, 1},
{nullptr,
std::bind(&StreamChecker::evalSetFeofFerror, _1, _2, _3, _4,
ErrorFError),
0}},
};
+ mutable std::optional<int> EofVal;
+
void evalFopen(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -303,6 +311,18 @@ private:
void evalFseek(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
+ void evalFgetpos(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFsetpos(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalFtell(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void evalRewind(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const;
+
void preDefault(const FnDescription *Desc, const CallEvent &Call,
CheckerContext &C) const;
@@ -318,7 +338,7 @@ private:
const StreamErrorState &ErrorKind) const;
/// Check that the stream (in StreamVal) is not NULL.
- /// If it can only be NULL a fatal error is emitted and nullptr returned.
+ /// If it can only be NULL a sink node is generated and nullptr returned.
/// Otherwise the return value is a new state where the stream is constrained
/// to be non-null.
ProgramStateRef ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
@@ -368,7 +388,7 @@ private:
// (and matching name) as stream functions.
if (!Call.isGlobalCFunction())
return nullptr;
- for (auto P : Call.parameters()) {
+ for (auto *P : Call.parameters()) {
QualType T = P->getType();
if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
return nullptr;
@@ -411,6 +431,17 @@ private:
});
}
+ void initEof(CheckerContext &C) const {
+ if (EofVal)
+ return;
+
+ if (const std::optional<int> OptInt =
+ tryExpandAsInteger("EOF", C.getPreprocessor()))
+ EofVal = *OptInt;
+ else
+ EofVal = -1;
+ }
+
/// Searches for the ExplodedNode where the file descriptor was acquired for
/// StreamSym.
static const ExplodedNode *getAcquisitionSite(const ExplodedNode *N,
@@ -426,8 +457,7 @@ private:
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
inline void assertStreamStateOpened(const StreamState *SS) {
- assert(SS->isOpened() &&
- "Previous create of error node for non-opened stream failed?");
+ assert(SS->isOpened() && "Stream is expected to be opened");
}
const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
@@ -457,6 +487,8 @@ const ExplodedNode *StreamChecker::getAcquisitionSite(const ExplodedNode *N,
void StreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
+ initEof(C);
+
const FnDescription *Desc = lookupFn(Call);
if (!Desc || !Desc->PreFn)
return;
@@ -526,7 +558,7 @@ void StreamChecker::evalFreopen(const FnDescription *Desc,
if (!CE)
return;
- Optional<DefinedSVal> StreamVal =
+ std::optional<DefinedSVal> StreamVal =
getStreamArg(Desc, Call).getAs<DefinedSVal>();
if (!StreamVal)
return;
@@ -574,6 +606,10 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
if (!SS)
return;
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
assertStreamStateOpened(SS);
// Close the File Descriptor.
@@ -581,7 +617,16 @@ void StreamChecker::evalFclose(const FnDescription *Desc, const CallEvent &Call,
// and can not be used any more.
State = State->set<StreamMap>(Sym, StreamState::getClosed(Desc));
- C.addTransition(State);
+ // Return 0 on success, EOF on failure.
+ SValBuilder &SVB = C.getSValBuilder();
+ ProgramStateRef StateSuccess = State->BindExpr(
+ CE, C.getLocationContext(), SVB.makeIntVal(0, C.getASTContext().IntTy));
+ ProgramStateRef StateFailure =
+ State->BindExpr(CE, C.getLocationContext(),
+ SVB.makeIntVal(*EofVal, C.getASTContext().IntTy));
+
+ C.addTransition(StateSuccess);
+ C.addTransition(StateFailure);
}
void StreamChecker::preFread(const FnDescription *Desc, const CallEvent &Call,
@@ -639,10 +684,10 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc,
if (!CE)
return;
- Optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
+ std::optional<NonLoc> SizeVal = Call.getArgSVal(1).getAs<NonLoc>();
if (!SizeVal)
return;
- Optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
+ std::optional<NonLoc> NMembVal = Call.getArgSVal(2).getAs<NonLoc>();
if (!NMembVal)
return;
@@ -766,6 +811,131 @@ void StreamChecker::evalFseek(const FnDescription *Desc, const CallEvent &Call,
C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym));
}
+void StreamChecker::evalFgetpos(const FnDescription *Desc,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!Sym)
+ return;
+
+ // Do not evaluate if stream is not found.
+ if (!State->get<StreamMap>(Sym))
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ // This function does not affect the stream state.
+ // Still we add success and failure state with the appropriate return value.
+ // StdLibraryFunctionsChecker can change these states (set the 'errno' state).
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFsetpos(const FnDescription *Desc,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (!SS)
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ DefinedSVal RetVal = makeRetVal(C, CE);
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+ ProgramStateRef StateNotFailed, StateFailed;
+ std::tie(StateFailed, StateNotFailed) =
+ C.getConstraintManager().assumeDual(State, RetVal);
+
+ StateNotFailed = StateNotFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorNone, false));
+
+ // At failure ferror could be set.
+ // The standards do not tell what happens with the file position at failure.
+ // But we can assume that it is dangerous to make a next I/O operation after
+ // the position was not set correctly (similar to 'fseek').
+ StateFailed = StateFailed->set<StreamMap>(
+ StreamSym, StreamState::getOpened(Desc, ErrorNone | ErrorFError, true));
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!Sym)
+ return;
+
+ if (!State->get<StreamMap>(Sym))
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc RetVal = makeRetVal(C, CE).castAs<NonLoc>();
+ ProgramStateRef StateNotFailed =
+ State->BindExpr(CE, C.getLocationContext(), RetVal);
+ auto Cond = SVB.evalBinOp(State, BO_GE, RetVal,
+ SVB.makeZeroVal(C.getASTContext().LongTy),
+ SVB.getConditionType())
+ .getAs<DefinedOrUnknownSVal>();
+ if (!Cond)
+ return;
+ StateNotFailed = StateNotFailed->assume(*Cond, true);
+ if (!StateNotFailed)
+ return;
+
+ ProgramStateRef StateFailed = State->BindExpr(
+ CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy));
+
+ C.addTransition(StateNotFailed);
+ C.addTransition(StateFailed);
+}
+
+void StreamChecker::evalRewind(const FnDescription *Desc, const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol();
+ if (!StreamSym)
+ return;
+
+ const StreamState *SS = State->get<StreamMap>(StreamSym);
+ if (!SS)
+ return;
+
+ auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ assertStreamStateOpened(SS);
+
+ State = State->set<StreamMap>(StreamSym,
+ StreamState::getOpened(Desc, ErrorNone, false));
+
+ C.addTransition(State);
+}
+
void StreamChecker::evalClearerr(const FnDescription *Desc,
const CallEvent &Call,
CheckerContext &C) const {
@@ -869,13 +1039,11 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE,
std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream);
if (!StateNotNull && StateNull) {
- if (ExplodedNode *N = C.generateErrorNode(StateNull)) {
- auto R = std::make_unique<PathSensitiveBugReport>(
- BT_FileNull, "Stream pointer might be NULL.", N);
- if (StreamE)
- bugreporter::trackExpressionValue(N, StreamE, *R);
- C.emitReport(std::move(R));
- }
+ // Stream argument is NULL, stop analysis on this path.
+ // This case should occur only if StdLibraryFunctionsChecker (or ModelPOSIX
+ // option of it) is not turned on, otherwise that checker ensures non-null
+ // argument.
+ C.generateSink(StateNull, C.getPredecessor());
return nullptr;
}
@@ -976,7 +1144,8 @@ ProgramStateRef StreamChecker::ensureNoFilePositionIndeterminate(
ProgramStateRef
StreamChecker::ensureFseekWhenceCorrect(SVal WhenceVal, CheckerContext &C,
ProgramStateRef State) const {
- Optional<nonloc::ConcreteInt> CI = WhenceVal.getAs<nonloc::ConcreteInt>();
+ std::optional<nonloc::ConcreteInt> CI =
+ WhenceVal.getAs<nonloc::ConcreteInt>();
if (!CI)
return State;
diff --git a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
index 44162094e49a..a95c0e183284 100644
--- a/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Checkers/Taint.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -63,7 +64,7 @@ ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V,
// their parent region, which is a conjured symbol default-bound to the base
// region of the parent region.
if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<SVal> binding =
+ if (std::optional<SVal> binding =
State->getStateManager().getStoreManager().getDefaultBinding(
*LCV)) {
if (SymbolRef Sym = binding->getAsSymbol())
diff --git a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index eeec807ccee4..28fe11d5ed06 100644
--- a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/FoldingSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -100,7 +101,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
const Expr *E = nullptr;
- if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
+ if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
BinaryOperator::Opcode Op = BO->getOpcode();
if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
@@ -132,7 +133,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
}
bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const {
- Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
+ std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
if (!DSV)
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index ebe5ad53cc30..b17b983f0345 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -18,6 +18,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -93,7 +94,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
ProgramPoint P = PrevN->getLocation();
ProgramStateRef St = N->getState();
- if (Optional<PostStmt> PS = P.getAs<PostStmt>())
+ if (std::optional<PostStmt> PS = P.getAs<PostStmt>())
if (PS->getStmt() == Ex)
St = PrevN->getState();
diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 816a547cadc3..27f3345e67ac 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -69,8 +70,8 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
continue;
// Get the VarRegion associated with VD in the local stack frame.
- if (Optional<UndefinedVal> V =
- state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
+ if (std::optional<UndefinedVal> V =
+ state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT)
BT.reset(
diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 05f8f6084c0b..0fa3d6043971 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -92,7 +92,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (const auto *CD =
dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
if (CD->isImplicit()) {
- for (auto I : CD->inits()) {
+ for (auto *I : CD->inits()) {
if (I->getInit()->IgnoreImpCasts() == StoreE) {
OS << "Value assigned to field '" << I->getMember()->getName()
<< "' in implicit constructor is garbage or undefined";
diff --git a/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
new file mode 100644
index 000000000000..f053ee887a1a
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/UndefinedNewArraySizeChecker.cpp
@@ -0,0 +1,80 @@
+//===--- UndefinedNewArraySizeChecker.cpp -----------------------*- C++ -*--==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines UndefinedNewArraySizeChecker, a builtin check in ExprEngine
+// that checks if the size of the array in a new[] expression is undefined.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefinedNewArraySizeChecker : public Checker<check::PreCall> {
+
+private:
+ BugType BT{this, "Undefined array element count in new[]",
+ categories::LogicError};
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void HandleUndefinedArrayElementCount(CheckerContext &C, SVal ArgVal,
+ const Expr *Init,
+ SourceRange Range) const;
+};
+} // namespace
+
+void UndefinedNewArraySizeChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (const auto *AC = dyn_cast<CXXAllocatorCall>(&Call)) {
+ if (!AC->isArray())
+ return;
+
+ auto *SizeEx = *AC->getArraySizeExpr();
+ auto SizeVal = AC->getArraySizeVal();
+
+ if (SizeVal.isUndef())
+ HandleUndefinedArrayElementCount(C, SizeVal, SizeEx,
+ SizeEx->getSourceRange());
+ }
+}
+
+void UndefinedNewArraySizeChecker::HandleUndefinedArrayElementCount(
+ CheckerContext &C, SVal ArgVal, const Expr *Init, SourceRange Range) const {
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "Element count in new[] is a garbage value";
+
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
+ R->markInteresting(ArgVal);
+ R->addRange(Range);
+ bugreporter::trackExpressionValue(N, Init, *R);
+
+ C.emitReport(std::move(R));
+ }
+}
+
+void ento::registerUndefinedNewArraySizeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefinedNewArraySizeChecker>();
+}
+
+bool ento::shouldRegisterUndefinedNewArraySizeChecker(
+ const CheckerManager &mgr) {
+ return mgr.getLangOpts().CPlusPlus;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index f5bd765ff679..54e1e0e11909 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include <optional>
using namespace clang;
using namespace clang::ento;
@@ -121,9 +122,9 @@ struct DereferenceInfo {
/// Dereferences \p FR and returns with the pointee's region, and whether it
/// needs to be casted back to it's location type. If for whatever reason
-/// dereferencing fails, returns with None.
-static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
- const FieldRegion *FR);
+/// dereferencing fails, returns std::nullopt.
+static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR);
/// Returns whether \p T can be (transitively) dereferenced to a void pointer
/// type (void*, void**, ...).
@@ -159,7 +160,7 @@ bool FindUninitializedFields::isDereferencableUninit(
// At this point the pointer itself is initialized and points to a valid
// location, we'll now check the pointee.
- llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR);
+ std::optional<DereferenceInfo> DerefInfo = dereference(State, FR);
if (!DerefInfo) {
IsAnyFieldInitialized = true;
return false;
@@ -217,8 +218,8 @@ bool FindUninitializedFields::isDereferencableUninit(
// Utility functions.
//===----------------------------------------------------------------------===//
-static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
- const FieldRegion *FR) {
+static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR) {
llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
@@ -234,7 +235,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
// The region we'd like to acquire.
const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
if (!R)
- return None;
+ return std::nullopt;
VisitedRegions.insert(R);
@@ -245,7 +246,7 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
R = Tmp->getAs<TypedValueRegion>();
if (!R)
- return None;
+ return std::nullopt;
// We found a cyclic pointer, like int *ptr = (int *)&ptr.
if (!VisitedRegions.insert(R).second)
diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index aa3f4524798a..f503b3e88bb3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -17,11 +17,11 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -40,7 +40,7 @@ namespace {
class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
- mutable Optional<uint64_t> Val_O_CREAT;
+ mutable std::optional<uint64_t> Val_O_CREAT;
public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
@@ -234,7 +234,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
}
NonLoc oflags = V.castAs<NonLoc>();
NonLoc ocreateFlag = C.getSValBuilder()
- .makeIntVal(Val_O_CREAT.value(), oflagsEx->getType())
+ .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
.castAs<NonLoc>();
SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
oflags, ocreateFlag,
diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index d231be64c2e1..3ad6858ead46 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -24,6 +24,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/SmallSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -74,7 +75,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
if (!PM)
PM = &LC->getParentMap();
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
const CFGBlock *CB = BE->getBlock();
reachable.insert(CB->getBlockID());
}
@@ -129,7 +130,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
bool foundUnreachable = false;
for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end();
ci != ce; ++ci) {
- if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
+ if (std::optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable ||
CE->isBuiltinAssumeFalse(Eng.getContext())) {
@@ -199,7 +200,7 @@ void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB,
// Find the Stmt* in a CFGBlock for reporting a warning
const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {
for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) {
- if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> S = I->getAs<CFGStmt>()) {
if (!isa<DeclStmt>(S->getStmt()))
return S->getStmt();
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index cf519b085892..fe910ce35302 100644
--- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -24,6 +24,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -192,7 +193,7 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C,
DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy);
SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy);
- if (Optional<DefinedSVal> LessThanZeroDVal =
+ if (std::optional<DefinedSVal> LessThanZeroDVal =
LessThanZeroVal.getAs<DefinedSVal>()) {
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef StatePos, StateNeg;
diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index fbefd5f9ffdc..2d1b873abf73 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -100,27 +100,26 @@ private:
};
const SmallVector<ValistChecker::VAListAccepter, 15>
- ValistChecker::VAListAccepters = {
- {{"vfprintf", 3}, 2},
- {{"vfscanf", 3}, 2},
- {{"vprintf", 2}, 1},
- {{"vscanf", 2}, 1},
- {{"vsnprintf", 4}, 3},
- {{"vsprintf", 3}, 2},
- {{"vsscanf", 3}, 2},
- {{"vfwprintf", 3}, 2},
- {{"vfwscanf", 3}, 2},
- {{"vwprintf", 2}, 1},
- {{"vwscanf", 2}, 1},
- {{"vswprintf", 4}, 3},
- // vswprintf is the wide version of vsnprintf,
- // vsprintf has no wide version
- {{"vswscanf", 3}, 2}};
-
-const CallDescription
- ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1),
- ValistChecker::VaCopy("__builtin_va_copy", 2),
- ValistChecker::VaEnd("__builtin_va_end", 1);
+ ValistChecker::VAListAccepters = {{{{"vfprintf"}, 3}, 2},
+ {{{"vfscanf"}, 3}, 2},
+ {{{"vprintf"}, 2}, 1},
+ {{{"vscanf"}, 2}, 1},
+ {{{"vsnprintf"}, 4}, 3},
+ {{{"vsprintf"}, 3}, 2},
+ {{{"vsscanf"}, 3}, 2},
+ {{{"vfwprintf"}, 3}, 2},
+ {{{"vfwscanf"}, 3}, 2},
+ {{{"vwprintf"}, 2}, 1},
+ {{{"vwscanf"}, 2}, 1},
+ {{{"vswprintf"}, 4}, 3},
+ // vswprintf is the wide version of
+ // vsnprintf, vsprintf has no wide version
+ {{{"vswscanf"}, 3}, 2}};
+
+const CallDescription ValistChecker::VaStart({"__builtin_va_start"}, /*Args=*/2,
+ /*Params=*/1),
+ ValistChecker::VaCopy({"__builtin_va_copy"}, 2),
+ ValistChecker::VaEnd({"__builtin_va_end"}, 1);
} // end anonymous namespace
void ValistChecker::checkPreCall(const CallEvent &Call,
diff --git a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 04e6603b4cbe..86a4e6fbcd6a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -35,6 +35,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/AST/ParentMap.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -154,8 +155,8 @@ void VforkChecker::checkPostCall(const CallEvent &Call,
// Get return value of vfork.
SVal VforkRetVal = Call.getReturnValue();
- Optional<DefinedOrUnknownSVal> DVal =
- VforkRetVal.getAs<DefinedOrUnknownSVal>();
+ std::optional<DefinedOrUnknownSVal> DVal =
+ VforkRetVal.getAs<DefinedOrUnknownSVal>();
if (!DVal)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 9c7a59971763..64028b277021 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -12,8 +12,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include <optional>
-using llvm::Optional;
namespace clang {
std::pair<const Expr *, bool>
@@ -34,8 +34,7 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
}
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
- Optional<bool> IsGetterOfRefCt =
- isGetterOfRefCounted(memberCall->getMethodDecl());
+ std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
index 97f75135bf92..66d8588e2531 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/Casting.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -69,7 +70,7 @@ public:
if (shouldSkipDecl(RD))
return;
- for (auto Member : RD->fields()) {
+ for (auto *Member : RD->fields()) {
const Type *MemberType = Member->getType().getTypePtrOrNull();
if (!MemberType)
continue;
@@ -77,9 +78,9 @@ public:
if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
// If we don't see the definition we just don't know.
if (MemberCXXRD->hasDefinition()) {
- llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD);
- if (isRCAble && *isRCAble)
- reportBug(Member, MemberType, MemberCXXRD, RD);
+ std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
+ if (isRCAble && *isRCAble)
+ reportBug(Member, MemberType, MemberCXXRD, RD);
}
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index a198943c9433..9b1d7ae3e6a3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,9 +12,8 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
-#include "llvm/ADT/Optional.h"
+#include <optional>
-using llvm::Optional;
using namespace clang;
namespace {
@@ -45,29 +44,31 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
namespace clang {
-llvm::Optional<const clang::CXXRecordDecl *>
-isRefCountable(const CXXBaseSpecifier *Base) {
+std::optional<const clang::CXXRecordDecl*>
+isRefCountable(const CXXBaseSpecifier* Base)
+{
assert(Base);
const Type *T = Base->getType().getTypePtrOrNull();
if (!T)
- return llvm::None;
+ return std::nullopt;
const CXXRecordDecl *R = T->getAsCXXRecordDecl();
if (!R)
- return llvm::None;
+ return std::nullopt;
if (!R->hasDefinition())
- return llvm::None;
+ return std::nullopt;
return hasPublicRefAndDeref(R) ? R : nullptr;
}
-llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) {
+std::optional<bool> isRefCountable(const CXXRecordDecl* R)
+{
assert(R);
R = R->getDefinition();
if (!R)
- return llvm::None;
+ return std::nullopt;
if (hasPublicRefAndDeref(R))
return true;
@@ -77,20 +78,19 @@ llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) {
bool AnyInconclusiveBase = false;
const auto isRefCountableBase =
- [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
- Optional<const clang::CXXRecordDecl *> IsRefCountable =
- clang::isRefCountable(Base);
- if (!IsRefCountable) {
- AnyInconclusiveBase = true;
- return false;
- }
- return (*IsRefCountable) != nullptr;
+ [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
+ std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base);
+ if (!IsRefCountable) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*IsRefCountable) != nullptr;
};
bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
/*LookupInDependent =*/true);
if (AnyInconclusiveBase)
- return llvm::None;
+ return std::nullopt;
return BasesResult;
}
@@ -112,19 +112,21 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
|| FunctionName == "Identifier";
}
-llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) {
+std::optional<bool> isUncounted(const CXXRecordDecl* Class)
+{
// Keep isRefCounted first as it's cheaper.
if (isRefCounted(Class))
return false;
- llvm::Optional<bool> IsRefCountable = isRefCountable(Class);
+ std::optional<bool> IsRefCountable = isRefCountable(Class);
if (!IsRefCountable)
- return llvm::None;
+ return std::nullopt;
return (*IsRefCountable);
}
-llvm::Optional<bool> isUncountedPtr(const Type *T) {
+std::optional<bool> isUncountedPtr(const Type* T)
+{
assert(T);
if (T->isPointerType() || T->isReferenceType()) {
@@ -135,7 +137,8 @@ llvm::Optional<bool> isUncountedPtr(const Type *T) {
return false;
}
-Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) {
+std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
+{
assert(M);
if (isa<CXXMethodDecl>(M)) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 753adea0d14d..91e3ccf2ec30 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -10,6 +10,7 @@
#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
#include "llvm/ADT/APInt.h"
+#include <optional>
namespace clang {
class CXXBaseSpecifier;
@@ -26,31 +27,31 @@ class Type;
// Ref<T>.
/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
-/// not, None if inconclusive.
-llvm::Optional<const clang::CXXRecordDecl *>
-isRefCountable(const clang::CXXBaseSpecifier *Base);
+/// not, std::nullopt if inconclusive.
+std::optional<const clang::CXXRecordDecl*>
+isRefCountable(const clang::CXXBaseSpecifier* Base);
-/// \returns true if \p Class is ref-countable, false if not, None if
+/// \returns true if \p Class is ref-countable, false if not, std::nullopt if
/// inconclusive.
-llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class);
+std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class);
/// \returns true if \p Class is ref-counted, false if not.
bool isRefCounted(const clang::CXXRecordDecl *Class);
/// \returns true if \p Class is ref-countable AND not ref-counted, false if
-/// not, None if inconclusive.
-llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class);
+/// not, std::nullopt if inconclusive.
+std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class);
/// \returns true if \p T is either a raw pointer or reference to an uncounted
-/// class, false if not, None if inconclusive.
-llvm::Optional<bool> isUncountedPtr(const clang::Type *T);
+/// class, false if not, std::nullopt if inconclusive.
+std::optional<bool> isUncountedPtr(const clang::Type* T);
/// \returns true if \p F creates ref-countable object from uncounted parameter,
/// false if not.
bool isCtorOfRefCounted(const clang::FunctionDecl *F);
/// \returns true if \p M is getter of a ref-counted class, false if not.
-llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
+std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
/// \returns true if \p F is a conversion between ref-countable or ref-counted
/// pointer types.
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index fa9ece217cc0..48dcfc4a3c46 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -76,8 +77,7 @@ public:
(AccSpec == AS_none && RD->isClass()))
return false;
- llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD =
- isRefCountable(Base);
+ std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base);
if (!RefCntblBaseRD || !(*RefCntblBaseRD))
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index e6d0948f71bb..4ae8c442fa70 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/DenseSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -85,7 +86,7 @@ public:
continue; // FIXME? Should we bail?
// FIXME: more complex types (arrays, references to raw pointers, etc)
- Optional<bool> IsUncounted = isUncountedPtr(ArgType);
+ std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
if (!IsUncounted || !(*IsUncounted))
continue;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
index deebbd603b2c..004b0b9d398b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -57,18 +58,18 @@ public:
void visitLambdaExpr(LambdaExpr *L) const {
for (const LambdaCapture &C : L->captures()) {
if (C.capturesVariable()) {
- VarDecl *CapturedVar = C.getCapturedVar();
+ ValueDecl *CapturedVar = C.getCapturedVar();
if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
- Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
- if (IsUncountedPtr && *IsUncountedPtr) {
- reportBug(C, CapturedVar, CapturedVarType);
- }
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ reportBug(C, CapturedVar, CapturedVarType);
+ }
}
}
}
}
- void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
+ void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
const Type *T) const {
assert(CapturedVar);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
index 7e86f28cb70f..fa7475934981 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
@@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/DenseSet.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -169,7 +170,7 @@ public:
if (!ArgType)
return;
- Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
if (IsUncountedPtr && *IsUncountedPtr) {
const Expr *const InitExpr = V->getInit();
if (!InitExpr)
diff --git a/clang/lib/StaticAnalyzer/Checkers/Yaml.h b/clang/lib/StaticAnalyzer/Checkers/Yaml.h
index 497189f4c160..4159e14105f8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/Yaml.h
+++ b/clang/lib/StaticAnalyzer/Checkers/Yaml.h
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLTraits.h"
+#include <optional>
namespace clang {
namespace ento {
@@ -25,10 +26,10 @@ namespace ento {
/// template parameter must have a yaml MappingTraits.
/// Emit diagnostic error in case of any failure.
template <class T, class Checker>
-llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
- StringRef Option, StringRef ConfigFile) {
+std::optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
+ StringRef Option, StringRef ConfigFile) {
if (ConfigFile.trim().empty())
- return None;
+ return std::nullopt;
llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get();
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
@@ -38,7 +39,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
Mgr.reportInvalidCheckerOptionValue(Chk, Option,
"a valid filename instead of '" +
std::string(ConfigFile) + "'");
- return None;
+ return std::nullopt;
}
llvm::yaml::Input Input(Buffer.get()->getBuffer());
@@ -48,7 +49,7 @@ llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
if (std::error_code ec = Input.error()) {
Mgr.reportInvalidCheckerOptionValue(Chk, Option,
"a valid yaml file: " + ec.message());
- return None;
+ return std::nullopt;
}
return Config;
diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
index 084789509533..aae1a17bc0ae 100644
--- a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
@@ -39,11 +39,11 @@ private:
// SEI CERT ENV31-C
const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
- {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
- {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
- {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
- {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
- {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
};
void postPreviousReturnInvalidatingCall(const CallEvent &Call,
@@ -51,13 +51,15 @@ private:
// SEI CERT ENV34-C
const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
- {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
- {{"setlocale", 2},
+ {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"setlocale"}, 2},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
- {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
- {{"localeconv", 0},
+ {{{"strerror"}, 1},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"localeconv"}, 0},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{{"asctime"}, 1},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
- {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
};
public:
diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
index ed3bdafad084..eae162cda693 100644
--- a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
@@ -30,7 +30,7 @@ class PutenvWithAutoChecker : public Checker<check::PostCall> {
private:
BugType BT{this, "'putenv' function should not be called with auto variables",
categories::SecurityError};
- const CallDescription Putenv{"putenv", 1};
+ const CallDescription Putenv{{"putenv"}, 1};
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 009cbd4559b5..86ef4a568665 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -23,6 +23,7 @@
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
+#include <optional>
#include <utility>
#include <vector>
@@ -64,45 +65,44 @@ void AnalyzerOptions::printFormattedEntry(
ExplorationStrategyKind
AnalyzerOptions::getExplorationStrategy() const {
auto K =
- llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>(
- ExplorationStrategy)
+ llvm::StringSwitch<std::optional<ExplorationStrategyKind>>(
+ ExplorationStrategy)
.Case("dfs", ExplorationStrategyKind::DFS)
.Case("bfs", ExplorationStrategyKind::BFS)
- .Case("unexplored_first",
- ExplorationStrategyKind::UnexploredFirst)
+ .Case("unexplored_first", ExplorationStrategyKind::UnexploredFirst)
.Case("unexplored_first_queue",
ExplorationStrategyKind::UnexploredFirstQueue)
.Case("unexplored_first_location_queue",
ExplorationStrategyKind::UnexploredFirstLocationQueue)
.Case("bfs_block_dfs_contents",
ExplorationStrategyKind::BFSBlockDFSContents)
- .Default(None);
+ .Default(std::nullopt);
assert(K && "User mode is invalid.");
- return K.value();
+ return *K;
}
CTUPhase1InliningKind AnalyzerOptions::getCTUPhase1Inlining() const {
- auto K = llvm::StringSwitch<llvm::Optional<CTUPhase1InliningKind>>(
+ auto K = llvm::StringSwitch<std::optional<CTUPhase1InliningKind>>(
CTUPhase1InliningMode)
.Case("none", CTUPhase1InliningKind::None)
.Case("small", CTUPhase1InliningKind::Small)
.Case("all", CTUPhase1InliningKind::All)
- .Default(None);
+ .Default(std::nullopt);
assert(K && "CTU inlining mode is invalid.");
- return K.value();
+ return *K;
}
IPAKind AnalyzerOptions::getIPAMode() const {
- auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode)
- .Case("none", IPAK_None)
- .Case("basic-inlining", IPAK_BasicInlining)
- .Case("inlining", IPAK_Inlining)
- .Case("dynamic", IPAK_DynamicDispatch)
- .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
- .Default(None);
+ auto K = llvm::StringSwitch<std::optional<IPAKind>>(IPAMode)
+ .Case("none", IPAK_None)
+ .Case("basic-inlining", IPAK_BasicInlining)
+ .Case("inlining", IPAK_Inlining)
+ .Case("dynamic", IPAK_DynamicDispatch)
+ .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
+ .Default(std::nullopt);
assert(K && "IPA Mode is invalid.");
- return K.value();
+ return *K;
}
bool
@@ -111,14 +111,13 @@ AnalyzerOptions::mayInlineCXXMemberFunction(
if (getIPAMode() < IPAK_Inlining)
return false;
- auto K =
- llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>(
- CXXMemberInliningMode)
- .Case("constructors", CIMK_Constructors)
- .Case("destructors", CIMK_Destructors)
- .Case("methods", CIMK_MemberFunctions)
- .Case("none", CIMK_None)
- .Default(None);
+ auto K = llvm::StringSwitch<std::optional<CXXInlineableMemberKind>>(
+ CXXMemberInliningMode)
+ .Case("constructors", CIMK_Constructors)
+ .Case("destructors", CIMK_Destructors)
+ .Case("methods", CIMK_MemberFunctions)
+ .Case("none", CIMK_None)
+ .Default(std::nullopt);
assert(K && "Invalid c++ member function inlining mode.");
@@ -162,12 +161,12 @@ StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
StringRef OptionName,
bool SearchInParents) const {
- auto Ret = llvm::StringSwitch<llvm::Optional<bool>>(
- getCheckerStringOption(CheckerName, OptionName,
- SearchInParents))
- .Case("true", true)
- .Case("false", false)
- .Default(None);
+ auto Ret =
+ llvm::StringSwitch<std::optional<bool>>(
+ getCheckerStringOption(CheckerName, OptionName, SearchInParents))
+ .Case("true", true)
+ .Case("false", false)
+ .Default(std::nullopt);
assert(Ret &&
"This option should be either 'true' or 'false', and should've been "
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 4d6b82e63f6a..a7f149b87e79 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -46,8 +46,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
@@ -66,6 +64,7 @@
#include <cstddef>
#include <iterator>
#include <memory>
+#include <optional>
#include <queue>
#include <string>
#include <tuple>
@@ -221,8 +220,8 @@ class PathDiagnosticBuilder : public BugReporterContext {
public:
/// Find a non-invalidated report for a given equivalence class, and returns
/// a PathDiagnosticBuilder able to construct bug reports for different
- /// consumers. Returns None if no valid report is found.
- static Optional<PathDiagnosticBuilder>
+ /// consumers. Returns std::nullopt if no valid report is found.
+ static std::optional<PathDiagnosticBuilder>
findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports,
PathSensitiveBugReporter &Reporter);
@@ -309,7 +308,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
}
// Check if the parameter is a pointer to the symbol.
- if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
// Do not attempt to dereference void*.
if ((*I)->getType()->isVoidPointerType())
continue;
@@ -1033,7 +1032,7 @@ static bool isContainedByStmt(const ParentMap &PM, const Stmt *S,
static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term,
const ExplodedNode *N) {
while (N) {
- Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
+ std::optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
if (SP) {
const Stmt *S = SP->getStmt();
if (!isContainedByStmt(PM, Term, S))
@@ -1194,7 +1193,7 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
"location context associated with the active path!");
// Have we encountered an exit from a function call?
- if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
+ if (std::optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
// We are descending into a call (backwards). Construct
// a new call piece to contain the path pieces for that call.
@@ -1566,21 +1565,22 @@ static void simplifySimpleBranches(PathPieces &pieces) {
/// Returns the number of bytes in the given (character-based) SourceRange.
///
-/// If the locations in the range are not on the same line, returns None.
+/// If the locations in the range are not on the same line, returns
+/// std::nullopt.
///
/// Note that this does not do a precise user-visible character or column count.
-static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
- SourceRange Range) {
+static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
+ SourceRange Range) {
SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
SM.getExpansionRange(Range.getEnd()).getEnd());
FileID FID = SM.getFileID(ExpansionRange.getBegin());
if (FID != SM.getFileID(ExpansionRange.getEnd()))
- return None;
+ return std::nullopt;
- Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
+ std::optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
if (!Buffer)
- return None;
+ return std::nullopt;
unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd());
@@ -1591,15 +1591,15 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
// SourceRange is covering a large or small amount of space in the user's
// editor.
if (Snippet.find_first_of("\r\n") != StringRef::npos)
- return None;
+ return std::nullopt;
// This isn't Unicode-aware, but it doesn't need to be.
return Snippet.size();
}
/// \sa getLengthOnSingleLine(SourceManager, SourceRange)
-static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
- const Stmt *S) {
+static std::optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
+ const Stmt *S) {
return getLengthOnSingleLine(SM, S->getSourceRange());
}
@@ -1658,9 +1658,9 @@ static void removeContextCycles(PathPieces &Path, const SourceManager &SM) {
if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
const size_t MAX_SHORT_LINE_LENGTH = 80;
- Optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
+ std::optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
- Optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
+ std::optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
Path.erase(I);
I = Path.erase(NextI);
@@ -1719,7 +1719,7 @@ static void removePunyEdges(PathPieces &path, const SourceManager &SM,
std::swap(SecondLoc, FirstLoc);
SourceRange EdgeRange(FirstLoc, SecondLoc);
- Optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
+ std::optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
// If the statements are on different lines, continue.
if (!ByteWidth)
@@ -2310,7 +2310,7 @@ void PathSensitiveBugReport::markInteresting(const LocationContext *LC) {
InterestingLocationContexts.insert(LC);
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(SVal V) const {
auto RKind = getInterestingnessKind(V.getAsRegion());
auto SKind = getInterestingnessKind(V.getAsSymbol());
@@ -2332,25 +2332,25 @@ PathSensitiveBugReport::getInterestingnessKind(SVal V) const {
"BugReport::getInterestingnessKind currently can only handle 2 different "
"tracking kinds! Please define what tracking kind should we return here "
"when the kind of getAsRegion() and getAsSymbol() is different!");
- return None;
+ return std::nullopt;
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const {
if (!sym)
- return None;
+ return std::nullopt;
// We don't currently consider metadata symbols to be interesting
// even if we know their region is interesting. Is that correct behavior?
auto It = InterestingSymbols.find(sym);
if (It == InterestingSymbols.end())
- return None;
+ return std::nullopt;
return It->getSecond();
}
-Optional<bugreporter::TrackingKind>
+std::optional<bugreporter::TrackingKind>
PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const {
if (!R)
- return None;
+ return std::nullopt;
R = R->getBaseRegion();
auto It = InterestingRegions.find(R);
@@ -2359,7 +2359,7 @@ PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const {
if (const auto *SR = dyn_cast<SymbolicRegion>(R))
return getInterestingnessKind(SR->getSymbol());
- return None;
+ return std::nullopt;
}
bool PathSensitiveBugReport::isInteresting(SVal V) const {
@@ -2387,7 +2387,7 @@ const Stmt *PathSensitiveBugReport::getStmt() const {
ProgramPoint ProgP = ErrorNode->getLocation();
const Stmt *S = nullptr;
- if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
+ if (std::optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
if (BE->getBlock() == &Exit)
S = ErrorNode->getPreviousStmtForDiagnostics();
@@ -2419,7 +2419,7 @@ PathSensitiveBugReport::getLocation() const {
if (!S) {
// If this is an implicit call, return the implicit call point location.
- if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
+ if (std::optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
return PathDiagnosticLocation(PIE->getLocation(), SM);
if (auto FE = P.getAs<FunctionExitPoint>()) {
if (const ReturnStmt *RS = FE->getStmt())
@@ -2821,7 +2821,7 @@ generateVisitorsDiagnostics(PathSensitiveBugReport *R,
return Notes;
}
-Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
+std::optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
ArrayRef<PathSensitiveBugReport *> &bugReports,
PathSensitiveBugReporter &Reporter) {
@@ -2880,7 +2880,7 @@ PathSensitiveBugReporter::generatePathDiagnostics(
auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
- Optional<PathDiagnosticBuilder> PDB =
+ std::optional<PathDiagnosticBuilder> PDB =
PathDiagnosticBuilder::findValidReport(bugReports, *this);
if (PDB) {
@@ -3097,9 +3097,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) {
// For path diagnostic consumers that don't support extra notes,
// we may optionally convert those to path notes.
- for (auto I = report->getNotes().rbegin(),
- E = report->getNotes().rend(); I != E; ++I) {
- PathDiagnosticNotePiece *Piece = I->get();
+ for (const auto &I : llvm::reverse(report->getNotes())) {
+ PathDiagnosticNotePiece *Piece = I.get();
auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
Piece->getLocation(), Piece->getString());
for (const auto &R: Piece->getRanges())
@@ -3108,9 +3107,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
Pieces.push_front(std::move(ConvertedPiece));
}
} else {
- for (auto I = report->getNotes().rbegin(),
- E = report->getNotes().rend(); I != E; ++I)
- Pieces.push_front(*I);
+ for (const auto &I : llvm::reverse(report->getNotes()))
+ Pieces.push_front(I);
}
for (const auto &I : report->getFixits())
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 3a90c37a36da..2b461acf9a73 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -46,8 +46,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
@@ -60,6 +58,7 @@
#include <cassert>
#include <deque>
#include <memory>
+#include <optional>
#include <string>
#include <utility>
@@ -206,8 +205,8 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
RLCV->getStore() == RightNode->getState()->getStore();
}
-static Optional<SVal> getSValForVar(const Expr *CondVarExpr,
- const ExplodedNode *N) {
+static std::optional<SVal> getSValForVar(const Expr *CondVarExpr,
+ const ExplodedNode *N) {
ProgramStateRef State = N->getState();
const LocationContext *LCtx = N->getLocationContext();
@@ -227,16 +226,16 @@ static Optional<SVal> getSValForVar(const Expr *CondVarExpr,
if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>())
return State->getRawSVal(*FieldL, FD->getType());
- return None;
+ return std::nullopt;
}
-static Optional<const llvm::APSInt *>
+static std::optional<const llvm::APSInt *>
getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
- if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
if (auto CI = V->getAs<nonloc::ConcreteInt>())
return &CI->getValue();
- return None;
+ return std::nullopt;
}
static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
@@ -248,8 +247,9 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame()))
return false;
- if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
- if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V))
+ if (std::optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (std::optional<bugreporter::TrackingKind> K =
+ B->getInterestingnessKind(*V))
return *K == bugreporter::TrackingKind::Condition;
return false;
@@ -257,7 +257,7 @@ static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
static bool isInterestingExpr(const Expr *E, const ExplodedNode *N,
const PathSensitiveBugReport *B) {
- if (Optional<SVal> V = getSValForVar(E, N))
+ if (std::optional<SVal> V = getSValForVar(E, N))
return B->getInterestingnessKind(*V).has_value();
return false;
}
@@ -538,8 +538,8 @@ private:
/// Dereferences fields up to a given recursion limit.
/// Note that \p Vec is passed by value, leading to quadratic copying cost,
/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
- /// \return A chain fields leading to the region of interest or None.
- const Optional<RegionVector>
+ /// \return A chain fields leading to the region of interest or std::nullopt.
+ const std::optional<RegionVector>
findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,
const MemRegion *R, const RegionVector &Vec = {},
int depth = 0);
@@ -619,26 +619,26 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent,
/// Dereferences fields up to a given recursion limit.
/// Note that \p Vec is passed by value, leading to quadratic copying cost,
/// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
-/// \return A chain fields leading to the region of interest or None.
-const Optional<NoStoreFuncVisitor::RegionVector>
+/// \return A chain fields leading to the region of interest or std::nullopt.
+const std::optional<NoStoreFuncVisitor::RegionVector>
NoStoreFuncVisitor::findRegionOfInterestInRecord(
const RecordDecl *RD, ProgramStateRef State, const MemRegion *R,
const NoStoreFuncVisitor::RegionVector &Vec /* = {} */,
int depth /* = 0 */) {
if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
- return None;
+ return std::nullopt;
if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
if (!RDX->hasDefinition())
- return None;
+ return std::nullopt;
// Recursively examine the base classes.
// Note that following base classes does not increase the recursion depth.
if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
for (const auto &II : RDX->bases())
if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
- if (Optional<RegionVector> Out =
+ if (std::optional<RegionVector> Out =
findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
return Out;
@@ -664,12 +664,12 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord(
continue;
if (const RecordDecl *RRD = PT->getAsRecordDecl())
- if (Optional<RegionVector> Out =
+ if (std::optional<RegionVector> Out =
findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
return Out;
}
- return None;
+ return std::nullopt;
}
PathDiagnosticPieceRef
@@ -730,7 +730,7 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters(
ProgramStateRef State = N->getState();
if (const RecordDecl *RD = PT->getAsRecordDecl())
- if (Optional<RegionVector> P =
+ if (std::optional<RegionVector> P =
findRegionOfInterestInRecord(RD, State, MR))
return maybeEmitNote(R, Call, N, *P, RegionOfInterest, ParamName,
ParamIsReferenceType, IndirectionLevel);
@@ -928,12 +928,12 @@ public:
private:
/// \return Source location of right hand side of an assignment
/// into \c RegionOfInterest, empty optional if none found.
- Optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
+ std::optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
const Stmt *S = N->getStmtForDiagnostics();
ProgramStateRef State = N->getState();
auto *LCtx = N->getLocationContext();
if (!S)
- return None;
+ return std::nullopt;
if (const auto *DS = dyn_cast<DeclStmt>(S)) {
if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
@@ -948,7 +948,7 @@ private:
return RHS->getBeginLoc();
}
}
- return None;
+ return std::nullopt;
}
};
@@ -1001,7 +1001,7 @@ public:
if (N->getLocationContext() != CalleeSFC)
return nullptr;
- Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
+ std::optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
if (!SP)
return nullptr;
@@ -1023,7 +1023,7 @@ public:
assert(RetE && "Tracking a return value for a void function");
// Handle cases where a reference is returned and then immediately used.
- Optional<Loc> LValue;
+ std::optional<Loc> LValue;
if (RetE->isGLValue()) {
if ((LValue = V.getAs<Loc>())) {
SVal RValue = State->getRawSVal(*LValue, RetE->getType());
@@ -1122,7 +1122,7 @@ public:
assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
// Are we at the entry node for this call?
- Optional<CallEnter> CE = N->getLocationAs<CallEnter>();
+ std::optional<CallEnter> CE = N->getLocationAs<CallEnter>();
if (!CE)
return nullptr;
@@ -1140,7 +1140,7 @@ public:
ProgramStateRef State = N->getState();
CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State);
for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
- Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
+ std::optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
if (!ArgV)
continue;
@@ -1187,8 +1187,6 @@ public:
}
};
-} // end of anonymous namespace
-
//===----------------------------------------------------------------------===//
// StoreSiteFinder
//===----------------------------------------------------------------------===//
@@ -1228,6 +1226,7 @@ public:
BugReporterContext &BRC,
PathSensitiveBugReport &BR) override;
};
+} // namespace
void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
@@ -1241,7 +1240,7 @@ void StoreSiteFinder::Profile(llvm::FoldingSetNodeID &ID) const {
/// Returns true if \p N represents the DeclStmt declaring and initializing
/// \p VR.
static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
- Optional<PostStmt> P = N->getLocationAs<PostStmt>();
+ std::optional<PostStmt> P = N->getLocationAs<PostStmt>();
if (!P)
return false;
@@ -1340,13 +1339,12 @@ static void showBRDiagnostics(llvm::raw_svector_ostream &OS, StoreInfo SI) {
static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
StoreInfo SI) {
const auto *VR = cast<VarRegion>(SI.Dest);
- const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+ const auto *D = VR->getDecl();
OS << "Passing ";
if (isa<loc::ConcreteInt>(SI.Value)) {
- OS << (isObjCPointer(Param) ? "nil object reference"
- : "null pointer value");
+ OS << (isObjCPointer(D) ? "nil object reference" : "null pointer value");
} else if (SI.Value.isUndef()) {
OS << "uninitialized value";
@@ -1361,12 +1359,19 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream &OS,
OS << "value";
}
- // Printed parameter indexes are 1-based, not 0-based.
- unsigned Idx = Param->getFunctionScopeIndex() + 1;
- OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
- if (VR->canPrintPretty()) {
- OS << " ";
- VR->printPretty(OS);
+ if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
+ // Printed parameter indexes are 1-based, not 0-based.
+ unsigned Idx = Param->getFunctionScopeIndex() + 1;
+ OS << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
+ if (VR->canPrintPretty()) {
+ OS << " ";
+ VR->printPretty(OS);
+ }
+ } else if (const auto *ImplParam = dyn_cast<ImplicitParamDecl>(D)) {
+ if (ImplParam->getParameterKind() ==
+ ImplicitParamDecl::ImplicitParamKind::ObjCSelf) {
+ OS << " via implicit parameter 'self'";
+ }
}
}
@@ -1410,6 +1415,83 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &OS,
}
}
+static bool isTrivialCopyOrMoveCtor(const CXXConstructExpr *CE) {
+ if (!CE)
+ return false;
+
+ const auto *CtorDecl = CE->getConstructor();
+
+ return CtorDecl->isCopyOrMoveConstructor() && CtorDecl->isTrivial();
+}
+
+static const Expr *tryExtractInitializerFromList(const InitListExpr *ILE,
+ const MemRegion *R) {
+
+ const auto *TVR = dyn_cast_or_null<TypedValueRegion>(R);
+
+ if (!TVR)
+ return nullptr;
+
+ const auto ITy = ILE->getType().getCanonicalType();
+
+ // Push each sub-region onto the stack.
+ std::stack<const TypedValueRegion *> TVRStack;
+ while (isa<FieldRegion>(TVR) || isa<ElementRegion>(TVR)) {
+ // We found a region that matches the type of the init list,
+ // so we assume this is the outer-most region. This can happen
+ // if the initializer list is inside a class. If our assumption
+ // is wrong, we return a nullptr in the end.
+ if (ITy == TVR->getValueType().getCanonicalType())
+ break;
+
+ TVRStack.push(TVR);
+ TVR = cast<TypedValueRegion>(TVR->getSuperRegion());
+ }
+
+ // If the type of the outer most region doesn't match the type
+ // of the ILE, we can't match the ILE and the region.
+ if (ITy != TVR->getValueType().getCanonicalType())
+ return nullptr;
+
+ const Expr *Init = ILE;
+ while (!TVRStack.empty()) {
+ TVR = TVRStack.top();
+ TVRStack.pop();
+
+ // We hit something that's not an init list before
+ // running out of regions, so we most likely failed.
+ if (!isa<InitListExpr>(Init))
+ return nullptr;
+
+ ILE = cast<InitListExpr>(Init);
+ auto NumInits = ILE->getNumInits();
+
+ if (const auto *FR = dyn_cast<FieldRegion>(TVR)) {
+ const auto *FD = FR->getDecl();
+
+ if (FD->getFieldIndex() >= NumInits)
+ return nullptr;
+
+ Init = ILE->getInit(FD->getFieldIndex());
+ } else if (const auto *ER = dyn_cast<ElementRegion>(TVR)) {
+ const auto Ind = ER->getIndex();
+
+ // If index is symbolic, we can't figure out which expression
+ // belongs to the region.
+ if (!Ind.isConstant())
+ return nullptr;
+
+ const auto IndVal = Ind.getAsInteger()->getLimitedValue();
+ if (IndVal >= NumInits)
+ return nullptr;
+
+ Init = ILE->getInit(IndVal);
+ }
+ }
+
+ return Init;
+}
+
PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
@@ -1431,7 +1513,8 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
// If this is a post initializer expression, initializing the region, we
// should track the initializer expression.
- if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) {
+ if (std::optional<PostInitializer> PIP =
+ Pred->getLocationAs<PostInitializer>()) {
const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
if (FieldReg == R) {
StoreSite = Pred;
@@ -1449,25 +1532,101 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
return nullptr;
if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) {
- Optional<PostStore> PS = Succ->getLocationAs<PostStore>();
+ std::optional<PostStore> PS = Succ->getLocationAs<PostStore>();
if (!PS || PS->getLocationValue() != R)
return nullptr;
}
StoreSite = Succ;
- // If this is an assignment expression, we can track the value
- // being assigned.
- if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
- if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>())
+ if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) {
+ // If this is an assignment expression, we can track the value
+ // being assigned.
+ if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
if (BO->isAssignmentOp())
InitE = BO->getRHS();
+ }
+ // If we have a declaration like 'S s{1,2}' that needs special
+ // handling, we handle it here.
+ else if (const auto *DS = P->getStmtAs<DeclStmt>()) {
+ const auto *Decl = DS->getSingleDecl();
+ if (isa<VarDecl>(Decl)) {
+ const auto *VD = cast<VarDecl>(Decl);
+
+ // FIXME: Here we only track the inner most region, so we lose
+ // information, but it's still better than a crash or no information
+ // at all.
+ //
+ // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y',
+ // and throw away the rest.
+ if (const auto *ILE = dyn_cast<InitListExpr>(VD->getInit()))
+ InitE = tryExtractInitializerFromList(ILE, R);
+ }
+ } else if (const auto *CE = P->getStmtAs<CXXConstructExpr>()) {
+
+ const auto State = Succ->getState();
+
+ if (isTrivialCopyOrMoveCtor(CE) && isa<SubRegion>(R)) {
+ // Migrate the field regions from the current object to
+ // the parent object. If we track 'a.y.e' and encounter
+ // 'S a = b' then we need to track 'b.y.e'.
+
+ // Push the regions to a stack, from last to first, so
+ // considering the example above the stack will look like
+ // (bottom) 'e' -> 'y' (top).
+
+ std::stack<const SubRegion *> SRStack;
+ const SubRegion *SR = cast<SubRegion>(R);
+ while (isa<FieldRegion>(SR) || isa<ElementRegion>(SR)) {
+ SRStack.push(SR);
+ SR = cast<SubRegion>(SR->getSuperRegion());
+ }
+
+ // Get the region for the object we copied/moved from.
+ const auto *OriginEx = CE->getArg(0);
+ const auto OriginVal =
+ State->getSVal(OriginEx, Succ->getLocationContext());
+
+ // Pop the stored field regions and apply them to the origin
+ // object in the same order we had them on the copy.
+ // OriginField will evolve like 'b' -> 'b.y' -> 'b.y.e'.
+ SVal OriginField = OriginVal;
+ while (!SRStack.empty()) {
+ const auto *TopR = SRStack.top();
+ SRStack.pop();
+
+ if (const auto *FR = dyn_cast<FieldRegion>(TopR)) {
+ OriginField = State->getLValue(FR->getDecl(), OriginField);
+ } else if (const auto *ER = dyn_cast<ElementRegion>(TopR)) {
+ OriginField = State->getLValue(ER->getElementType(),
+ ER->getIndex(), OriginField);
+ } else {
+ // FIXME: handle other region type
+ }
+ }
+
+ // Track 'b.y.e'.
+ getParentTracker().track(V, OriginField.getAsRegion(), Options);
+ InitE = OriginEx;
+ }
+ }
+ // This branch can occur in cases like `Ctor() : field{ x, y } {}'.
+ else if (const auto *ILE = P->getStmtAs<InitListExpr>()) {
+ // FIXME: Here we only track the top level region, so we lose
+ // information, but it's still better than a crash or no information
+ // at all.
+ //
+ // E.g.: The region we have is 's.s2.s3.s4.y' and we only track 'y', and
+ // throw away the rest.
+ InitE = tryExtractInitializerFromList(ILE, R);
+ }
+ }
// If this is a call entry, the variable should be a parameter.
// FIXME: Handle CXXThisRegion as well. (This is not a priority because
// 'this' should never be NULL, but this visitor isn't just for NULL and
// UndefinedVal.)
- if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
+ if (std::optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
if (const auto *VR = dyn_cast<VarRegion>(R)) {
if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
@@ -1590,7 +1749,7 @@ PathDiagnosticPieceRef StoreSiteFinder::VisitNode(const ExplodedNode *Succ,
R,
OldRegion};
- if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
+ if (std::optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
const Stmt *S = PS->getStmt();
const auto *DS = dyn_cast<DeclStmt>(S);
const auto *VR = dyn_cast<VarRegion>(R);
@@ -1983,7 +2142,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, const ExplodedNode *N) {
const ExplodedNode *NI = N;
do {
ProgramPoint ProgPoint = NI->getLocation();
- if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
const CFGBlock *srcBlk = BE->getSrc();
if (const Stmt *term = srcBlk->getTerminatorStmt()) {
if (term == CO) {
@@ -2058,6 +2217,7 @@ PathDiagnosticPieceRef StoreHandler::constructNote(StoreInfo SI,
return std::make_shared<PathDiagnosticEventPiece>(L, NodeText);
}
+namespace {
class DefaultStoreHandler final : public StoreHandler {
public:
using StoreHandler::StoreHandler;
@@ -2255,7 +2415,8 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
do {
// If that is satisfied we found our statement as an inlined call.
- if (Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>())
+ if (std::optional<CallExitEnd> CEE =
+ ExprNode->getLocationAs<CallExitEnd>())
if (CEE->getCalleeContext()->getCallSite() == E)
break;
@@ -2270,7 +2431,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
// FIXME: This code currently bypasses the call site for the
// conservatively evaluated allocator.
if (!BypassCXXNewExprEval)
- if (Optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = ExprNode->getLocationAs<StmtPoint>())
// See if we do not enter into another context.
if (SP->getStmt() == E && CurrentSFC == PredSFC)
break;
@@ -2285,7 +2446,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
return {};
// Finally, see if we inlined the call.
- Optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
+ std::optional<CallExitEnd> CEE = ExprNode->getLocationAs<CallExitEnd>();
if (!CEE)
return {};
@@ -2299,7 +2460,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
// Handle cases where a reference is returned and then immediately used.
if (cast<Expr>(E)->isGLValue())
- if (Optional<Loc> LValue = RetVal.getAs<Loc>())
+ if (std::optional<Loc> LValue = RetVal.getAs<Loc>())
RetVal = State->getSVal(*LValue);
// See if the return value is NULL. If so, suppress the report.
@@ -2307,7 +2468,7 @@ class InlinedFunctionCallHandler final : public ExpressionHandler {
bool EnableNullFPSuppression = false;
if (Opts.EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths)
- if (Optional<Loc> RetLoc = RetVal.getAs<Loc>())
+ if (std::optional<Loc> RetLoc = RetVal.getAs<Loc>())
EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
PathSensitiveBugReport &Report = getParentTracker().getReport();
@@ -2342,7 +2503,7 @@ public:
// what is written inside the pointer.
bool CanDereference = true;
if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
- if (SR->getSymbol()->getType()->getPointeeType()->isVoidType())
+ if (SR->getPointeeStaticType()->isVoidType())
CanDereference = false;
} else if (L->getRegionAs<AllocaRegion>())
CanDereference = false;
@@ -2395,6 +2556,29 @@ public:
if (!RVNode)
return {};
+ Tracker::Result CombinedResult;
+ Tracker &Parent = getParentTracker();
+
+ const auto track = [&CombinedResult, &Parent, ExprNode,
+ Opts](const Expr *Inner) {
+ CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts));
+ };
+
+ // FIXME: Initializer lists can appear in many different contexts
+ // and most of them needs a special handling. For now let's handle
+ // what we can. If the initializer list only has 1 element, we track
+ // that.
+ // This snippet even handles nesting, e.g.: int *x{{{{{y}}}}};
+ if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
+ if (ILE->getNumInits() == 1) {
+ track(ILE->getInit(0));
+
+ return CombinedResult;
+ }
+
+ return {};
+ }
+
ProgramStateRef RVState = RVNode->getState();
SVal V = RVState->getSValAsScalarOrLoc(E, RVNode->getLocationContext());
const auto *BO = dyn_cast<BinaryOperator>(E);
@@ -2406,13 +2590,6 @@ public:
SVal LHSV = RVState->getSVal(BO->getLHS(), RVNode->getLocationContext());
// Track both LHS and RHS of a multiplication.
- Tracker::Result CombinedResult;
- Tracker &Parent = getParentTracker();
-
- const auto track = [&CombinedResult, &Parent, ExprNode, Opts](Expr *Inner) {
- CombinedResult.combineWith(Parent.track(Inner, ExprNode, Opts));
- };
-
if (BO->getOpcode() == BO_Mul) {
if (LHSV.isZeroConstant())
track(BO->getLHS());
@@ -2426,6 +2603,7 @@ public:
return CombinedResult;
}
};
+} // namespace
Tracker::Tracker(PathSensitiveBugReport &Report) : Report(Report) {
// Default expression handlers.
@@ -2524,7 +2702,7 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
PathDiagnosticPieceRef
NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
PathSensitiveBugReport &BR) {
- Optional<PreStmt> P = N->getLocationAs<PreStmt>();
+ std::optional<PreStmt> P = N->getLocationAs<PreStmt>();
if (!P)
return nullptr;
@@ -2588,7 +2766,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
// If an assumption was made on a branch, it should be caught
// here by looking at the state transition.
- if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ if (std::optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
const CFGBlock *SrcBlock = BE->getSrc();
if (const Stmt *Term = SrcBlock->getTerminatorStmt()) {
// If the tag of the previous node is 'Eagerly Assume...' the current
@@ -2605,7 +2783,7 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
return nullptr;
}
- if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
+ if (std::optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
const ProgramPointTag *CurrentNodeTag = PS->getTag();
if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second)
return nullptr;
@@ -2744,13 +2922,11 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage);
}
-bool ConditionBRVisitor::patternMatch(const Expr *Ex,
- const Expr *ParentEx,
- raw_ostream &Out,
- BugReporterContext &BRC,
+bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx,
+ raw_ostream &Out, BugReporterContext &BRC,
PathSensitiveBugReport &report,
const ExplodedNode *N,
- Optional<bool> &prunable,
+ std::optional<bool> &prunable,
bool IsSameFieldName) {
const Expr *OriginalExpr = Ex;
Ex = Ex->IgnoreParenCasts();
@@ -2836,7 +3012,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue,
bool IsAssuming) {
bool shouldInvert = false;
- Optional<bool> shouldPrune;
+ std::optional<bool> shouldPrune;
// Check if the field name of the MemberExprs is ambiguous. Example:
// " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
@@ -2947,7 +3123,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
PathDiagnosticLocation Loc(Cond, SM, LCtx);
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message);
if (shouldPrune)
- event->setPrunable(shouldPrune.value());
+ event->setPrunable(*shouldPrune);
return event;
}
@@ -3070,7 +3246,7 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
if (!Ty->isIntegralOrEnumerationType())
return false;
- Optional<const llvm::APSInt *> IntValue;
+ std::optional<const llvm::APSInt *> IntValue;
if (!IsAssuming)
IntValue = getConcreteIntegerValue(CondVarExpr, N);
@@ -3081,9 +3257,9 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
Out << (TookTrue ? "not equal to 0" : "0");
} else {
if (Ty->isBooleanType())
- Out << (IntValue.value()->getBoolValue() ? "true" : "false");
+ Out << ((*IntValue)->getBoolValue() ? "true" : "false");
else
- Out << *IntValue.value();
+ Out << **IntValue;
}
return true;
@@ -3196,7 +3372,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
ProgramPoint ProgLoc = N->getLocation();
// We are only interested in visiting CallEnter nodes.
- Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
+ std::optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
if (!CEnter)
return nullptr;
@@ -3275,11 +3451,11 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
}
// And check for satisfiability
- Optional<bool> IsSAT = RefutationSolver->check();
+ std::optional<bool> IsSAT = RefutationSolver->check();
if (!IsSAT)
return;
- if (!IsSAT.value())
+ if (!*IsSAT)
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
}
@@ -3334,7 +3510,7 @@ PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N,
if (!T)
return nullptr;
- if (Optional<std::string> Msg = T->generateMessage(BRC, R)) {
+ if (std::optional<std::string> Msg = T->generateMessage(BRC, R)) {
PathDiagnosticLocation Loc =
PathDiagnosticLocation::create(PP, BRC.getSourceManager());
auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
index bb8b7492e248..94b2fde0a6f3 100644
--- a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
@@ -17,13 +17,13 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/Optional.h"
#include <iterator>
+#include <optional>
using namespace llvm;
using namespace clang;
-using MaybeCount = Optional<unsigned>;
+using MaybeCount = std::optional<unsigned>;
// A constructor helper.
static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
@@ -32,11 +32,11 @@ static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
return RequiredParams;
if (RequiredArgs)
return RequiredArgs;
- return None;
+ return std::nullopt;
}
ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
- ArrayRef<const char *> QualifiedName,
+ ArrayRef<StringRef> QualifiedName,
MaybeCount RequiredArgs /*= None*/,
MaybeCount RequiredParams /*= None*/)
: RequiredArgs(RequiredArgs),
@@ -44,11 +44,12 @@ ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
Flags(Flags) {
assert(!QualifiedName.empty());
this->QualifiedName.reserve(QualifiedName.size());
- llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName));
+ llvm::transform(QualifiedName, std::back_inserter(this->QualifiedName),
+ [](StringRef From) { return From.str(); });
}
/// Construct a CallDescription with default flags.
-ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
+ento::CallDescription::CallDescription(ArrayRef<StringRef> QualifiedName,
MaybeCount RequiredArgs /*= None*/,
MaybeCount RequiredParams /*= None*/)
: CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 3a8d69df7a64..8516e3643425 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -49,8 +49,6 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableList.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
@@ -62,6 +60,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <optional>
#include <utility>
#define DEBUG_TYPE "static-analyzer-call-event"
@@ -424,6 +423,38 @@ static SVal processArgument(SVal Value, const Expr *ArgumentExpr,
return Value;
}
+/// Cast the argument value to the type of the parameter at the function
+/// declaration.
+/// Returns the argument value if it didn't need a cast.
+/// Or returns the cast argument if it needed a cast.
+/// Or returns 'Unknown' if it would need a cast but the callsite and the
+/// runtime definition don't match in terms of argument and parameter count.
+static SVal castArgToParamTypeIfNeeded(const CallEvent &Call, unsigned ArgIdx,
+ SVal ArgVal, SValBuilder &SVB) {
+ const FunctionDecl *RTDecl =
+ Call.getRuntimeDefinition().getDecl()->getAsFunction();
+ const auto *CallExprDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+
+ if (!RTDecl || !CallExprDecl)
+ return ArgVal;
+
+ // The function decl of the Call (in the AST) will not have any parameter
+ // declarations, if it was 'only' declared without a prototype. However, the
+ // engine will find the appropriate runtime definition - basically a
+ // redeclaration, which has a function body (and a function prototype).
+ if (CallExprDecl->hasPrototype() || !RTDecl->hasPrototype())
+ return ArgVal;
+
+ // Only do this cast if the number arguments at the callsite matches with
+ // the parameters at the runtime definition.
+ if (Call.getNumArgs() != RTDecl->getNumParams())
+ return UnknownVal();
+
+ const Expr *ArgExpr = Call.getArgExpr(ArgIdx);
+ const ParmVarDecl *Param = RTDecl->getParamDecl(ArgIdx);
+ return SVB.evalCast(ArgVal, Param->getType(), ArgExpr->getType());
+}
+
static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
CallEvent::BindingsTy &Bindings,
SValBuilder &SVB,
@@ -449,12 +480,18 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// which makes getArgSVal() fail and return UnknownVal.
SVal ArgVal = Call.getArgSVal(Idx);
const Expr *ArgExpr = Call.getArgExpr(Idx);
- if (!ArgVal.isUnknown()) {
- Loc ParamLoc = SVB.makeLoc(
- MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
- Bindings.push_back(
- std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB)));
- }
+
+ if (ArgVal.isUnknown())
+ continue;
+
+ // Cast the argument value to match the type of the parameter in some
+ // edge-cases.
+ ArgVal = castArgToParamTypeIfNeeded(Call, Idx, ArgVal, SVB);
+
+ Loc ParamLoc = SVB.makeLoc(
+ MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
+ Bindings.push_back(
+ std::make_pair(ParamLoc, processArgument(ArgVal, ArgExpr, *I, SVB)));
}
// FIXME: Variadic arguments are not handled at all right now.
@@ -477,24 +514,23 @@ const ConstructionContext *CallEvent::getConstructionContext() const {
return nullptr;
}
-Optional<SVal>
-CallEvent::getReturnValueUnderConstruction() const {
+std::optional<SVal> CallEvent::getReturnValueUnderConstruction() const {
const auto *CC = getConstructionContext();
if (!CC)
- return None;
+ return std::nullopt;
EvalCallOptions CallOpts;
ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
- SVal RetVal =
- Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
- getLocationContext(), CC, CallOpts);
+ SVal RetVal = Engine.computeObjectUnderConstruction(
+ getOriginExpr(), getState(), &Engine.getBuilderContext(),
+ getLocationContext(), CC, CallOpts);
return RetVal;
}
ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -770,7 +806,7 @@ void CXXInstanceCall::getInitialStackFrameContents(
QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
// FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
- Optional<SVal> V =
+ std::optional<SVal> V =
StateMgr.getStoreManager().evalBaseToDerived(ThisVal, Ty);
if (!V) {
// We might have suffered some sort of placement new earlier, so
@@ -819,7 +855,7 @@ const BlockDataRegion *BlockCall::getBlockRegion() const {
ArrayRef<ParmVarDecl*> BlockCall::parameters() const {
const BlockDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -908,7 +944,7 @@ RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const {
ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const {
const ObjCMethodDecl *D = getDecl();
if (!D)
- return None;
+ return std::nullopt;
return D->parameters();
}
@@ -1124,7 +1160,7 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
// Find the redeclaration that defines the method.
if (!MD->hasBody()) {
- for (auto I : MD->redecls())
+ for (auto *I : MD->redecls())
if (I->hasBody())
MD = cast<ObjCMethodDecl>(I);
}
@@ -1185,10 +1221,10 @@ lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
// stays around until clang quits, which also may be bad if we
// need to release memory.
using PrivateMethodCache =
- llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+ llvm::DenseMap<PrivateMethodKey, std::optional<const ObjCMethodDecl *>>;
static PrivateMethodCache PMC;
- Optional<const ObjCMethodDecl *> &Val =
+ std::optional<const ObjCMethodDecl *> &Val =
PMC[{Interface, LookupSelector, InstanceMethod}];
// Query lookupPrivateMethod() if the cache does not hit.
@@ -1398,9 +1434,10 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
SVal ThisVal = State->getSVal(ThisPtr);
const Stmt *Trigger;
- if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>())
+ if (std::optional<CFGAutomaticObjDtor> AutoDtor =
+ E.getAs<CFGAutomaticObjDtor>())
Trigger = AutoDtor->getTriggerStmt();
- else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
+ else if (std::optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
Trigger = DeleteDtor->getDeleteExpr();
else
Trigger = Dtor->getBody();
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index 626ae1ae8066..84ad20a54807 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/Lex/Preprocessor.h"
+#include <optional>
namespace clang {
@@ -110,14 +111,13 @@ Nullability getNullabilityAnnotation(QualType Type) {
return Nullability::Unspecified;
}
-llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
- const Preprocessor &PP) {
+std::optional<int> tryExpandAsInteger(StringRef Macro, const Preprocessor &PP) {
const auto *MacroII = PP.getIdentifierInfo(Macro);
if (!MacroII)
- return llvm::None;
+ return std::nullopt;
const MacroInfo *MI = PP.getMacroInfo(MacroII);
if (!MI)
- return llvm::None;
+ return std::nullopt;
// Filter out parens.
std::vector<Token> FilteredTokens;
@@ -131,12 +131,12 @@ llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
// FIXME: EOF macro token coming from a PCH file on macOS while marked as
// literal, doesn't contain any literal data
if (!T.isLiteral() || !T.getLiteralData())
- return llvm::None;
+ return std::nullopt;
StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
llvm::APInt IntValue;
constexpr unsigned AutoSenseRadix = 0;
if (ValueStr.getAsInteger(AutoSenseRadix, IntValue))
- return llvm::None;
+ return std::nullopt;
// Parse an optional minus sign.
size_t Size = FilteredTokens.size();
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 94287b7992dd..6fc16223ea82 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -28,6 +28,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
#include <cassert>
+#include <optional>
#include <vector>
using namespace clang;
@@ -35,10 +36,7 @@ using namespace ento;
bool CheckerManager::hasPathSensitiveCheckers() const {
const auto IfAnyAreNonEmpty = [](const auto &... Callbacks) -> bool {
- bool Result = false;
- // FIXME: Use fold expressions in C++17.
- LLVM_ATTRIBUTE_UNUSED int Unused[]{0, (Result |= !Callbacks.empty())...};
- return Result;
+ return (!Callbacks.empty() || ...);
};
return IfAnyAreNonEmpty(
StmtCheckers, PreObjCMessageCheckers, ObjCMessageNilCheckers,
@@ -656,7 +654,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
ExprEngine &Eng,
const EvalCallOptions &CallOpts) {
for (auto *const Pred : Src) {
- Optional<CheckerNameRef> evaluatorChecker;
+ std::optional<CheckerNameRef> evaluatorChecker;
ExplodedNodeSet checkDst;
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index de90f4a71be0..386a34f6cd4a 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -26,7 +26,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
@@ -34,6 +33,7 @@
#include <algorithm>
#include <cassert>
#include <memory>
+#include <optional>
#include <utility>
using namespace clang;
@@ -273,10 +273,10 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
const ReturnStmt *RS = nullptr;
if (!L.getSrc()->empty()) {
CFGElement LastElement = L.getSrc()->back();
- if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) {
RS = dyn_cast<ReturnStmt>(LastStmt->getStmt());
- } else if (Optional<CFGAutomaticObjDtor> AutoDtor =
- LastElement.getAs<CFGAutomaticObjDtor>()) {
+ } else if (std::optional<CFGAutomaticObjDtor> AutoDtor =
+ LastElement.getAs<CFGAutomaticObjDtor>()) {
RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt());
}
}
@@ -314,11 +314,10 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
setBlockCounter(Counter);
// Process the entrance of the block.
- if (Optional<CFGElement> E = L.getFirstElement()) {
+ if (std::optional<CFGElement> E = L.getFirstElement()) {
NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
ExprEng.processCFGElement(*E, Pred, 0, &Ctx);
- }
- else
+ } else
HandleBlockExit(L.getBlock(), Pred);
}
@@ -616,7 +615,7 @@ void CoreEngine::enqueue(ExplodedNodeSet &Set,
}
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) {
- for (auto I : Set) {
+ for (auto *I : Set) {
// If we are in an inlined call, generate CallExitBegin node.
if (I->getLocationContext()->getParent()) {
I = generateCallExitBeginNode(I, RS);
diff --git a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
index db9698b4086e..6a86536492cd 100644
--- a/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/DynamicExtent.cpp
@@ -44,6 +44,7 @@ DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
const MemRegion *MR,
SValBuilder &SVB,
QualType ElementTy) {
+ assert(MR != nullptr && "Not-null region expected");
MR = MR->StripCasts();
DefinedOrUnknownSVal Size = getDynamicExtent(State, MR, SVB);
diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 719793fa224c..3d017b81762a 100644
--- a/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -274,7 +274,8 @@ void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
const Stmt *S = I->first.getStmt();
Indent(Out, InnerSpace, IsDot)
- << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": ";
+ << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"kind\": \""
+ << S->getStmtClassName() << "\", \"pretty\": ";
S->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
Out << ", \"value\": ";
diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 294572b7dbe4..d274d4d16db3 100644
--- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -25,12 +25,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <memory>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -139,7 +139,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Condition 10.
const ProgramPoint SuccLoc = succ->getLocation();
- if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
if (CallEvent::isCallStmt(SP->getStmt()))
return false;
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 19149d079822..977c2b7f51fd 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -48,6 +48,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
@@ -64,7 +65,6 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/ImmutableSet.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
@@ -77,6 +77,7 @@
#include <cassert>
#include <cstdint>
#include <memory>
+#include <optional>
#include <string>
#include <tuple>
#include <utility>
@@ -187,7 +188,7 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
// This trait is responsible for storing the index of the element that is to be
// constructed in the next iteration. As a result a CXXConstructExpr is only
-// stored if it is array type. Also the index is the index of the continous
+// stored if it is array type. Also the index is the index of the continuous
// memory region, which is important for multi-dimensional arrays. E.g:: int
// arr[2][2]; assume arr[1][1] will be the next element under construction, so
// the index is 3.
@@ -204,6 +205,12 @@ typedef llvm::ImmutableMap<
std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
PendingInitLoopMap;
REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap)
+
+typedef llvm::ImmutableMap<const LocationContext *, unsigned>
+ PendingArrayDestructionMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingArrayDestruction,
+ PendingArrayDestructionMap)
+
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -263,7 +270,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
svalBuilder.makeZeroVal(T),
svalBuilder.getConditionType());
- Optional<DefinedOrUnknownSVal> Constraint =
+ std::optional<DefinedOrUnknownSVal> Constraint =
Constraint_untested.getAs<DefinedOrUnknownSVal>();
if (!Constraint)
@@ -283,7 +290,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
const MemRegion *R = state->getRegion(SelfD, InitLoc);
SVal V = state->getSVal(loc::MemRegionVal(R));
- if (Optional<Loc> LV = V.getAs<Loc>()) {
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
// Assume that the pointer value in 'self' is non-null.
state = state->assume(*LV, true);
assert(state && "'self' cannot be null");
@@ -299,7 +306,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
if (SFC->getParent() == nullptr) {
loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
- if (Optional<Loc> LV = V.getAs<Loc>()) {
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
state = state->assume(*LV, true);
assert(state && "'this' cannot be null");
}
@@ -375,7 +382,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
// into that region. This is not correct, but it is better than nothing.
const TypedValueRegion *TR = nullptr;
if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {
- if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
State = finishObjectConstruction(State, MT, LC);
State = State->BindExpr(Result, LC, *V);
return State;
@@ -470,12 +477,11 @@ ProgramStateRef ExprEngine::setIndexOfElementToConstruct(
return State->set<IndexOfElementToConstruct>(Key, Idx);
}
-Optional<unsigned> ExprEngine::getPendingInitLoop(ProgramStateRef State,
- const CXXConstructExpr *E,
- const LocationContext *LCtx) {
-
- return Optional<unsigned>::create(
- State->get<PendingInitLoop>({E, LCtx->getStackFrame()}));
+std::optional<unsigned>
+ExprEngine::getPendingInitLoop(ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ const unsigned *V = State->get<PendingInitLoop>({E, LCtx->getStackFrame()});
+ return V ? std::make_optional(*V) : std::nullopt;
}
ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State,
@@ -498,13 +504,13 @@ ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State,
return State->set<PendingInitLoop>(Key, Size);
}
-Optional<unsigned>
+std::optional<unsigned>
ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State,
const CXXConstructExpr *E,
const LocationContext *LCtx) {
-
- return Optional<unsigned>::create(
- State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()}));
+ const unsigned *V =
+ State->get<IndexOfElementToConstruct>({E, LCtx->getStackFrame()});
+ return V ? std::make_optional(*V) : std::nullopt;
}
ProgramStateRef
@@ -517,6 +523,36 @@ ExprEngine::removeIndexOfElementToConstruct(ProgramStateRef State,
return State->remove<IndexOfElementToConstruct>(Key);
}
+std::optional<unsigned>
+ExprEngine::getPendingArrayDestruction(ProgramStateRef State,
+ const LocationContext *LCtx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ const unsigned *V =
+ State->get<PendingArrayDestruction>(LCtx->getStackFrame());
+ return V ? std::make_optional(*V) : std::nullopt;
+}
+
+ProgramStateRef ExprEngine::setPendingArrayDestruction(
+ ProgramStateRef State, const LocationContext *LCtx, unsigned Idx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ auto Key = LCtx->getStackFrame();
+
+ return State->set<PendingArrayDestruction>(Key, Idx);
+}
+
+ProgramStateRef
+ExprEngine::removePendingArrayDestruction(ProgramStateRef State,
+ const LocationContext *LCtx) {
+ assert(LCtx && "LocationContext shouldn't be null!");
+
+ auto Key = LCtx->getStackFrame();
+
+ assert(LCtx && State->contains<PendingArrayDestruction>(Key));
+ return State->remove<PendingArrayDestruction>(Key);
+}
+
ProgramStateRef
ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
const ConstructionContextItem &Item,
@@ -537,9 +573,10 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
Init = Item.getCXXCtorInitializer()->getInit();
// In an ArrayInitLoopExpr the real initializer is returned by
- // getSubExpr().
+ // getSubExpr(). Note that AILEs can be nested in case of
+ // multidimesnional arrays.
if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init))
- Init = AILE->getSubExpr();
+ Init = extractElementInitializerFromNestedAILE(AILE);
// FIXME: Currently the state might already contain the marker due to
// incorrect handling of temporaries bound to default parameters.
@@ -557,12 +594,13 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
return State->set<ObjectsUnderConstruction>(Key, V);
}
-Optional<SVal>
+std::optional<SVal>
ExprEngine::getObjectUnderConstruction(ProgramStateRef State,
const ConstructionContextItem &Item,
const LocationContext *LC) {
ConstructedObjectKey Key(Item, LC->getStackFrame());
- return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key));
+ const SVal *V = State->get<ObjectsUnderConstruction>(Key);
+ return V ? std::make_optional(*V) : std::nullopt;
}
ProgramStateRef
@@ -656,7 +694,7 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
continue;
if (!HasItem) {
- Out << "[" << NL;
+ Out << '[' << NL;
HasItem = true;
}
@@ -687,12 +725,11 @@ printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
static void printIndicesOfElementsToConstructJson(
raw_ostream &Out, ProgramStateRef State, const char *NL,
- const LocationContext *LCtx, const ASTContext &Context,
- unsigned int Space = 0, bool IsDot = false) {
+ const LocationContext *LCtx, unsigned int Space = 0, bool IsDot = false) {
using KeyT = std::pair<const Expr *, const LocationContext *>;
- PrintingPolicy PP =
- LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+ const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP = Context.getPrintingPolicy();
++Space;
bool HasItem = false;
@@ -705,7 +742,7 @@ static void printIndicesOfElementsToConstructJson(
continue;
if (!HasItem) {
- Out << "[" << NL;
+ Out << '[' << NL;
HasItem = true;
}
@@ -724,17 +761,17 @@ static void printIndicesOfElementsToConstructJson(
const Expr *E = Key.first;
Out << "\"stmt_id\": " << E->getID(Context);
- // Kind - hack to display the current index
- Out << ", \"kind\": \"Cur: " << Value - 1 << "\"";
+ // Kind
+ Out << ", \"kind\": null";
// Pretty-print
Out << ", \"pretty\": ";
- Out << "\"" << E->getStmtClassName() << " "
+ Out << "\"" << E->getStmtClassName() << ' '
<< E->getSourceRange().printToString(Context.getSourceManager()) << " '"
<< QualType::getAsString(E->getType().split(), PP);
Out << "'\"";
- Out << ", \"value\": \"Next: " << Value << "\" }";
+ Out << ", \"value\": \"Current index: " << Value - 1 << "\" }";
if (Key != LastKey)
Out << ',';
@@ -748,40 +785,168 @@ static void printIndicesOfElementsToConstructJson(
}
}
-void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
- const LocationContext *LCtx, const char *NL,
- unsigned int Space, bool IsDot) const {
- Indent(Out, Space, IsDot) << "\"constructing_objects\": ";
+static void printPendingInitLoopJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL,
+ const LocationContext *LCtx,
+ unsigned int Space = 0,
+ bool IsDot = false) {
+ using KeyT = std::pair<const CXXConstructExpr *, const LocationContext *>;
- if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) {
- ++Space;
- Out << '[' << NL;
- LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
- printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot);
- });
+ const auto &Context = LCtx->getAnalysisDeclContext()->getASTContext();
+ PrintingPolicy PP = Context.getPrintingPolicy();
- --Space;
- Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects".
- } else {
- Out << "null," << NL;
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey;
+ for (const auto &I : State->get<PendingInitLoop>()) {
+ const KeyT &Key = I.first;
+ if (Key.second != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << '[' << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
}
- Indent(Out, Space, IsDot) << "\"index_of_element\": ";
- if (LCtx && !State->get<IndexOfElementToConstruct>().isEmpty()) {
- ++Space;
+ for (const auto &I : State->get<PendingInitLoop>()) {
+ const KeyT &Key = I.first;
+ unsigned Value = I.second;
+ if (Key.second != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ const CXXConstructExpr *E = Key.first;
+ Out << "\"stmt_id\": " << E->getID(Context);
+
+ Out << ", \"kind\": null";
+ Out << ", \"pretty\": ";
+ Out << '\"' << E->getStmtClassName() << ' '
+ << E->getSourceRange().printToString(Context.getSourceManager()) << " '"
+ << QualType::getAsString(E->getType().split(), PP);
+ Out << "'\"";
+
+ Out << ", \"value\": \"Flattened size: " << Value << "\"}";
+
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
+
+static void
+printPendingArrayDestructionsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const LocationContext *LCtx,
+ unsigned int Space = 0, bool IsDot = false) {
+ using KeyT = const LocationContext *;
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ KeyT LastKey = nullptr;
+ for (const auto &I : State->get<PendingArrayDestruction>()) {
+ const KeyT &Key = I.first;
+ if (Key != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << '[' << NL;
+ HasItem = true;
+ }
+
+ LastKey = Key;
+ }
+
+ for (const auto &I : State->get<PendingArrayDestruction>()) {
+ const KeyT &Key = I.first;
+ if (Key != LCtx)
+ continue;
+
+ Indent(Out, Space, IsDot) << "{ ";
+
+ Out << "\"stmt_id\": null";
+ Out << ", \"kind\": null";
+ Out << ", \"pretty\": \"Current index: \"";
+ Out << ", \"value\": \"" << I.second << "\" }";
+
+ if (Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
+ }
+}
- auto &Context = getContext();
+/// A helper function to generalize program state trait printing.
+/// The function invokes Printer as 'Printer(Out, State, NL, LC, Space, IsDot,
+/// std::forward<Args>(args)...)'. \n One possible type for Printer is
+/// 'void()(raw_ostream &, ProgramStateRef, const char *, const LocationContext
+/// *, unsigned int, bool, ...)' \n \param Trait The state trait to be printed.
+/// \param Printer A void function that prints Trait.
+/// \param Args An additional parameter pack that is passed to Print upon
+/// invocation.
+template <typename Trait, typename Printer, typename... Args>
+static void printStateTraitWithLocationContextJson(
+ raw_ostream &Out, ProgramStateRef State, const LocationContext *LCtx,
+ const char *NL, unsigned int Space, bool IsDot,
+ const char *jsonPropertyName, Printer printer, Args &&...args) {
+
+ using RequiredType =
+ void (*)(raw_ostream &, ProgramStateRef, const char *,
+ const LocationContext *, unsigned int, bool, Args &&...);
+
+ // Try to do as much compile time checking as possible.
+ // FIXME: check for invocable instead of function?
+ static_assert(std::is_function_v<std::remove_pointer_t<Printer>>,
+ "Printer is not a function!");
+ static_assert(std::is_convertible_v<Printer, RequiredType>,
+ "Printer doesn't have the required type!");
+
+ if (LCtx && !State->get<Trait>().isEmpty()) {
+ Indent(Out, Space, IsDot) << '\"' << jsonPropertyName << "\": ";
+ ++Space;
Out << '[' << NL;
LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
- printIndicesOfElementsToConstructJson(Out, State, NL, LC, Context, Space,
- IsDot);
+ printer(Out, State, NL, LC, Space, IsDot, std::forward<Args>(args)...);
});
--Space;
- Indent(Out, Space, IsDot) << "]," << NL; // End of "index_of_element".
- } else {
- Out << "null," << NL;
+ Indent(Out, Space, IsDot) << "]," << NL; // End of "jsonPropertyName".
}
+}
+
+void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
+ const LocationContext *LCtx, const char *NL,
+ unsigned int Space, bool IsDot) const {
+
+ printStateTraitWithLocationContextJson<ObjectsUnderConstruction>(
+ Out, State, LCtx, NL, Space, IsDot, "constructing_objects",
+ printObjectsUnderConstructionJson);
+ printStateTraitWithLocationContextJson<IndexOfElementToConstruct>(
+ Out, State, LCtx, NL, Space, IsDot, "index_of_element",
+ printIndicesOfElementsToConstructJson);
+ printStateTraitWithLocationContextJson<PendingInitLoop>(
+ Out, State, LCtx, NL, Space, IsDot, "pending_init_loops",
+ printPendingInitLoopJson);
+ printStateTraitWithLocationContextJson<PendingArrayDestruction>(
+ Out, State, LCtx, NL, Space, IsDot, "pending_destructors",
+ printPendingArrayDestructionsJson);
getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
IsDot);
@@ -1034,7 +1199,7 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
SVal LValue = State->getSVal(Init, stackFrame);
if (!Field->getType()->isReferenceType())
- if (Optional<Loc> LValueLoc = LValue.getAs<Loc>())
+ if (std::optional<Loc> LValueLoc = LValue.getAs<Loc>())
InitVal = State->getSVal(*LValueLoc);
// If we fail to get the value for some reason, use a symbolic value.
@@ -1071,6 +1236,43 @@ void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
}
+std::pair<ProgramStateRef, uint64_t>
+ExprEngine::prepareStateForArrayDestruction(const ProgramStateRef State,
+ const MemRegion *Region,
+ const QualType &ElementTy,
+ const LocationContext *LCtx,
+ SVal *ElementCountVal) {
+ assert(Region != nullptr && "Not-null region expected");
+
+ QualType Ty = ElementTy.getDesugaredType(getContext());
+ while (const auto *NTy = dyn_cast<ArrayType>(Ty))
+ Ty = NTy->getElementType().getDesugaredType(getContext());
+
+ auto ElementCount = getDynamicElementCount(State, Region, svalBuilder, Ty);
+
+ if (ElementCountVal)
+ *ElementCountVal = ElementCount;
+
+ // Note: the destructors are called in reverse order.
+ unsigned Idx = 0;
+ if (auto OptionalIdx = getPendingArrayDestruction(State, LCtx)) {
+ Idx = *OptionalIdx;
+ } else {
+ // The element count is either unknown, or an SVal that's not an integer.
+ if (!ElementCount.isConstant())
+ return {State, 0};
+
+ Idx = ElementCount.getAsInteger()->getLimitedValue();
+ }
+
+ if (Idx == 0)
+ return {State, 0};
+
+ --Idx;
+
+ return {setPendingArrayDestruction(State, LCtx, Idx), Idx};
+}
+
void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
ExplodedNode *Pred) {
ExplodedNodeSet Dst;
@@ -1120,11 +1322,14 @@ void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ const auto *DtorDecl = Dtor.getDestructorDecl(getContext());
const VarDecl *varDecl = Dtor.getVarDecl();
QualType varType = varDecl->getType();
ProgramStateRef state = Pred->getState();
- SVal dest = state->getLValue(varDecl, Pred->getLocationContext());
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ SVal dest = state->getLValue(varDecl, LCtx);
const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion();
if (varType->isReferenceType()) {
@@ -1140,14 +1345,46 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
varType = cast<TypedValueRegion>(Region)->getValueType();
}
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
+ unsigned Idx = 0;
+ if (isa<ArrayType>(varType)) {
+ SVal ElementCount;
+ std::tie(state, Idx) = prepareStateForArrayDestruction(
+ state, Region, varType, LCtx, &ElementCount);
+
+ if (ElementCount.isConstant()) {
+ uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue();
+ assert(ArrayLength &&
+ "An automatic dtor for a 0 length array shouldn't be triggered!");
+
+ // Still handle this case if we don't have assertions enabled.
+ if (!ArrayLength) {
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping automatic 0 length array destruction, "
+ "which shouldn't be in the CFG.");
+ PostImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateSink(PP, Pred->getState(), Pred);
+ return;
+ }
+ }
+ }
+
EvalCallOptions CallOpts;
Region = makeElementRegion(state, loc::MemRegionVal(Region), varType,
- CallOpts.IsArrayCtorOrDtor)
+ CallOpts.IsArrayCtorOrDtor, Idx)
.getAsRegion();
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(DtorDecl, varDecl->getLocation(), LCtx, &PT);
+ Pred = Bldr.generateNode(PP, state, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
+
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
/*IsBase=*/false, Pred, Dst, CallOpts);
}
@@ -1175,20 +1412,54 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
return;
}
+ auto getDtorDecl = [](const QualType &DTy) {
+ const CXXRecordDecl *RD = DTy->getAsCXXRecordDecl();
+ return RD->getDestructor();
+ };
+
+ unsigned Idx = 0;
EvalCallOptions CallOpts;
const MemRegion *ArgR = ArgVal.getAsRegion();
+
if (DE->isArrayForm()) {
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
CallOpts.IsArrayCtorOrDtor = true;
// Yes, it may even be a multi-dimensional array.
while (const auto *AT = getContext().getAsArrayType(DTy))
DTy = AT->getElementType();
- if (ArgR)
- ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy);
+
+ if (ArgR) {
+ SVal ElementCount;
+ std::tie(State, Idx) = prepareStateForArrayDestruction(
+ State, ArgR, DTy, LCtx, &ElementCount);
+
+ // If we're about to destruct a 0 length array, don't run any of the
+ // destructors.
+ if (ElementCount.isConstant() &&
+ ElementCount.getAsInteger()->getLimitedValue() == 0) {
+
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping 0 length array delete destruction");
+ PostImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(PP, Pred->getState(), Pred);
+ return;
+ }
+
+ ArgR = State->getLValue(DTy, svalBuilder.makeArrayIndex(Idx), ArgVal)
+ .getAsRegion();
+ }
}
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(getDtorDecl(DTy), DE->getBeginLoc(), LCtx, &PT);
+ Pred = Bldr.generateNode(PP, State, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
+
VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
}
@@ -1214,6 +1485,7 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const auto *DtorDecl = D.getDestructorDecl(getContext());
const FieldDecl *Member = D.getFieldDecl();
QualType T = Member->getType();
ProgramStateRef State = Pred->getState();
@@ -1225,11 +1497,44 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>();
SVal FieldVal = State->getLValue(Member, ThisLoc);
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
+ unsigned Idx = 0;
+ if (isa<ArrayType>(T)) {
+ SVal ElementCount;
+ std::tie(State, Idx) = prepareStateForArrayDestruction(
+ State, FieldVal.getAsRegion(), T, LCtx, &ElementCount);
+
+ if (ElementCount.isConstant()) {
+ uint64_t ArrayLength = ElementCount.getAsInteger()->getLimitedValue();
+ assert(ArrayLength &&
+ "A member dtor for a 0 length array shouldn't be triggered!");
+
+ // Still handle this case if we don't have assertions enabled.
+ if (!ArrayLength) {
+ static SimpleProgramPointTag PT(
+ "ExprEngine", "Skipping member 0 length array destruction, which "
+ "shouldn't be in the CFG.");
+ PostImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateSink(PP, Pred->getState(), Pred);
+ return;
+ }
+ }
+ }
+
EvalCallOptions CallOpts;
- FieldVal = makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor);
+ FieldVal =
+ makeElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor, Idx);
+
+ NodeBuilder Bldr(Pred, Dst, getBuilderContext());
+
+ static SimpleProgramPointTag PT("ExprEngine",
+ "Prepare for object destruction");
+ PreImplicitCall PP(DtorDecl, Member->getLocation(), LCtx, &PT);
+ Pred = Bldr.generateNode(PP, State, Pred);
+
+ if (!Pred)
+ return;
+ Bldr.takeNodes(Pred);
VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
/*IsBase=*/false, Pred, Dst, CallOpts);
@@ -1243,9 +1548,8 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
const LocationContext *LC = Pred->getLocationContext();
const MemRegion *MR = nullptr;
- if (Optional<SVal> V =
- getObjectUnderConstruction(State, D.getBindTemporaryExpr(),
- Pred->getLocationContext())) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(
+ State, D.getBindTemporaryExpr(), Pred->getLocationContext())) {
// FIXME: Currently we insert temporary destructors for default parameters,
// but we don't insert the constructors, so the entry in
// ObjectsUnderConstruction may be missing.
@@ -1280,15 +1584,31 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
EvalCallOptions CallOpts;
CallOpts.IsTemporaryCtorOrDtor = true;
if (!MR) {
- // If we have no MR, we still need to unwrap the array to avoid destroying
- // the whole array at once. Regardless, we'd eventually need to model array
- // destructors properly, element-by-element.
+ // FIXME: If we have no MR, we still need to unwrap the array to avoid
+ // destroying the whole array at once.
+ //
+ // For this case there is no universal solution as there is no way to
+ // directly create an array of temporary objects. There are some expressions
+ // however which can create temporary objects and have an array type.
+ //
+ // E.g.: std::initializer_list<S>{S(), S()};
+ //
+ // The expression above has a type of 'const struct S[2]' but it's a single
+ // 'std::initializer_list<>'. The destructors of the 2 temporary 'S()'
+ // objects will be called anyway, because they are 2 separate objects in 2
+ // separate clusters, i.e.: not an array.
+ //
+ // Now the 'std::initializer_list<>' is not an array either even though it
+ // has the type of an array. The point is, we only want to invoke the
+ // destructor for the initializer list once not twice or so.
while (const ArrayType *AT = getContext().getAsArrayType(T)) {
T = AT->getElementType();
- CallOpts.IsArrayCtorOrDtor = true;
+
+ // FIXME: Enable this flag once we handle this case properly.
+ // CallOpts.IsArrayCtorOrDtor = true;
}
} else {
- // We'd eventually need to makeElementRegion() trick here,
+ // FIXME: We'd eventually need to makeElementRegion() trick here,
// but for now we don't have the respective construction contexts,
// so MR would always be null in this case. Do nothing for now.
}
@@ -1426,6 +1746,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTaskyieldDirectiveClass:
case Stmt::OMPBarrierDirectiveClass:
case Stmt::OMPTaskwaitDirectiveClass:
+ case Stmt::OMPErrorDirectiveClass:
case Stmt::OMPTaskgroupDirectiveClass:
case Stmt::OMPFlushDirectiveClass:
case Stmt::OMPDepobjDirectiveClass:
@@ -1579,6 +1900,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ConceptSpecializationExprClass:
case Stmt::CXXRewrittenBinaryOperatorClass:
case Stmt::RequiresExprClass:
+ case Expr::CXXParenListInitExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
@@ -1638,7 +1960,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
IsTemporary = true;
}
- Optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
+ std::optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
if (!ConstantVal)
ConstantVal = UnknownVal();
@@ -1793,7 +2115,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
}
// FALLTHROUGH
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case Stmt::CallExprClass:
@@ -2104,7 +2426,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
continue;
if (L.getAs<CallEnter>())
continue;
- if (Optional<StmtPoint> SP = L.getAs<StmtPoint>())
+ if (std::optional<StmtPoint> SP = L.getAs<StmtPoint>())
if (SP->getStmt() == CE)
continue;
break;
@@ -2117,8 +2439,9 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
// Build an Epsilon node from which we will restart the analyzes.
// Note that CE is permitted to be NULL!
- ProgramPoint NewNodeLoc =
- EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+ static SimpleProgramPointTag PT("ExprEngine", "Replay without inlining");
+ ProgramPoint NewNodeLoc = EpsilonPoint(
+ BeforeProcessingCall->getLocationContext(), CE, nullptr, &PT);
// Add the special flag to GDM to signal retrying with no inlining.
// Note, changing the state ensures that we are not going to cache out.
ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
@@ -2310,7 +2633,7 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
for (; I != E; ++I) {
CFGElement Elem = *I;
- Optional<CFGStmt> CS = Elem.getAs<CFGStmt>();
+ std::optional<CFGStmt> CS = Elem.getAs<CFGStmt>();
if (!CS)
continue;
const Stmt *LastStmt = CS->getStmt();
@@ -2348,9 +2671,9 @@ bool ExprEngine::hasMoreIteration(ProgramStateRef State,
}
/// Split the state on whether there are any more iterations left for this loop.
-/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the
-/// acquisition of the loop condition value failed.
-static Optional<std::pair<ProgramStateRef, ProgramStateRef>>
+/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or std::nullopt when
+/// the acquisition of the loop condition value failed.
+static std::optional<std::pair<ProgramStateRef, ProgramStateRef>>
assumeCondition(const Stmt *Condition, ExplodedNode *N) {
ProgramStateRef State = N->getState();
if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) {
@@ -2389,7 +2712,7 @@ assumeCondition(const Stmt *Condition, ExplodedNode *N) {
// If the condition is still unknown, give up.
if (X.isUnknownOrUndef())
- return None;
+ return std::nullopt;
DefinedSVal V = X.castAs<DefinedSVal>();
@@ -2514,7 +2837,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
using iterator = IndirectGotoNodeBuilder::iterator;
- if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
+ if (std::optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
const LabelDecl *L = LV->getLabel();
for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) {
@@ -2666,7 +2989,7 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
V2 = V1;
ProgramStateRef StateCase;
- if (Optional<NonLoc> NL = CondV.getAs<NonLoc>())
+ if (std::optional<NonLoc> NL = CondV.getAs<NonLoc>())
std::tie(StateCase, DefaultSt) =
DefaultSt->assumeInclusiveRange(*NL, V1, V2);
else // UnknownVal
@@ -2725,14 +3048,14 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
const Decl *D = LocCtxt->getDecl();
const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
- Optional<std::pair<SVal, QualType>> VInfo;
+ std::optional<std::pair<SVal, QualType>> VInfo;
if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
MD->getParent()->isLambda()) {
// Lookup the field of the lambda.
const CXXRecordDecl *CXXRec = MD->getParent();
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
@@ -2839,6 +3162,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
+ if (const auto *TPO = dyn_cast<TemplateParamObjectDecl>(D)) {
+ // FIXME: We should meaningfully implement this.
+ (void)TPO;
+ return;
+ }
+
llvm_unreachable("Support for this Decl not implemented.");
}
@@ -2857,7 +3186,7 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex,
for (auto *Node : CheckerPreStmt) {
// The constructor visitior has already taken care of everything.
- if (auto *CE = dyn_cast<CXXConstructExpr>(Ex->getSubExpr()))
+ if (isa<CXXConstructExpr>(Ex->getSubExpr()))
break;
const LocationContext *LCtx = Node->getLocationContext();
@@ -3035,6 +3364,14 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
SVal baseExprVal =
MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
+ // FIXME: Copied from RegionStoreManager::bind()
+ if (const auto *SR =
+ dyn_cast_or_null<SymbolicRegion>(baseExprVal.getAsRegion())) {
+ QualType T = SR->getPointeeStaticType();
+ baseExprVal =
+ loc::MemRegionVal(getStoreManager().GetElementZeroRegion(SR, T));
+ }
+
const auto *field = cast<FieldDecl>(Member);
SVal L = state->getLValue(field, baseExprVal);
@@ -3124,7 +3461,8 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(
for (const std::pair<SVal, SVal> &LocAndVal : LocAndVals) {
// Cases (1) and (2).
const MemRegion *MR = LocAndVal.first.getAsRegion();
- if (!MR || !MR->hasStackStorage()) {
+ if (!MR ||
+ !isa<StackSpaceRegion, StaticGlobalSpaceRegion>(MR->getMemorySpace())) {
Escaped.push_back(LocAndVal.second);
continue;
}
@@ -3248,7 +3586,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
Val, LC, /* notifyChanges = */ !atDeclInit);
const MemRegion *LocReg = nullptr;
- if (Optional<loc::MemRegionVal> LocRegVal =
+ if (std::optional<loc::MemRegionVal> LocRegVal =
location.getAs<loc::MemRegionVal>()) {
LocReg = LocRegVal->getRegion();
}
@@ -3389,7 +3727,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
ProgramStateRef state = Pred->getState();
SVal V = state->getSVal(Ex, Pred->getLocationContext());
- Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
if (SEV && SEV->isExpression()) {
const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags =
geteagerlyAssumeBinOpBifurcationTags();
@@ -3430,7 +3768,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
SVal X = state->getSVal(O, Pred->getLocationContext());
assert(!isa<NonLoc>(X)); // Should be an Lval, or unknown, undef.
- if (Optional<Loc> LV = X.getAs<Loc>())
+ if (std::optional<Loc> LV = X.getAs<Loc>())
state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
}
@@ -3522,7 +3860,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
Out << ", \"tag\": ";
if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
- Out << '\"' << Tag->getTagDescription() << "\"";
+ Out << '\"' << Tag->getTagDescription() << '\"';
else
Out << "null";
Out << ", \"node_id\": " << OtherNode->getID() <<
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 43e298f3de08..6652c065e04f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -446,10 +447,10 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
// Check if the value being cast does not evaluates to 0.
if (!val.isZeroConstant())
- if (Optional<SVal> V =
+ if (std::optional<SVal> V =
StateMgr.getStoreManager().evalBaseToDerived(val, T)) {
- val = *V;
- Failed = false;
+ val = *V;
+ Failed = false;
}
if (Failed) {
@@ -484,7 +485,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
resultType = getContext().getPointerType(resultType);
if (!val.isConstant()) {
- Optional<SVal> V = getStoreManager().evalBaseToDerived(val, T);
+ std::optional<SVal> V = getStoreManager().evalBaseToDerived(val, T);
val = V ? *V : UnknownVal();
}
@@ -524,7 +525,7 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
}
// Explicitly proceed with default handler for this case cascade.
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
// Various C++ casts that are not handled yet.
case CK_ToUnion:
case CK_MatrixCast:
@@ -823,7 +824,7 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
SVal V;
for (CFGElement CE : llvm::reverse(*SrcBlock)) {
- if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
const Expr *ValEx = cast<Expr>(CS->getStmt());
ValEx = ValEx->IgnoreParens();
@@ -1001,7 +1002,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
}
case UO_Plus:
assert(!U->isGLValue());
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case UO_Deref:
case UO_Extension: {
handleUOExtension(I, U, Bldr);
@@ -1043,16 +1044,15 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
// Note: technically we do "E == 0", but this is the same in the
// transfer functions as "0 == E".
SVal Result;
- if (Optional<Loc> LV = V.getAs<Loc>()) {
- Loc X = svalBuilder.makeNullWithType(Ex->getType());
- Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
+ if (std::optional<Loc> LV = V.getAs<Loc>()) {
+ Loc X = svalBuilder.makeNullWithType(Ex->getType());
+ Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
} else if (Ex->getType()->isFloatingType()) {
- // FIXME: handle floating point types.
- Result = UnknownVal();
+ // FIXME: handle floating point types.
+ Result = UnknownVal();
} else {
- nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
- Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X,
- U->getType());
+ nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
+ Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, U->getType());
}
state = state->BindExpr(U, LCtx, Result);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 04e00274b2a7..6eb37287b136 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -10,15 +10,17 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
-#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/StmtCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/Basic/PrettyStackTrace.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -74,7 +76,7 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
// If the value being copied is not unknown, load from its location to get
// an aggregate rvalue.
- if (Optional<Loc> L = V.getAs<Loc>())
+ if (std::optional<Loc> L = V.getAs<Loc>())
V = Pred->getState()->getSVal(*L);
else
assert(V.isUnknownOrUndef());
@@ -111,9 +113,15 @@ SVal ExprEngine::makeElementRegion(ProgramStateRef State, SVal LValue,
return LValue;
}
+// In case when the prvalue is returned from the function (kind is one of
+// SimpleReturnedValueKind, CXX17ElidedCopyReturnedValueKind), then
+// it's materialization happens in context of the caller.
+// We pass BldrCtx explicitly, as currBldrCtx always refers to callee's context.
SVal ExprEngine::computeObjectUnderConstruction(
- const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
- const ConstructionContext *CC, EvalCallOptions &CallOpts, unsigned Idx) {
+ const Expr *E, ProgramStateRef State, const NodeBuilderContext *BldrCtx,
+ const LocationContext *LCtx, const ConstructionContext *CC,
+ EvalCallOptions &CallOpts, unsigned Idx) {
+
SValBuilder &SVB = getSValBuilder();
MemRegionManager &MRMgr = SVB.getRegionManager();
ASTContext &ACtx = SVB.getContext();
@@ -171,13 +179,14 @@ SVal ExprEngine::computeObjectUnderConstruction(
if (const SubRegion *MR =
dyn_cast_or_null<SubRegion>(V.getAsRegion())) {
if (NE->isArray()) {
- // TODO: In fact, we need to call the constructor for every
- // allocated element, not just the first one!
CallOpts.IsArrayCtorOrDtor = true;
- auto R = MRMgr.getElementRegion(NE->getType()->getPointeeType(),
- svalBuilder.makeArrayIndex(Idx), MR,
- SVB.getContext());
+ auto Ty = NE->getType()->getPointeeType();
+ while (const auto *AT = getContext().getAsArrayType(Ty))
+ Ty = AT->getElementType();
+
+ auto R = MRMgr.getElementRegion(Ty, svalBuilder.makeArrayIndex(Idx),
+ MR, SVB.getContext());
return loc::MemRegionVal(R);
}
@@ -209,8 +218,11 @@ SVal ExprEngine::computeObjectUnderConstruction(
CallerLCtx = CallerLCtx->getParent();
assert(!isa<BlockInvocationContext>(CallerLCtx));
}
+
+ NodeBuilderContext CallerBldrCtx(getCoreEngine(),
+ SFC->getCallSiteBlock(), CallerLCtx);
return computeObjectUnderConstruction(
- cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ cast<Expr>(SFC->getCallSite()), State, &CallerBldrCtx, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
} else {
// We are on the top frame of the analysis. We do not know where is the
@@ -250,7 +262,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
EvalCallOptions PreElideCallOpts = CallOpts;
SVal V = computeObjectUnderConstruction(
- TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructorAfterElision(), State, BldrCtx, LCtx,
TCC->getConstructionContextAfterElision(), CallOpts);
// FIXME: This definition of "copy elision has not failed" is unreliable.
@@ -263,7 +275,7 @@ SVal ExprEngine::computeObjectUnderConstruction(
// a simple temporary.
CallOpts = PreElideCallOpts;
CallOpts.IsElidableCtorThatHasNotBeenElided = true;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
@@ -316,13 +328,13 @@ SVal ExprEngine::computeObjectUnderConstruction(
unsigned Idx = ACC->getIndex();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
+ auto getArgLoc = [&](CallEventRef<> Caller) -> std::optional<SVal> {
const LocationContext *FutureSFC =
- Caller->getCalleeStackFrame(currBldrCtx->blockCount());
+ Caller->getCalleeStackFrame(BldrCtx->blockCount());
// Return early if we are unable to reliably foresee
// the future stack frame.
if (!FutureSFC)
- return None;
+ return std::nullopt;
// This should be equivalent to Caller->getDecl() for now, but
// FutureSFC->getDecl() is likely to support better stuff (like
@@ -331,22 +343,22 @@ SVal ExprEngine::computeObjectUnderConstruction(
// FIXME: Support for variadic arguments is not implemented here yet.
if (CallEvent::isVariadic(CalleeD))
- return None;
+ return std::nullopt;
// Operator arguments do not correspond to operator parameters
// because this-argument is implemented as a normal argument in
// operator call expressions but not in operator declarations.
const TypedValueRegion *TVR = Caller->getParameterLocation(
- *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
+ *Caller->getAdjustedParameterIndex(Idx), BldrCtx->blockCount());
if (!TVR)
- return None;
+ return std::nullopt;
return loc::MemRegionVal(TVR);
};
if (const auto *CE = dyn_cast<CallExpr>(E)) {
CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
- if (Optional<SVal> V = getArgLoc(Caller))
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
@@ -355,13 +367,13 @@ SVal ExprEngine::computeObjectUnderConstruction(
// constructor because we won't need it.
CallEventRef<> Caller =
CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx);
- if (Optional<SVal> V = getArgLoc(Caller))
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
} else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
- if (Optional<SVal> V = getArgLoc(Caller))
+ if (std::optional<SVal> V = getArgLoc(Caller))
return *V;
else
break;
@@ -455,7 +467,7 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
}
// If we decided not to elide the constructor, proceed as if
// it's a simple temporary.
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
@@ -524,16 +536,32 @@ bindRequiredArrayElementToEnvironment(ProgramStateRef State,
// | `-DeclRefExpr
// `-ArrayInitIndexExpr
//
+ // The resulting expression for a multidimensional array.
+ // ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-DeclRefExpr <-- match this
+ // `-ArrayInitLoopExpr
+ // |-OpaqueValueExpr
+ // | `-ArraySubscriptExpr
+ // | |-ImplicitCastExpr
+ // | | `-OpaqueValueExpr
+ // | | `-DeclRefExpr
+ // | `-ArrayInitIndexExpr
+ // `-CXXConstructExpr <-- extract this
+ // ` ...
+
+ const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
+
// HACK: There is no way we can put the index of the array element into the
// CFG unless we unroll the loop, so we manually select and bind the required
// parameter to the environment.
- const auto *CE = cast<CXXConstructExpr>(AILE->getSubExpr());
- const auto *OVESrc = AILE->getCommonExpr()->getSourceExpr();
+ const auto *CE =
+ cast<CXXConstructExpr>(extractElementInitializerFromNestedAILE(AILE));
SVal Base = UnknownVal();
if (const auto *ME = dyn_cast<MemberExpr>(OVESrc))
Base = State->getSVal(ME, LCtx);
- else if (const auto *DRE = cast<DeclRefExpr>(OVESrc))
+ else if (const auto *DRE = dyn_cast<DeclRefExpr>(OVESrc))
Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
else
llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");
@@ -556,18 +584,18 @@ void ExprEngine::handleConstructor(const Expr *E,
SVal Target = UnknownVal();
if (CE) {
- if (Optional<SVal> ElidedTarget =
+ if (std::optional<SVal> ElidedTarget =
getObjectUnderConstruction(State, CE, LCtx)) {
- // We've previously modeled an elidable constructor by pretending that it
- // in fact constructs into the correct target. This constructor can
- // therefore be skipped.
- Target = *ElidedTarget;
- StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
- State = finishObjectConstruction(State, CE, LCtx);
- if (auto L = Target.getAs<Loc>())
- State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
- Bldr.generateNode(CE, Pred, State);
- return;
+ // We've previously modeled an elidable constructor by pretending that
+ // it in fact constructs into the correct target. This constructor can
+ // therefore be skipped.
+ Target = *ElidedTarget;
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ State = finishObjectConstruction(State, CE, LCtx);
+ if (auto L = Target.getAs<Loc>())
+ State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
+ Bldr.generateNode(CE, Pred, State);
+ return;
}
}
@@ -589,6 +617,27 @@ void ExprEngine::handleConstructor(const Expr *E,
unsigned Idx = 0;
if (CE->getType()->isArrayType() || AILE) {
+
+ auto isZeroSizeArray = [&] {
+ uint64_t Size = 1;
+
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType()))
+ Size = getContext().getConstantArrayElementCount(CAT);
+ else if (AILE)
+ Size = getContext().getArrayInitLoopExprElementCount(AILE);
+
+ return Size == 0;
+ };
+
+ // No element construction will happen in a 0 size array.
+ if (isZeroSizeArray()) {
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ static SimpleProgramPointTag T{"ExprEngine",
+ "Skipping 0 size array construction"};
+ Bldr.generateNode(CE, Pred, State, &T);
+ return;
+ }
+
Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u);
State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1);
}
@@ -596,16 +645,17 @@ void ExprEngine::handleConstructor(const Expr *E,
if (AILE) {
// Only set this once even though we loop through it multiple times.
if (!getPendingInitLoop(State, CE, LCtx))
- State = setPendingInitLoop(State, CE, LCtx,
- AILE->getArraySize().getLimitedValue());
+ State = setPendingInitLoop(
+ State, CE, LCtx,
+ getContext().getArrayInitLoopExprElementCount(AILE));
State = bindRequiredArrayElementToEnvironment(
State, AILE, LCtx, svalBuilder.makeArrayIndex(Idx));
}
// The target region is found from construction context.
- std::tie(State, Target) =
- handleConstructionContext(CE, State, LCtx, CC, CallOpts, Idx);
+ std::tie(State, Target) = handleConstructionContext(
+ CE, State, currBldrCtx, LCtx, CC, CallOpts, Idx);
break;
}
case CXXConstructExpr::CK_VirtualBase: {
@@ -620,7 +670,7 @@ void ExprEngine::handleConstructor(const Expr *E,
("This virtual base should have already been initialized by "
"the most derived class!"));
(void)OuterCtor;
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
case CXXConstructExpr::CK_NonVirtualBase:
// In C++17, classes with non-virtual bases may be aggregates, so they would
@@ -640,7 +690,7 @@ void ExprEngine::handleConstructor(const Expr *E,
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case CXXConstructExpr::CK_Delegating: {
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
@@ -905,6 +955,11 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
// skip it for now.
ProgramStateRef State = I->getState();
SVal RetVal = State->getSVal(CNE, LCtx);
+ // [basic.stc.dynamic.allocation] (on the return value of an allocation
+ // function):
+ // "The order, contiguity, and initial value of storage allocated by
+ // successive calls to an allocation function are unspecified."
+ State = State->bindDefaultInitial(RetVal, UndefinedVal{}, LCtx);
// If this allocation function is not declared as non-throwing, failures
// /must/ be signalled by exceptions, and thus the return value will never
@@ -1143,20 +1198,26 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
SVal InitVal;
if (!FieldForCapture->hasCapturedVLAType()) {
- Expr *InitExpr = *i;
-
- if (const auto AILE = dyn_cast<ArrayInitLoopExpr>(InitExpr)) {
- // If the AILE initializes a POD array, we need to keep it as the
- // InitExpr.
- if (dyn_cast<CXXConstructExpr>(AILE->getSubExpr()))
- InitExpr = AILE->getSubExpr();
- }
+ const Expr *InitExpr = *i;
assert(InitExpr && "Capture missing initialization expression");
- if (dyn_cast<CXXConstructExpr>(InitExpr)) {
- InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
- InitVal = State->getSVal(InitVal.getAsRegion());
+ // Capturing a 0 length array is a no-op, so we ignore it to get a more
+ // accurate analysis. If it's not ignored, it would set the default
+ // binding of the lambda to 'Unknown', which can lead to falsely detecting
+ // 'Uninitialized' values as 'Unknown' and not reporting a warning.
+ const auto FTy = FieldForCapture->getType();
+ if (FTy->isConstantArrayType() &&
+ getContext().getConstantArrayElementCount(
+ getContext().getAsConstantArrayType(FTy)) == 0)
+ continue;
+
+ // With C++17 copy elision the InitExpr can be anything, so instead of
+ // pattern matching all cases, we simple check if the current field is
+ // under construction or not, regardless what it's InitExpr is.
+ if (const auto OUC =
+ getObjectUnderConstruction(State, {LE, Idx}, LocCtxt)) {
+ InitVal = State->getSVal(OUC->getAsRegion());
State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
} else
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 8fb2ce9cd18f..54528475cb31 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -25,6 +25,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -85,10 +86,10 @@ static std::pair<const Stmt*,
const ProgramPoint &PP = Node->getLocation();
if (PP.getStackFrame() == SF) {
- if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
+ if (std::optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
S = SP->getStmt();
break;
- } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) {
+ } else if (std::optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) {
S = CEE->getCalleeContext()->getCallSite();
if (S)
break;
@@ -96,17 +97,17 @@ static std::pair<const Stmt*,
// If there is no statement, this is an implicitly-generated call.
// We'll walk backwards over it and then continue the loop to find
// an actual statement.
- Optional<CallEnter> CE;
+ std::optional<CallEnter> CE;
do {
Node = Node->getFirstPred();
CE = Node->getLocationAs<CallEnter>();
} while (!CE || CE->getCalleeContext() != CEE->getCalleeContext());
// Continue searching the graph.
- } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) {
+ } else if (std::optional<BlockEdge> BE = PP.getAs<BlockEdge>()) {
Blk = BE->getSrc();
}
- } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) {
+ } else if (std::optional<CallEnter> CE = PP.getAs<CallEnter>()) {
// If we reached the CallEnter for this function, it has no statements.
if (CE->getCalleeContext() == SF)
break;
@@ -195,6 +196,53 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl();
}
+// Returns the number of elements in the array currently being destructed.
+// If the element count is not found 0 will be returned.
+static unsigned getElementCountOfArrayBeingDestructed(
+ const CallEvent &Call, const ProgramStateRef State, SValBuilder &SVB) {
+ assert(isa<CXXDestructorCall>(Call) &&
+ "The call event is not a destructor call!");
+
+ const auto &DtorCall = cast<CXXDestructorCall>(Call);
+
+ auto ThisVal = DtorCall.getCXXThisVal();
+
+ if (auto ThisElementRegion = dyn_cast<ElementRegion>(ThisVal.getAsRegion())) {
+ auto ArrayRegion = ThisElementRegion->getAsArrayOffset().getRegion();
+ auto ElementType = ThisElementRegion->getElementType();
+
+ auto ElementCount =
+ getDynamicElementCount(State, ArrayRegion, SVB, ElementType);
+
+ if (!ElementCount.isConstant())
+ return 0;
+
+ return ElementCount.getAsInteger()->getLimitedValue();
+ }
+
+ return 0;
+}
+
+ProgramStateRef ExprEngine::removeStateTraitsUsedForArrayEvaluation(
+ ProgramStateRef State, const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+
+ assert(LCtx && "Location context must be provided!");
+
+ if (E) {
+ if (getPendingInitLoop(State, E, LCtx))
+ State = removePendingInitLoop(State, E, LCtx);
+
+ if (getIndexOfElementToConstruct(State, E, LCtx))
+ State = removeIndexOfElementToConstruct(State, E, LCtx);
+ }
+
+ if (getPendingArrayDestruction(State, LCtx))
+ State = removePendingArrayDestruction(State, LCtx);
+
+ return State;
+}
+
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
@@ -234,6 +282,16 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// but we want to evaluate it as many times as many elements the array has.
bool ShouldRepeatCall = false;
+ if (const auto *DtorDecl =
+ dyn_cast_or_null<CXXDestructorDecl>(Call->getDecl())) {
+ if (auto Idx = getPendingArrayDestruction(state, callerCtx)) {
+ ShouldRepeatCall = *Idx > 0;
+
+ auto ThisVal = svalBuilder.getCXXThis(DtorDecl->getParent(), calleeCtx);
+ state = state->killBinding(ThisVal);
+ }
+ }
+
// If the callee returns an expression, bind its value to CallExpr.
if (CE) {
if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
@@ -264,14 +322,6 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
state = state->BindExpr(CCE, callerCtx, ThisV);
ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx);
-
- if (!ShouldRepeatCall) {
- if (getIndexOfElementToConstruct(state, CCE, callerCtx))
- state = removeIndexOfElementToConstruct(state, CCE, callerCtx);
-
- if (getPendingInitLoop(state, CCE, callerCtx))
- state = removePendingInitLoop(state, CCE, callerCtx);
- }
}
if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) {
@@ -290,6 +340,11 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
}
}
+ if (!ShouldRepeatCall) {
+ state = removeStateTraitsUsedForArrayEvaluation(
+ state, dyn_cast_or_null<CXXConstructExpr>(CE), callerCtx);
+ }
+
// Step 3: BindedRetNode -> CleanedNodes
// If we can find a statement and a block in the inlined function, run remove
// dead bindings before returning from the call. This is important to ensure
@@ -337,9 +392,8 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// result onto the work list.
// CEENode -> Dst -> WorkList
NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
- SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx,
- &Ctx);
- SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex());
+ SaveAndRestore<const NodeBuilderContext *> NBCSave(currBldrCtx, &Ctx);
+ SaveAndRestore CBISave(currStmtIdx, calleeCtx->getIndex());
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
@@ -586,8 +640,7 @@ ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State,
const LocationContext *LC = Call.getLocationContext();
for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) {
unsigned I = Call.getASTArgumentIndex(CallI);
- if (Optional<SVal> V =
- getObjectUnderConstruction(State, {E, I}, LC)) {
+ if (std::optional<SVal> V = getObjectUnderConstruction(State, {E, I}, LC)) {
SVal VV = *V;
(void)VV;
assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion())
@@ -720,9 +773,9 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
SVal Target;
assert(RTC->getStmt() == Call.getOriginExpr());
EvalCallOptions CallOpts; // FIXME: We won't really need those.
- std::tie(State, Target) =
- handleConstructionContext(Call.getOriginExpr(), State, LCtx,
- RTC->getConstructionContext(), CallOpts);
+ std::tie(State, Target) = handleConstructionContext(
+ Call.getOriginExpr(), State, currBldrCtx, LCtx,
+ RTC->getConstructionContext(), CallOpts);
const MemRegion *TargetR = Target.getAsRegion();
assert(TargetR);
// Invalidate the region so that it didn't look uninitialized. If this is
@@ -762,6 +815,11 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
svalBuilder.evalBinOp(State, BO_Mul, ElementCount, ElementSize,
svalBuilder.getArrayIndexType());
+ // FIXME: This line is to prevent a crash. For more details please check
+ // issue #56264.
+ if (Size.isUndef())
+ Size = UnknownVal();
+
State = setDynamicExtent(State, MR, Size.castAs<DefinedOrUnknownSVal>(),
svalBuilder);
} else {
@@ -813,11 +871,6 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
!Opts.MayInlineCXXAllocator)
return CIP_DisallowedOnce;
- // FIXME: We don't handle constructors or destructors for arrays properly.
- // Even once we do, we still need to be careful about implicitly-generated
- // initializers for array fields in default move/copy constructors.
- // We still allow construction into ElementRegion targets when they don't
- // represent array elements.
if (CallOpts.IsArrayCtorOrDtor) {
if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC))
return CIP_DisallowedOnce;
@@ -872,9 +925,12 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors");
(void)ADC;
- // FIXME: We don't handle destructors for arrays properly.
- if (CallOpts.IsArrayCtorOrDtor)
- return CIP_DisallowedOnce;
+ if (CallOpts.IsArrayCtorOrDtor) {
+ if (!shouldInlineArrayDestruction(getElementCountOfArrayBeingDestructed(
+ Call, Pred->getState(), svalBuilder))) {
+ return CIP_DisallowedOnce;
+ }
+ }
// Allow disabling temporary destructor inlining with a separate option.
if (CallOpts.IsTemporaryCtorOrDtor &&
@@ -889,7 +945,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
break;
}
case CE_CXXDeallocator:
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case CE_CXXAllocator:
if (Opts.MayInlineCXXAllocator)
break;
@@ -1034,9 +1090,9 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
return false;
// Check if this function has been marked as non-inlinable.
- Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
+ std::optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
if (MayInline) {
- if (!MayInline.value())
+ if (!*MayInline)
return false;
} else {
@@ -1091,22 +1147,36 @@ bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State,
if (!CE)
return false;
- auto Type = CE->getType();
-
// FIXME: Handle other arrays types.
- if (const auto *CAT = dyn_cast<ConstantArrayType>(Type)) {
- unsigned Size = getContext().getConstantArrayElementCount(CAT);
-
- return Size <= AMgr.options.maxBlockVisitOnPath;
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) {
+ unsigned ArrSize = getContext().getConstantArrayElementCount(CAT);
+
+ // This might seem conter-intuitive at first glance, but the functions are
+ // closely related. Reasoning about destructors depends only on the type
+ // of the expression that initialized the memory region, which is the
+ // CXXConstructExpr. So to avoid code repetition, the work is delegated
+ // to the function that reasons about destructor inlining. Also note that
+ // if the constructors of the array elements are inlined, the destructors
+ // can also be inlined and if the destructors can be inline, it's safe to
+ // inline the constructors.
+ return shouldInlineArrayDestruction(ArrSize);
}
// Check if we're inside an ArrayInitLoopExpr, and it's sufficiently small.
if (auto Size = getPendingInitLoop(State, CE, LCtx))
- return *Size <= AMgr.options.maxBlockVisitOnPath;
+ return shouldInlineArrayDestruction(*Size);
return false;
}
+bool ExprEngine::shouldInlineArrayDestruction(uint64_t Size) {
+
+ uint64_t maxAllowedSize = AMgr.options.maxBlockVisitOnPath;
+
+ // Declaring a 0 element array is also possible.
+ return Size <= maxAllowedSize && Size > 0;
+}
+
bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State,
const CXXConstructExpr *E,
const LocationContext *LCtx) {
@@ -1189,7 +1259,12 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
}
}
- // If we can't inline it, handle the return value and invalidate the regions.
+ // If we can't inline it, clean up the state traits used only if the function
+ // is inlined.
+ State = removeStateTraitsUsedForArrayEvaluation(
+ State, dyn_cast_or_null<CXXConstructExpr>(E), Call->getLocationContext());
+
+ // Also handle the return value and invalidate the regions.
conservativeEvalCall(*Call, Bldr, Pred, State);
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 5a55e81497b0..25c36e9aea24 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -206,7 +206,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
ExplodedNodeSet dstPostCheckers;
getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred,
*Msg, *this);
- for (auto I : dstPostCheckers)
+ for (auto *I : dstPostCheckers)
finishArgumentConstruction(Dst, I, *Msg);
return;
}
@@ -270,7 +270,7 @@ void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
// If there were constructors called for object-type arguments, clean them up.
ExplodedNodeSet dstArgCleanup;
- for (auto I : dstEval)
+ for (auto *I : dstEval)
finishArgumentConstruction(dstArgCleanup, I, *Msg);
ExplodedNodeSet dstPostvisit;
diff --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index 506d61d94d5f..a80352816be6 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -24,6 +25,7 @@ using namespace clang::ast_matchers;
static const int MAXIMUM_STEP_UNROLLED = 128;
+namespace {
struct LoopState {
private:
enum Kind { Normal, Unrolled } K;
@@ -56,6 +58,7 @@ public:
ID.AddInteger(maxStep);
}
};
+} // namespace
// The tracked stack of loops. The stack indicates that which loops the
// simulated element contained by. The loops are marked depending if we decided
@@ -175,7 +178,7 @@ static bool isCapturedByReference(ExplodedNode *N, const DeclRefExpr *DR) {
const CXXRecordDecl *LambdaCXXRec = MD->getParent();
// Lookup the fields of the lambda
- llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ llvm::DenseMap<const ValueDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
LambdaCXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
@@ -284,7 +287,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
return true;
ProgramPoint P = N->getLocation();
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
+ if (std::optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
S = BE->getBlock()->getTerminatorStmt();
if (S == LoopStmt)
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 81c11099e93f..0c126a632f74 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -35,7 +35,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
@@ -51,6 +50,7 @@
#include <cstdint>
#include <functional>
#include <iterator>
+#include <optional>
#include <string>
#include <tuple>
#include <utility>
@@ -790,18 +790,30 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
return true;
if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ using FAMKind = LangOptions::StrictFlexArraysLevelKind;
+ const FAMKind StrictFlexArraysLevel =
+ Ctx.getLangOpts().getStrictFlexArraysLevel();
+ const AnalyzerOptions &Opts = SVB.getAnalyzerOptions();
const llvm::APInt &Size = CAT->getSize();
- if (Size.isZero())
- return true;
-
- if (getContext().getLangOpts().StrictFlexArrays >= 2)
- return false;
- const AnalyzerOptions &Opts = SVB.getAnalyzerOptions();
- // FIXME: this option is probably redundant with -fstrict-flex-arrays=1.
- if (Opts.ShouldConsiderSingleElementArraysAsFlexibleArrayMembers &&
- Size.isOne())
+ if (StrictFlexArraysLevel <= FAMKind::ZeroOrIncomplete && Size.isZero())
return true;
+
+ // The "-fstrict-flex-arrays" should have precedence over
+ // consider-single-element-arrays-as-flexible-array-members
+ // analyzer-config when checking single element arrays.
+ if (StrictFlexArraysLevel == FAMKind::Default) {
+ // FIXME: After clang-17 released, we should remove this branch.
+ if (Opts.ShouldConsiderSingleElementArraysAsFlexibleArrayMembers &&
+ Size.isOne())
+ return true;
+ } else {
+ // -fstrict-flex-arrays was specified, since it's not the default, so
+ // ignore analyzer-config.
+ if (StrictFlexArraysLevel <= FAMKind::OneZeroOrIncomplete &&
+ Size.isOne())
+ return true;
+ }
}
return false;
};
@@ -1029,7 +1041,7 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
T = getContext().VoidTy;
if (!T->getAs<FunctionType>()) {
FunctionProtoType::ExtProtoInfo Ext;
- T = getContext().getFunctionType(T, None, Ext);
+ T = getContext().getFunctionType(T, std::nullopt, Ext);
}
T = getContext().getBlockPointerType(T);
@@ -1076,14 +1088,18 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
}
else {
- if (LC) {
+ bool IsArcManagedBlock = Ctx.getLangOpts().ObjCAutoRefCount;
+
+ // ARC managed blocks can be initialized on stack or directly in heap
+ // depending on the implementations. So we initialize them with
+ // UnknownRegion.
+ if (!IsArcManagedBlock && LC) {
// FIXME: Once we implement scope handling, we want the parent region
// to be the scope.
const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
sReg = getStackLocalsRegion(STC);
- }
- else {
+ } else {
// We allow 'LC' to be NULL for cases where want BlockDataRegions
// without context-sensitivity.
sReg = getUnknownRegion();
@@ -1286,8 +1302,8 @@ bool MemRegion::hasGlobalsOrParametersStorage() const {
return isa<StackArgumentsSpaceRegion, GlobalsSpaceRegion>(getMemorySpace());
}
-// getBaseRegion strips away all elements and fields, and get the base region
-// of them.
+// Strips away all elements and fields.
+// Returns the base region of them.
const MemRegion *MemRegion::getBaseRegion() const {
const MemRegion *R = this;
while (true) {
@@ -1307,8 +1323,7 @@ const MemRegion *MemRegion::getBaseRegion() const {
return R;
}
-// getgetMostDerivedObjectRegion gets the region of the root class of a C++
-// class hierarchy.
+// Returns the region of the root class of a C++ class hierarchy.
const MemRegion *MemRegion::getMostDerivedObjectRegion() const {
const MemRegion *R = this;
while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
@@ -1482,7 +1497,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
// If our base region is symbolic, we don't know what type it really is.
// Pretend the type of the symbol is the true dynamic type.
// (This will at least be self-consistent for the life of the symbol.)
- Ty = SR->getSymbol()->getType()->getPointeeType();
+ Ty = SR->getPointeeStaticType();
RootIsSymbolic = true;
}
@@ -1539,7 +1554,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
}
SVal Index = ER->getIndex();
- if (Optional<nonloc::ConcreteInt> CI =
+ if (std::optional<nonloc::ConcreteInt> CI =
Index.getAs<nonloc::ConcreteInt>()) {
// Don't bother calculating precise offsets if we already have a
// symbolic offset somewhere in the chain.
diff --git a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index d35646bfba91..a3b08d4581a5 100644
--- a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -28,6 +28,7 @@
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
#include <memory>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -165,7 +166,7 @@ static void printCoverage(const PathDiagnostic *D,
FIDMap &FM,
llvm::raw_fd_ostream &o);
-static Optional<StringRef> getExpandedMacro(
+static std::optional<StringRef> getExpandedMacro(
SourceLocation MacroLoc, const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions, const SourceManager &SM);
@@ -384,9 +385,9 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
SourceLocation MacroExpansionLoc =
P->getLocation().asLocation().getExpansionLoc();
- const Optional<StringRef> MacroName =
+ const std::optional<StringRef> MacroName =
MacroExpansions.getOriginalText(MacroExpansionLoc);
- const Optional<StringRef> ExpansionText =
+ const std::optional<StringRef> ExpansionText =
getExpandedMacro(MacroExpansionLoc, CTU, MacroExpansions, SM);
if (!MacroName || !ExpansionText)
@@ -407,11 +408,11 @@ void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
// Output the macro name.
Indent(o, indent) << "<key>name</key>";
- EmitString(o, MacroName.value()) << '\n';
+ EmitString(o, *MacroName) << '\n';
// Output what it expands into.
Indent(o, indent) << "<key>expansion</key>";
- EmitString(o, ExpansionText.value()) << '\n';
+ EmitString(o, *ExpansionText) << '\n';
// Finish up.
--indent;
@@ -825,7 +826,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
// Definitions of helper functions and methods for expanding macros.
//===----------------------------------------------------------------------===//
-static Optional<StringRef>
+static std::optional<StringRef>
getExpandedMacro(SourceLocation MacroExpansionLoc,
const cross_tu::CrossTranslationUnitContext &CTU,
const MacroExpansionContext &MacroExpansions,
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index a6d0e242924b..90ebbaad2bf3 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -216,8 +217,6 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
}
ProgramStateRef ProgramState::killBinding(Loc LV) const {
- assert(!isa<loc::MemRegionVal>(LV) && "Use invalidateRegion instead.");
-
Store OldStore = getStore();
const StoreRef &newStore =
getStateManager().StoreMgr->killBinding(OldStore, LV);
@@ -314,7 +313,7 @@ ProgramStateRef ProgramState::BindExpr(const Stmt *S,
return getStateManager().getPersistentState(NewSt);
}
-LLVM_NODISCARD std::pair<ProgramStateRef, ProgramStateRef>
+[[nodiscard]] std::pair<ProgramStateRef, ProgramStateRef>
ProgramState::assumeInBoundDual(DefinedOrUnknownSVal Idx,
DefinedOrUnknownSVal UpperBound,
QualType indexTy) const {
@@ -580,20 +579,20 @@ bool ScanReachableSymbols::scan(const SymExpr *sym) {
}
bool ScanReachableSymbols::scan(SVal val) {
- if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>())
return scan(X->getRegion());
- if (Optional<nonloc::LazyCompoundVal> X =
+ if (std::optional<nonloc::LazyCompoundVal> X =
val.getAs<nonloc::LazyCompoundVal>())
return scan(*X);
- if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>())
+ if (std::optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>())
return scan(X->getLoc());
if (SymbolRef Sym = val.getAsSymbol())
return scan(Sym);
- if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
+ if (std::optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
return scan(*X);
return true;
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 2d4dfae1e750..1017dff2b0f3 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -26,6 +26,7 @@
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <iterator>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -913,20 +914,20 @@ namespace {
class EquivalenceClass : public llvm::FoldingSetNode {
public:
/// Find equivalence class for the given symbol in the given state.
- LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State,
- SymbolRef Sym);
+ [[nodiscard]] static inline EquivalenceClass find(ProgramStateRef State,
+ SymbolRef Sym);
/// Merge classes for the given symbols and return a new state.
- LLVM_NODISCARD static inline ProgramStateRef merge(RangeSet::Factory &F,
- ProgramStateRef State,
- SymbolRef First,
- SymbolRef Second);
+ [[nodiscard]] static inline ProgramStateRef merge(RangeSet::Factory &F,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second);
// Merge this class with the given class and return a new state.
- LLVM_NODISCARD inline ProgramStateRef
+ [[nodiscard]] inline ProgramStateRef
merge(RangeSet::Factory &F, ProgramStateRef State, EquivalenceClass Other);
/// Return a set of class members for the given state.
- LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State) const;
+ [[nodiscard]] inline SymbolSet getClassMembers(ProgramStateRef State) const;
/// Return true if the current class is trivial in the given state.
/// A class is trivial if and only if there is not any member relations stored
@@ -939,43 +940,42 @@ public:
/// members and then during the removal of dead symbols we remove one of its
/// members. In this case, the class is still non-trivial (it still has the
/// mappings in ClassMembers), even though it has only one member.
- LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State) const;
+ [[nodiscard]] inline bool isTrivial(ProgramStateRef State) const;
/// Return true if the current class is trivial and its only member is dead.
- LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
- SymbolReaper &Reaper) const;
+ [[nodiscard]] inline bool isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper) const;
- LLVM_NODISCARD static inline ProgramStateRef
+ [[nodiscard]] static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State, SymbolRef First,
SymbolRef Second);
- LLVM_NODISCARD static inline ProgramStateRef
+ [[nodiscard]] static inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass First, EquivalenceClass Second);
- LLVM_NODISCARD inline ProgramStateRef
+ [[nodiscard]] inline ProgramStateRef
markDisequal(RangeSet::Factory &F, ProgramStateRef State,
EquivalenceClass Other) const;
- LLVM_NODISCARD static inline ClassSet
- getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
- LLVM_NODISCARD inline ClassSet
- getDisequalClasses(ProgramStateRef State) const;
- LLVM_NODISCARD inline ClassSet
+ [[nodiscard]] static inline ClassSet getDisequalClasses(ProgramStateRef State,
+ SymbolRef Sym);
+ [[nodiscard]] inline ClassSet getDisequalClasses(ProgramStateRef State) const;
+ [[nodiscard]] inline ClassSet
getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
- LLVM_NODISCARD static inline Optional<bool> areEqual(ProgramStateRef State,
- EquivalenceClass First,
- EquivalenceClass Second);
- LLVM_NODISCARD static inline Optional<bool>
+ [[nodiscard]] static inline std::optional<bool>
+ areEqual(ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+ [[nodiscard]] static inline std::optional<bool>
areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
/// Remove one member from the class.
- LLVM_NODISCARD ProgramStateRef removeMember(ProgramStateRef State,
- const SymbolRef Old);
+ [[nodiscard]] ProgramStateRef removeMember(ProgramStateRef State,
+ const SymbolRef Old);
/// Iterate over all symbols and try to simplify them.
- LLVM_NODISCARD static inline ProgramStateRef simplify(SValBuilder &SVB,
- RangeSet::Factory &F,
- ProgramStateRef State,
- EquivalenceClass Class);
+ [[nodiscard]] static inline ProgramStateRef simplify(SValBuilder &SVB,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Class);
void dumpToStream(ProgramStateRef State, raw_ostream &os) const;
LLVM_DUMP_METHOD void dump(ProgramStateRef State) const {
@@ -983,10 +983,10 @@ public:
}
/// Check equivalence data for consistency.
- LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
+ [[nodiscard]] LLVM_ATTRIBUTE_UNUSED static bool
isClassDataConsistent(ProgramStateRef State);
- LLVM_NODISCARD QualType getType() const {
+ [[nodiscard]] QualType getType() const {
return getRepresentativeSymbol()->getType();
}
@@ -1041,7 +1041,7 @@ private:
// Constraint functions
//===----------------------------------------------------------------------===//
-LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED bool
+[[nodiscard]] LLVM_ATTRIBUTE_UNUSED bool
areFeasible(ConstraintRangeTy Constraints) {
return llvm::none_of(
Constraints,
@@ -1050,24 +1050,24 @@ areFeasible(ConstraintRangeTy Constraints) {
});
}
-LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
- EquivalenceClass Class) {
+[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State,
+ EquivalenceClass Class) {
return State->get<ConstraintRange>(Class);
}
-LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
- SymbolRef Sym) {
+[[nodiscard]] inline const RangeSet *getConstraint(ProgramStateRef State,
+ SymbolRef Sym) {
return getConstraint(State, EquivalenceClass::find(State, Sym));
}
-LLVM_NODISCARD ProgramStateRef setConstraint(ProgramStateRef State,
- EquivalenceClass Class,
- RangeSet Constraint) {
+[[nodiscard]] ProgramStateRef setConstraint(ProgramStateRef State,
+ EquivalenceClass Class,
+ RangeSet Constraint) {
return State->set<ConstraintRange>(Class, Constraint);
}
-LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State,
- ConstraintRangeTy Constraints) {
+[[nodiscard]] ProgramStateRef setConstraints(ProgramStateRef State,
+ ConstraintRangeTy Constraints) {
return State->set<ConstraintRange>(Constraints);
}
@@ -1084,7 +1084,7 @@ LLVM_NODISCARD ProgramStateRef setConstraints(ProgramStateRef State,
/// \returns true if assuming this Sym to be true means equality of operands
/// false if it means disequality of operands
/// None otherwise
-Optional<bool> meansEquality(const SymSymExpr *Sym) {
+std::optional<bool> meansEquality(const SymSymExpr *Sym) {
switch (Sym->getOpcode()) {
case BO_Sub:
// This case is: A - B != 0 -> disequality check.
@@ -1096,7 +1096,7 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) {
// This case is: A != B != 0 -> diseqiality check.
return false;
default:
- return llvm::None;
+ return std::nullopt;
}
}
@@ -1105,8 +1105,8 @@ Optional<bool> meansEquality(const SymSymExpr *Sym) {
//===----------------------------------------------------------------------===//
template <class SecondTy, class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- SecondTy Second, RestTy... Tail);
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
template <class... RangeTy> struct IntersectionTraits;
@@ -1118,7 +1118,7 @@ template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> {
template <> struct IntersectionTraits<> {
// We ran out of types, and we didn't find any RangeSet, so the result should
// be optional.
- using Type = Optional<RangeSet>;
+ using Type = std::optional<RangeSet>;
};
template <class OptionalOrPointer, class... TailTy>
@@ -1128,32 +1128,33 @@ struct IntersectionTraits<OptionalOrPointer, TailTy...> {
};
template <class EndTy>
-LLVM_NODISCARD inline EndTy intersect(RangeSet::Factory &F, EndTy End) {
- // If the list contains only RangeSet or Optional<RangeSet>, simply return
- // that range set.
+[[nodiscard]] inline EndTy intersect(RangeSet::Factory &F, EndTy End) {
+ // If the list contains only RangeSet or std::optional<RangeSet>, simply
+ // return that range set.
return End;
}
-LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet>
+[[nodiscard]] LLVM_ATTRIBUTE_UNUSED inline std::optional<RangeSet>
intersect(RangeSet::Factory &F, const RangeSet *End) {
- // This is an extraneous conversion from a raw pointer into Optional<RangeSet>
+ // This is an extraneous conversion from a raw pointer into
+ // std::optional<RangeSet>
if (End) {
return *End;
}
- return llvm::None;
+ return std::nullopt;
}
template <class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- RangeSet Second, RestTy... Tail) {
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
// Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version
// of the function and can be sure that the result is RangeSet.
return intersect(F, F.intersect(Head, Second), Tail...);
}
template <class SecondTy, class... RestTy>
-LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
- SecondTy Second, RestTy... Tail) {
+[[nodiscard]] inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
if (Second) {
// Here we call the <RangeSet,RangeSet,...> version of the function...
return intersect(F, Head, *Second, Tail...);
@@ -1165,11 +1166,12 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
/// Main generic intersect function.
/// It intersects all of the given range sets. If some of the given arguments
-/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+/// don't hold a range set (nullptr or std::nullopt), the function will skip
+/// them.
///
/// Available representations for the arguments are:
/// * RangeSet
-/// * Optional<RangeSet>
+/// * std::optional<RangeSet>
/// * RangeSet *
/// Pointer to a RangeSet is automatically assumed to be nullable and will get
/// checked as well as the optional version. If this behaviour is undesired,
@@ -1177,13 +1179,14 @@ LLVM_NODISCARD inline RangeSet intersect(RangeSet::Factory &F, RangeSet Head,
///
/// Return type depends on the arguments' types. If we can be sure in compile
/// time that there will be a range set as a result, the returning type is
-/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>.
+/// simply RangeSet, in other cases we have to back off to
+/// std::optional<RangeSet>.
///
/// Please, prefer optional range sets to raw pointers. If the last argument is
-/// a raw pointer and all previous arguments are None, it will cost one
-/// additional check to convert RangeSet * into Optional<RangeSet>.
+/// a raw pointer and all previous arguments are std::nullopt, it will cost one
+/// additional check to convert RangeSet * into std::optional<RangeSet>.
template <class HeadTy, class SecondTy, class... RestTy>
-LLVM_NODISCARD inline
+[[nodiscard]] inline
typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type
intersect(RangeSet::Factory &F, HeadTy Head, SecondTy Second,
RestTy... Tail) {
@@ -1213,7 +1216,7 @@ public:
}
RangeSet VisitSymExpr(SymbolRef Sym) {
- if (Optional<RangeSet> RS = getRangeForNegatedSym(Sym))
+ if (std::optional<RangeSet> RS = getRangeForNegatedSym(Sym))
return *RS;
// If we've reached this line, the actual type of the symbolic
// expression is not supported for advanced inference.
@@ -1223,7 +1226,7 @@ public:
}
RangeSet VisitUnarySymExpr(const UnarySymExpr *USE) {
- if (Optional<RangeSet> RS = getRangeForNegatedUnarySym(USE))
+ if (std::optional<RangeSet> RS = getRangeForNegatedUnarySym(USE))
return *RS;
return infer(USE->getType());
}
@@ -1333,18 +1336,7 @@ private:
}
RangeSet VisitBinaryOperator(RangeSet LHS, BinaryOperator::Opcode Op,
- RangeSet RHS, QualType T) {
- switch (Op) {
- case BO_Or:
- return VisitBinaryOperator<BO_Or>(LHS, RHS, T);
- case BO_And:
- return VisitBinaryOperator<BO_And>(LHS, RHS, T);
- case BO_Rem:
- return VisitBinaryOperator<BO_Rem>(LHS, RHS, T);
- default:
- return infer(T);
- }
- }
+ RangeSet RHS, QualType T);
//===----------------------------------------------------------------------===//
// Ranges and operators
@@ -1362,11 +1354,11 @@ private:
/// Try to convert given range into the given type.
///
- /// It will return llvm::None only when the trivial conversion is possible.
- llvm::Optional<Range> convert(const Range &Origin, APSIntType To) {
+ /// It will return std::nullopt only when the trivial conversion is possible.
+ std::optional<Range> convert(const Range &Origin, APSIntType To) {
if (To.testInRange(Origin.From(), false) != APSIntType::RTR_Within ||
To.testInRange(Origin.To(), false) != APSIntType::RTR_Within) {
- return llvm::None;
+ return std::nullopt;
}
return Range(ValueFactory.Convert(To, Origin.From()),
ValueFactory.Convert(To, Origin.To()));
@@ -1374,11 +1366,7 @@ private:
template <BinaryOperator::Opcode Op>
RangeSet VisitBinaryOperator(RangeSet LHS, RangeSet RHS, QualType T) {
- // We should propagate information about unfeasbility of one of the
- // operands to the resulting range.
- if (LHS.isEmpty() || RHS.isEmpty()) {
- return RangeFactory.getEmptySet();
- }
+ assert(!LHS.isEmpty() && !RHS.isEmpty());
Range CoarseLHS = fillGaps(LHS);
Range CoarseRHS = fillGaps(RHS);
@@ -1451,21 +1439,21 @@ private:
}
template <typename ProduceNegatedSymFunc>
- Optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F,
- QualType T) {
+ std::optional<RangeSet> getRangeForNegatedExpr(ProduceNegatedSymFunc F,
+ QualType T) {
// Do not negate if the type cannot be meaningfully negated.
if (!T->isUnsignedIntegerOrEnumerationType() &&
!T->isSignedIntegerOrEnumerationType())
- return llvm::None;
+ return std::nullopt;
if (SymbolRef NegatedSym = F())
if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym))
return RangeFactory.negate(*NegatedRange);
- return llvm::None;
+ return std::nullopt;
}
- Optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) {
+ std::optional<RangeSet> getRangeForNegatedUnarySym(const UnarySymExpr *USE) {
// Just get the operand when we negate a symbol that is already negated.
// -(-a) == a
return getRangeForNegatedExpr(
@@ -1477,7 +1465,7 @@ private:
USE->getType());
}
- Optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) {
+ std::optional<RangeSet> getRangeForNegatedSymSym(const SymSymExpr *SSE) {
return getRangeForNegatedExpr(
[SSE, State = this->State]() -> SymbolRef {
if (SSE->getOpcode() == BO_Sub)
@@ -1488,7 +1476,7 @@ private:
SSE->getType());
}
- Optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) {
+ std::optional<RangeSet> getRangeForNegatedSym(SymbolRef Sym) {
return getRangeForNegatedExpr(
[Sym, State = this->State]() {
return State->getSymbolManager().getUnarySymExpr(Sym, UO_Minus,
@@ -1507,12 +1495,12 @@ private:
// It covers all possible combinations (see CmpOpTable description).
// Note that `x` and `y` can also stand for subexpressions,
// not only for actual symbols.
- Optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) {
+ std::optional<RangeSet> getRangeForComparisonSymbol(const SymSymExpr *SSE) {
const BinaryOperatorKind CurrentOP = SSE->getOpcode();
// We currently do not support <=> (C++20).
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
- return llvm::None;
+ return std::nullopt;
static const OperatorRelationsTable CmpOpTable{};
@@ -1582,16 +1570,16 @@ private:
: getFalseRange(T);
}
- return llvm::None;
+ return std::nullopt;
}
- Optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) {
- Optional<bool> Equality = meansEquality(Sym);
+ std::optional<RangeSet> getRangeForEqualities(const SymSymExpr *Sym) {
+ std::optional<bool> Equality = meansEquality(Sym);
if (!Equality)
- return llvm::None;
+ return std::nullopt;
- if (Optional<bool> AreEqual =
+ if (std::optional<bool> AreEqual =
EquivalenceClass::areEqual(State, Sym->getLHS(), Sym->getRHS())) {
// Here we cover two cases at once:
// * if Sym is equality and its operands are known to be equal -> true
@@ -1603,7 +1591,7 @@ private:
return getFalseRange(Sym->getType());
}
- return llvm::None;
+ return std::nullopt;
}
RangeSet getTrueRange(QualType T) {
@@ -1626,6 +1614,57 @@ private:
//===----------------------------------------------------------------------===//
template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_NE>(RangeSet LHS,
+ RangeSet RHS,
+ QualType T) {
+ assert(!LHS.isEmpty() && !RHS.isEmpty());
+
+ if (LHS.getAPSIntType() == RHS.getAPSIntType()) {
+ if (intersect(RangeFactory, LHS, RHS).isEmpty())
+ return getTrueRange(T);
+
+ } else {
+ // We can only lose information if we are casting smaller signed type to
+ // bigger unsigned type. For e.g.,
+ // LHS (unsigned short): [2, USHRT_MAX]
+ // RHS (signed short): [SHRT_MIN, 0]
+ //
+ // Casting RHS to LHS type will leave us with overlapping values
+ // CastedRHS : [0, 0] U [SHRT_MAX + 1, USHRT_MAX]
+ //
+ // We can avoid this by checking if signed type's maximum value is lesser
+ // than unsigned type's minimum value.
+
+ // If both have different signs then only we can get more information.
+ if (LHS.isUnsigned() != RHS.isUnsigned()) {
+ if (LHS.isUnsigned() && (LHS.getBitWidth() >= RHS.getBitWidth())) {
+ if (RHS.getMaxValue().isNegative() ||
+ LHS.getAPSIntType().convert(RHS.getMaxValue()) < LHS.getMinValue())
+ return getTrueRange(T);
+
+ } else if (RHS.isUnsigned() && (LHS.getBitWidth() <= RHS.getBitWidth())) {
+ if (LHS.getMaxValue().isNegative() ||
+ RHS.getAPSIntType().convert(LHS.getMaxValue()) < RHS.getMinValue())
+ return getTrueRange(T);
+ }
+ }
+
+ // Both RangeSets should be casted to bigger unsigned type.
+ APSIntType CastingType(std::max(LHS.getBitWidth(), RHS.getBitWidth()),
+ LHS.isUnsigned() || RHS.isUnsigned());
+
+ RangeSet CastedLHS = RangeFactory.castTo(LHS, CastingType);
+ RangeSet CastedRHS = RangeFactory.castTo(RHS, CastingType);
+
+ if (intersect(RangeFactory, CastedLHS, CastedRHS).isEmpty())
+ return getTrueRange(T);
+ }
+
+ // In all other cases, the resulting range cannot be deduced.
+ return infer(T);
+}
+
+template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
QualType T) {
APSIntType ResultType = ValueFactory.getAPSIntType(T);
@@ -1785,6 +1824,29 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
}
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator(RangeSet LHS,
+ BinaryOperator::Opcode Op,
+ RangeSet RHS, QualType T) {
+ // We should propagate information about unfeasbility of one of the
+ // operands to the resulting range.
+ if (LHS.isEmpty() || RHS.isEmpty()) {
+ return RangeFactory.getEmptySet();
+ }
+
+ switch (Op) {
+ case BO_NE:
+ return VisitBinaryOperator<BO_NE>(LHS, RHS, T);
+ case BO_Or:
+ return VisitBinaryOperator<BO_Or>(LHS, RHS, T);
+ case BO_And:
+ return VisitBinaryOperator<BO_And>(LHS, RHS, T);
+ case BO_Rem:
+ return VisitBinaryOperator<BO_Rem>(LHS, RHS, T);
+ default:
+ return infer(T);
+ }
+}
+
//===----------------------------------------------------------------------===//
// Constraint manager implementation details
//===----------------------------------------------------------------------===//
@@ -1995,7 +2057,7 @@ public:
class ConstraintAssignor : public ConstraintAssignorBase<ConstraintAssignor> {
public:
template <class ClassOrSymbol>
- LLVM_NODISCARD static ProgramStateRef
+ [[nodiscard]] static ProgramStateRef
assign(ProgramStateRef State, SValBuilder &Builder, RangeSet::Factory &F,
ClassOrSymbol CoS, RangeSet NewConstraint) {
if (!State || NewConstraint.isEmpty())
@@ -2037,7 +2099,7 @@ private:
using Base = ConstraintAssignorBase<ConstraintAssignor>;
/// Base method for handling new constraints for symbols.
- LLVM_NODISCARD ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) {
+ [[nodiscard]] ProgramStateRef assign(SymbolRef Sym, RangeSet NewConstraint) {
// All constraints are actually associated with equivalence classes, and
// that's what we are going to do first.
State = assign(EquivalenceClass::find(State, Sym), NewConstraint);
@@ -2051,8 +2113,8 @@ private:
}
/// Base method for handling new constraints for classes.
- LLVM_NODISCARD ProgramStateRef assign(EquivalenceClass Class,
- RangeSet NewConstraint) {
+ [[nodiscard]] ProgramStateRef assign(EquivalenceClass Class,
+ RangeSet NewConstraint) {
// There is a chance that we might need to update constraints for the
// classes that are known to be disequal to Class.
//
@@ -2098,7 +2160,7 @@ private:
return EquivalenceClass::merge(RangeFactory, State, LHS, RHS);
}
- LLVM_NODISCARD Optional<bool> interpreteAsBool(RangeSet Constraint) {
+ [[nodiscard]] std::optional<bool> interpreteAsBool(RangeSet Constraint) {
assert(!Constraint.isEmpty() && "Empty ranges shouldn't get here");
if (Constraint.getConcreteValue())
@@ -2107,7 +2169,7 @@ private:
if (!Constraint.containsZero())
return true;
- return llvm::None;
+ return std::nullopt;
}
ProgramStateRef State;
@@ -2115,7 +2177,6 @@ private:
RangeSet::Factory &RangeFactory;
};
-
bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
const llvm::APSInt &Constraint) {
llvm::SmallSet<EquivalenceClass, 4> SimplifiedClasses;
@@ -2162,12 +2223,12 @@ bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym,
if (!handleRemainderOp(Sym, Constraint))
return false;
- Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
+ std::optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
if (!ConstraintAsBool)
return true;
- if (Optional<bool> Equality = meansEquality(Sym)) {
+ if (std::optional<bool> Equality = meansEquality(Sym)) {
// Here we cover two cases:
// * if Sym is equality and the new constraint is true -> Sym's operands
// should be marked as equal
@@ -2305,7 +2366,7 @@ EquivalenceClass::mergeImpl(RangeSet::Factory &RangeFactory,
//
// Intersection here makes perfect sense because both of these constraints
// must hold for the whole new class.
- if (Optional<RangeSet> NewClassConstraint =
+ if (std::optional<RangeSet> NewClassConstraint =
intersect(RangeFactory, getConstraint(State, *this),
getConstraint(State, Other))) {
// NOTE: Essentially, NewClassConstraint should NEVER be infeasible because
@@ -2503,16 +2564,16 @@ inline bool EquivalenceClass::addToDisequalityInfo(
return true;
}
-inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
- SymbolRef FirstSym,
- SymbolRef SecondSym) {
+inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ SymbolRef FirstSym,
+ SymbolRef SecondSym) {
return EquivalenceClass::areEqual(State, find(State, FirstSym),
find(State, SecondSym));
}
-inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
- EquivalenceClass First,
- EquivalenceClass Second) {
+inline std::optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ EquivalenceClass First,
+ EquivalenceClass Second) {
// The same equivalence class => symbols are equal.
if (First == Second)
return true;
@@ -2524,10 +2585,10 @@ inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
return false;
// It is not clear.
- return llvm::None;
+ return std::nullopt;
}
-LLVM_NODISCARD ProgramStateRef
+[[nodiscard]] ProgramStateRef
EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) {
SymbolSet ClsMembers = getClassMembers(State);
@@ -2556,9 +2617,8 @@ EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) {
}
// Re-evaluate an SVal with top-level `State->assume` logic.
-LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State,
- const RangeSet *Constraint,
- SVal TheValue) {
+[[nodiscard]] ProgramStateRef
+reAssume(ProgramStateRef State, const RangeSet *Constraint, SVal TheValue) {
if (!Constraint)
return State;
@@ -2587,7 +2647,7 @@ LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State,
// class to this class. This way, we simplify not just the symbols but the
// classes as well: we strive to keep the number of the classes to be the
// absolute minimum.
-LLVM_NODISCARD ProgramStateRef
+[[nodiscard]] ProgramStateRef
EquivalenceClass::simplify(SValBuilder &SVB, RangeSet::Factory &F,
ProgramStateRef State, EquivalenceClass Class) {
SymbolSet ClassMembers = Class.getClassMembers(State);
@@ -2717,7 +2777,7 @@ bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
//===----------------------------------------------------------------------===//
bool RangeConstraintManager::canReasonAbout(SVal X) const {
- Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ std::optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
const SymExpr *SE = SymVal->getSymbol();
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index d8ece9f39a25..46948c12617c 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -28,8 +28,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "llvm/ADT/ImmutableMap.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
+#include <optional>
#include <utility>
using namespace clang;
@@ -212,11 +212,11 @@ public:
removeBinding(R, BindingKey::Default);
}
- Optional<SVal> getDirectBinding(const MemRegion *R) const;
+ std::optional<SVal> getDirectBinding(const MemRegion *R) const;
/// getDefaultBinding - Returns an SVal* representing an optional default
/// binding associated with a region and its subregions.
- Optional<SVal> getDefaultBinding(const MemRegion *R) const;
+ std::optional<SVal> getDefaultBinding(const MemRegion *R) const;
/// Return the internal tree as a Store.
Store asStore() const {
@@ -262,12 +262,16 @@ public:
typedef const RegionBindingsRef& RegionBindingsConstRef;
-Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
- return Optional<SVal>::create(lookup(R, BindingKey::Direct));
+std::optional<SVal>
+RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
+ const SVal *V = lookup(R, BindingKey::Direct);
+ return V ? std::optional<SVal>(*V) : std::nullopt;
}
-Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
- return Optional<SVal>::create(lookup(R, BindingKey::Default));
+std::optional<SVal>
+RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
+ const SVal *V = lookup(R, BindingKey::Default);
+ return V ? std::optional<SVal>(*V) : std::nullopt;
}
RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
@@ -421,10 +425,10 @@ public:
RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
const SubRegion *R);
- Optional<SVal>
+ std::optional<SVal>
getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
const ElementRegion *R);
- Optional<SVal>
+ std::optional<SVal>
getSValFromInitListExpr(const InitListExpr *ILE,
const SmallVector<uint64_t, 2> &ConcreteOffsets,
QualType ElemT);
@@ -483,12 +487,11 @@ public: // Part of public interface to class.
/// than using a Default binding at the base of the entire region. This is a
/// heuristic attempting to avoid building long chains of LazyCompoundVals.
///
- /// \returns The updated store bindings, or \c None if binding non-lazily
- /// would be too expensive.
- Optional<RegionBindingsRef> tryBindSmallStruct(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- const RecordDecl *RD,
- nonloc::LazyCompoundVal LCV);
+ /// \returns The updated store bindings, or \c std::nullopt if binding
+ /// non-lazily would be too expensive.
+ std::optional<RegionBindingsRef>
+ tryBindSmallStruct(RegionBindingsConstRef B, const TypedValueRegion *R,
+ const RecordDecl *RD, nonloc::LazyCompoundVal LCV);
/// BindStruct - Bind a compound value to a structure.
RegionBindingsRef bindStruct(RegionBindingsConstRef B,
@@ -498,10 +501,9 @@ public: // Part of public interface to class.
RegionBindingsRef bindVector(RegionBindingsConstRef B,
const TypedValueRegion* R, SVal V);
- Optional<RegionBindingsRef> tryBindSmallArray(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- const ArrayType *AT,
- nonloc::LazyCompoundVal LCV);
+ std::optional<RegionBindingsRef>
+ tryBindSmallArray(RegionBindingsConstRef B, const TypedValueRegion *R,
+ const ArrayType *AT, nonloc::LazyCompoundVal LCV);
RegionBindingsRef bindArray(RegionBindingsConstRef B,
const TypedValueRegion* R,
@@ -548,7 +550,7 @@ public: // Part of public interface to class.
return getBinding(getRegionBindings(S), L, T);
}
- Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
+ std::optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
RegionBindingsRef B = getRegionBindings(S);
// Default bindings are always applied over a base region so look up the
// base region's default binding, otherwise the lookup will fail when R
@@ -589,10 +591,10 @@ public: // Part of public interface to class.
///
/// Note that callers may need to specially handle LazyCompoundVals, which
/// are returned as is in case the caller needs to treat them differently.
- Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty);
+ std::optional<SVal>
+ getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *superR,
+ const TypedValueRegion *R, QualType Ty);
/// Get the state and region whose binding this region \p R corresponds to.
///
@@ -608,6 +610,10 @@ public: // Part of public interface to class.
/// The precise value of "interesting" is determined for the purposes of
/// RegionStore's internal analysis. It must always contain all regions and
/// symbols, but may omit constants and other kinds of SVal.
+ ///
+ /// In contrast to compound values, LazyCompoundVals are also added
+ /// to the 'interesting values' list in addition to the child interesting
+ /// values.
const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV);
//===------------------------------------------------------------------===//
@@ -857,7 +863,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// Find the length (in bits) of the region being invalidated.
uint64_t Length = UINT64_MAX;
SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB);
- if (Optional<nonloc::ConcreteInt> ExtentCI =
+ if (std::optional<nonloc::ConcreteInt> ExtentCI =
Extent.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt &ExtentInt = ExtentCI->getValue();
assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
@@ -1029,15 +1035,14 @@ void InvalidateRegionsWorker::VisitBinding(SVal V) {
}
// Is it a LazyCompoundVal? All references get invalidated as well.
- if (Optional<nonloc::LazyCompoundVal> LCS =
+ if (std::optional<nonloc::LazyCompoundVal> LCS =
V.getAs<nonloc::LazyCompoundVal>()) {
- const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
-
- for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end();
- I != E; ++I)
- VisitBinding(*I);
+ // `getInterestingValues()` returns SVals contained within LazyCompoundVals,
+ // so there is no need to visit them.
+ for (SVal V : RM.getInterestingValues(*LCS))
+ if (!isa<nonloc::LazyCompoundVal>(V))
+ VisitBinding(V);
return;
}
@@ -1103,7 +1108,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
// a pointer value, but the thing pointed by that pointer may
// get invalidated.
SVal V = RM.getBinding(B, loc::MemRegionVal(VR));
- if (Optional<Loc> L = V.getAs<Loc>()) {
+ if (std::optional<Loc> L = V.getAs<Loc>()) {
if (const MemRegion *LR = L->getAsRegion())
AddToWorkList(LR);
}
@@ -1163,7 +1168,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
if (doNotInvalidateSuperRegion) {
// We are not doing blank invalidation of the whole array region so we
// have to manually invalidate each elements.
- Optional<uint64_t> NumElements;
+ std::optional<uint64_t> NumElements;
// Compute lower and upper offsets for region within array.
if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT))
@@ -1198,8 +1203,8 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E;
++I) {
const BindingKey &BK = I.getKey();
- Optional<uint64_t> ROffset =
- BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset();
+ std::optional<uint64_t> ROffset =
+ BK.hasSymbolicOffset() ? std::optional<uint64_t>() : BK.getOffset();
// Check offset is not symbolic and within array's boundaries.
// Handles arrays of 0 elements and of 0-sized elements as well.
@@ -1287,18 +1292,13 @@ void RegionStoreManager::populateWorkList(InvalidateRegionsWorker &W,
for (ArrayRef<SVal>::iterator I = Values.begin(),
E = Values.end(); I != E; ++I) {
SVal V = *I;
- if (Optional<nonloc::LazyCompoundVal> LCS =
- V.getAs<nonloc::LazyCompoundVal>()) {
-
- const SValListTy &Vals = getInterestingValues(*LCS);
+ if (std::optional<nonloc::LazyCompoundVal> LCS =
+ V.getAs<nonloc::LazyCompoundVal>()) {
- for (SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end(); I != E; ++I) {
- // Note: the last argument is false here because these are
- // non-top-level regions.
- if (const MemRegion *R = (*I).getAsRegion())
+ for (SVal S : getInterestingValues(*LCS))
+ if (const MemRegion *R = S.getAsRegion())
W.AddToWorkList(R);
- }
+
continue;
}
@@ -1354,11 +1354,11 @@ RegionStoreManager::invalidateRegions(Store store,
case GFK_All:
B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind,
Ex, Count, LCtx, B, Invalidated);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case GFK_SystemOnly:
B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind,
Ex, Count, LCtx, B, Invalidated);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case GFK_None:
break;
}
@@ -1416,19 +1416,20 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
return UnknownVal();
}
- if (!isa<TypedValueRegion>(MR)) {
- if (T.isNull()) {
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR))
- T = TR->getLocationType()->getPointeeType();
- else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
- T = SR->getSymbol()->getType()->getPointeeType();
- }
- assert(!T.isNull() && "Unable to auto-detect binding type!");
- assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
- MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
- } else {
- T = cast<TypedValueRegion>(MR)->getValueType();
+ // Auto-detect the binding type.
+ if (T.isNull()) {
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(MR))
+ T = TVR->getValueType();
+ else if (const auto *TR = dyn_cast<TypedRegion>(MR))
+ T = TR->getLocationType()->getPointeeType();
+ else if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ T = SR->getPointeeStaticType();
}
+ assert(!T.isNull() && "Unable to auto-detect binding type!");
+ assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
+
+ if (!isa<TypedValueRegion>(MR))
+ MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
// instead of 'Loc', and have the other Loc cases handled at a higher level.
@@ -1537,16 +1538,17 @@ static QualType getUnderlyingType(const SubRegion *R) {
///
/// Note that unlike RegionStoreManager::findLazyBinding, this will not search
/// for lazy bindings for super-regions of \p R.
-static Optional<nonloc::LazyCompoundVal>
+static std::optional<nonloc::LazyCompoundVal>
getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
const SubRegion *R, bool AllowSubregionBindings) {
- Optional<SVal> V = B.getDefaultBinding(R);
+ std::optional<SVal> V = B.getDefaultBinding(R);
if (!V)
- return None;
+ return std::nullopt;
- Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>();
+ std::optional<nonloc::LazyCompoundVal> LCV =
+ V->getAs<nonloc::LazyCompoundVal>();
if (!LCV)
- return None;
+ return std::nullopt;
// If the LCV is for a subregion, the types might not match, and we shouldn't
// reuse the binding.
@@ -1555,7 +1557,7 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
!RegionTy->isVoidPointerType()) {
QualType SourceRegionTy = LCV->getRegion()->getValueType();
if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy))
- return None;
+ return std::nullopt;
}
if (!AllowSubregionBindings) {
@@ -1565,20 +1567,19 @@ getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R,
/*IncludeAllDefaultBindings=*/true);
if (Bindings.size() > 1)
- return None;
+ return std::nullopt;
}
return *LCV;
}
-
std::pair<Store, const SubRegion *>
RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
const SubRegion *R,
const SubRegion *originalRegion) {
if (originalRegion != R) {
- if (Optional<nonloc::LazyCompoundVal> V =
- getExistingLazyBinding(svalBuilder, B, R, true))
+ if (std::optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, true))
return std::make_pair(V->getStore(), V->getRegion());
}
@@ -1666,7 +1667,7 @@ getElementRegionOffsetsWithBase(const ElementRegion *ER) {
/// \param ArrayExtents [in] The array of extents.
/// \param DstOffsets [out] The array of offsets of type `uint64_t`.
/// \returns:
-/// - `None` for successful convertion.
+/// - `std::nullopt` for successful convertion.
/// - `UndefinedVal` or `UnknownVal` otherwise. It's expected that this SVal
/// will be returned as a suitable value of the access operation.
/// which should be returned as a correct
@@ -1675,10 +1676,10 @@ getElementRegionOffsetsWithBase(const ElementRegion *ER) {
/// const int arr[10][20][30] = {}; // ArrayExtents { 10, 20, 30 }
/// int x1 = arr[4][5][6]; // SrcOffsets { NonLoc(6), NonLoc(5), NonLoc(4) }
/// // DstOffsets { 4, 5, 6 }
-/// // returns None
+/// // returns std::nullopt
/// int x2 = arr[42][5][-6]; // returns UndefinedVal
/// int x3 = arr[4][5][x2]; // returns UnknownVal
-static Optional<SVal>
+static std::optional<SVal>
convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets,
const SmallVector<uint64_t, 2> ArrayExtents,
SmallVector<uint64_t, 2> &DstOffsets) {
@@ -1718,10 +1719,10 @@ convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets,
// account.
return UnknownVal();
}
- return None;
+ return std::nullopt;
}
-Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
+std::optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
RegionBindingsConstRef B, const ElementRegion *R) {
assert(R && "ElementRegion should not be null");
@@ -1731,7 +1732,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
std::tie(SValOffsets, Base) = getElementRegionOffsetsWithBase(R);
const VarRegion *VR = dyn_cast<VarRegion>(Base);
if (!VR)
- return None;
+ return std::nullopt;
assert(!SValOffsets.empty() && "getElementRegionOffsets guarantees the "
"offsets vector is not empty.");
@@ -1742,7 +1743,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
if (!VD->getType().isConstQualified() &&
!R->getElementType().isConstQualified() &&
(!B.isMainAnalysis() || !VD->hasGlobalStorage()))
- return None;
+ return std::nullopt;
// Array's declaration should have `ConstantArrayType` type, because only this
// type contains an array extent. It may happen that array type can be of
@@ -1757,13 +1758,13 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
// NOTE: If `Init` is non-null, then a new `VD` is non-null for sure. So check
// `Init` for null only and don't worry about the replaced `VD`.
if (!Init)
- return None;
+ return std::nullopt;
// Array's declaration should have ConstantArrayType type, because only this
// type contains an array extent.
const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType());
if (!CAT)
- return None;
+ return std::nullopt;
// Get array extents.
SmallVector<uint64_t, 2> Extents = getConstantArrayExtents(CAT);
@@ -1775,11 +1776,11 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
// auto x = ptr[4][2]; // UB
// FIXME: Should return UndefinedVal.
if (SValOffsets.size() != Extents.size())
- return None;
+ return std::nullopt;
SmallVector<uint64_t, 2> ConcreteOffsets;
- if (Optional<SVal> V = convertOffsetsFromSvalToUnsigneds(SValOffsets, Extents,
- ConcreteOffsets))
+ if (std::optional<SVal> V = convertOffsetsFromSvalToUnsigneds(
+ SValOffsets, Extents, ConcreteOffsets))
return *V;
// Handle InitListExpr.
@@ -1797,7 +1798,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
// FIXME: Handle CompoundLiteralExpr.
- return None;
+ return std::nullopt;
}
/// Returns an SVal, if possible, for the specified position of an
@@ -1817,7 +1818,7 @@ Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
/// NOTE: Inorder to get a valid SVal, a caller shall guarantee valid offsets
/// for the given initialization list. Otherwise SVal can be an equivalent to 0
/// or lead to assertion.
-Optional<SVal> RegionStoreManager::getSValFromInitListExpr(
+std::optional<SVal> RegionStoreManager::getSValFromInitListExpr(
const InitListExpr *ILE, const SmallVector<uint64_t, 2> &Offsets,
QualType ElemT) {
assert(ILE && "InitListExpr should not be null");
@@ -1888,7 +1889,7 @@ SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL,
return svalBuilder.makeIntVal(Code, ElemT);
}
-static Optional<SVal> getDerivedSymbolForBinding(
+static std::optional<SVal> getDerivedSymbolForBinding(
RegionBindingsConstRef B, const TypedValueRegion *BaseRegion,
const TypedValueRegion *SubReg, const ASTContext &Ctx, SValBuilder &SVB) {
assert(BaseRegion);
@@ -1896,7 +1897,8 @@ static Optional<SVal> getDerivedSymbolForBinding(
QualType Ty = SubReg->getValueType();
if (BaseTy->isScalarType() && Ty->isScalarType()) {
if (Ctx.getTypeSizeInChars(BaseTy) >= Ctx.getTypeSizeInChars(Ty)) {
- if (const Optional<SVal> &ParentValue = B.getDirectBinding(BaseRegion)) {
+ if (const std::optional<SVal> &ParentValue =
+ B.getDirectBinding(BaseRegion)) {
if (SymbolRef ParentValueAsSym = ParentValue->getAsSymbol())
return SVB.getDerivedRegionValueSymbolVal(ParentValueAsSym, SubReg);
@@ -1909,13 +1911,13 @@ static Optional<SVal> getDerivedSymbolForBinding(
}
}
}
- return None;
+ return std::nullopt;
}
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
const MemRegion* superR = R->getSuperRegion();
@@ -1936,7 +1938,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
return getSValFromStringLiteral(SL, Idx.getZExtValue(), T);
}
} else if (isa<ElementRegion, VarRegion>(superR)) {
- if (Optional<SVal> V = getConstantValFromConstArrayInitializer(B, R))
+ if (std::optional<SVal> V = getConstantValFromConstArrayInitializer(B, R))
return *V;
}
@@ -1967,7 +1969,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
const FieldRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
// If the containing record was initialized, try to get its constant value.
@@ -1987,7 +1989,7 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
if (Index < InitList->getNumInits()) {
if (const Expr *FieldInit = InitList->getInit(Index))
- if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
return *V;
} else {
return svalBuilder.makeZeroVal(Ty);
@@ -2018,13 +2020,11 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
return getBindingForFieldOrElementCommon(B, R, Ty);
}
-Optional<SVal>
-RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
- const MemRegion *superR,
- const TypedValueRegion *R,
- QualType Ty) {
+std::optional<SVal> RegionStoreManager::getBindingForDerivedDefaultValue(
+ RegionBindingsConstRef B, const MemRegion *superR,
+ const TypedValueRegion *R, QualType Ty) {
- if (const Optional<SVal> &D = B.getDefaultBinding(superR)) {
+ if (const std::optional<SVal> &D = B.getDefaultBinding(superR)) {
const SVal &val = *D;
if (SymbolRef parentSym = val.getAsSymbol())
return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
@@ -2043,7 +2043,7 @@ RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
llvm_unreachable("Unknown default value");
}
- return None;
+ return std::nullopt;
}
SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion,
@@ -2114,7 +2114,8 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
const SubRegion *SR = R;
while (SR) {
const MemRegion *Base = SR->getSuperRegion();
- if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
+ if (std::optional<SVal> D =
+ getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
if (D->getAs<nonloc::LazyCompoundVal>()) {
hasPartialLazyBinding = true;
break;
@@ -2156,7 +2157,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
// Try to get direct binding if all other attempts failed thus far.
// Else, return UndefinedVal()
if (!hasPartialLazyBinding && !isa<BlockDataRegion>(R->getBaseRegion())) {
- if (const Optional<SVal> &V = B.getDefaultBinding(R))
+ if (const std::optional<SVal> &V = B.getDefaultBinding(R))
return *V;
return UndefinedVal();
}
@@ -2169,13 +2170,13 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B,
const ObjCIvarRegion* R) {
// Check if the region has a binding.
- if (const Optional<SVal> &V = B.getDirectBinding(R))
+ if (const std::optional<SVal> &V = B.getDirectBinding(R))
return *V;
const MemRegion *superR = R->getSuperRegion();
// Check if the super region has a default binding.
- if (const Optional<SVal> &V = B.getDefaultBinding(superR)) {
+ if (const std::optional<SVal> &V = B.getDefaultBinding(superR)) {
if (SymbolRef parentSym = V->getAsSymbol())
return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
@@ -2190,10 +2191,10 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
const VarRegion *R) {
// Check if the region has a binding.
- if (Optional<SVal> V = B.getDirectBinding(R))
+ if (std::optional<SVal> V = B.getDirectBinding(R))
return *V;
- if (Optional<SVal> V = B.getDefaultBinding(R))
+ if (std::optional<SVal> V = B.getDefaultBinding(R))
return *V;
// Lazily derive a value for the VarRegion.
@@ -2207,7 +2208,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
// Is 'VD' declared constant? If so, retrieve the constant value.
if (VD->getType().isConstQualified()) {
if (const Expr *Init = VD->getAnyInitializer()) {
- if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(Init))
return *V;
// If the variable is const qualified and has an initializer but
@@ -2228,7 +2229,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
// If we're in main(), then global initializers have not become stale yet.
if (B.isMainAnalysis())
if (const Expr *Init = VD->getAnyInitializer())
- if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ if (std::optional<SVal> V = svalBuilder.getConstantVal(Init))
return *V;
// Function-scoped static variables are default-initialized to 0; if they
@@ -2238,7 +2239,7 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
if (isa<StaticGlobalSpaceRegion>(MS))
return svalBuilder.makeZeroVal(T);
- if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) {
+ if (std::optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) {
assert(!V->getAs<nonloc::LazyCompoundVal>());
return *V;
}
@@ -2283,11 +2284,9 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
if (V.isUnknownOrUndef() || V.isConstant())
continue;
- if (Optional<nonloc::LazyCompoundVal> InnerLCV =
- V.getAs<nonloc::LazyCompoundVal>()) {
+ if (auto InnerLCV = V.getAs<nonloc::LazyCompoundVal>()) {
const SValListTy &InnerList = getInterestingValues(*InnerLCV);
List.insert(List.end(), InnerList.begin(), InnerList.end());
- continue;
}
List.push_back(V);
@@ -2298,8 +2297,8 @@ RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B,
const TypedValueRegion *R) {
- if (Optional<nonloc::LazyCompoundVal> V =
- getExistingLazyBinding(svalBuilder, B, R, false))
+ if (std::optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, false))
return *V;
return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R);
@@ -2359,7 +2358,7 @@ bool RegionStoreManager::includedInBindings(Store store,
//===----------------------------------------------------------------------===//
StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
- if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
if (const MemRegion* R = LV->getRegion())
return StoreRef(getRegionBindings(ST).removeBinding(R)
.asImmutableMap()
@@ -2390,22 +2389,21 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) {
return bindAggregate(B, TR, V);
}
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
- // Binding directly to a symbolic region should be treated as binding
- // to element 0.
- QualType T = SR->getSymbol()->getType();
- if (T->isAnyPointerType() || T->isReferenceType())
- T = T->getPointeeType();
-
- R = GetElementZeroRegion(SR, T);
- }
+ // Binding directly to a symbolic region should be treated as binding
+ // to element 0.
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ R = GetElementZeroRegion(SR, SR->getPointeeStaticType());
assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) &&
"'this' pointer is not an l-value and is not assignable");
// Clear out bindings that may overlap with this binding.
RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
- return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V);
+
+ // LazyCompoundVals should be always bound as 'default' bindings.
+ auto KeyKind = isa<nonloc::LazyCompoundVal>(V) ? BindingKey::Default
+ : BindingKey::Direct;
+ return NewB.addBinding(BindingKey::Make(R, KeyKind), V);
}
RegionBindingsRef
@@ -2435,7 +2433,7 @@ RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
return B.addBinding(R, BindingKey::Default, V);
}
-Optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
+std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
RegionBindingsConstRef B, const TypedValueRegion *R, const ArrayType *AT,
nonloc::LazyCompoundVal LCV) {
@@ -2443,16 +2441,16 @@ Optional<RegionBindingsRef> RegionStoreManager::tryBindSmallArray(
// If we don't know the size, create a lazyCompoundVal instead.
if (!CAT)
- return None;
+ return std::nullopt;
QualType Ty = CAT->getElementType();
if (!(Ty->isScalarType() || Ty->isReferenceType()))
- return None;
+ return std::nullopt;
// If the array is too big, create a LCV instead.
uint64_t ArrSize = CAT->getSize().getLimitedValue();
if (ArrSize > SmallArrayLimit)
- return None;
+ return std::nullopt;
RegionBindingsRef NewB = B;
@@ -2476,7 +2474,7 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType()));
QualType ElementTy = AT->getElementType();
- Optional<uint64_t> Size;
+ std::optional<uint64_t> Size;
if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT))
Size = CAT->getSize().getZExtValue();
@@ -2484,15 +2482,16 @@ RegionStoreManager::bindArray(RegionBindingsConstRef B,
// Check if the init expr is a literal. If so, bind the rvalue instead.
// FIXME: It's not responsibility of the Store to transform this lvalue
// to rvalue. ExprEngine or maybe even CFG should do this before binding.
- if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) {
SVal V = getBinding(B.asStore(), *MRV, R->getValueType());
return bindAggregate(B, R, V);
}
// Handle lazy compound values.
- if (Optional<nonloc::LazyCompoundVal> LCV =
+ if (std::optional<nonloc::LazyCompoundVal> LCV =
Init.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<RegionBindingsRef> NewB = tryBindSmallArray(B, R, AT, *LCV))
+ if (std::optional<RegionBindingsRef> NewB =
+ tryBindSmallArray(B, R, AT, *LCV))
return *NewB;
return bindAggregate(B, R, Init);
@@ -2573,16 +2572,14 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
return NewB;
}
-Optional<RegionBindingsRef>
-RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
- const TypedValueRegion *R,
- const RecordDecl *RD,
- nonloc::LazyCompoundVal LCV) {
+std::optional<RegionBindingsRef> RegionStoreManager::tryBindSmallStruct(
+ RegionBindingsConstRef B, const TypedValueRegion *R, const RecordDecl *RD,
+ nonloc::LazyCompoundVal LCV) {
FieldVector Fields;
if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD))
if (Class->getNumBases() != 0 || Class->getNumVBases() != 0)
- return None;
+ return std::nullopt;
for (const auto *FD : RD->fields()) {
if (FD->isUnnamedBitfield())
@@ -2591,11 +2588,17 @@ RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
// If there are too many fields, or if any of the fields are aggregates,
// just use the LCV as a default binding.
if (Fields.size() == SmallStructLimit)
- return None;
+ return std::nullopt;
QualType Ty = FD->getType();
+
+ // Zero length arrays are basically no-ops, so we also ignore them here.
+ if (Ty->isConstantArrayType() &&
+ Ctx.getConstantArrayElementCount(Ctx.getAsConstantArrayType(Ty)) == 0)
+ continue;
+
if (!(Ty->isScalarType() || Ty->isReferenceType()))
- return None;
+ return std::nullopt;
Fields.push_back(FD);
}
@@ -2626,9 +2629,10 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
return B;
// Handle lazy compound values and symbolic values.
- if (Optional<nonloc::LazyCompoundVal> LCV =
- V.getAs<nonloc::LazyCompoundVal>()) {
- if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV))
+ if (std::optional<nonloc::LazyCompoundVal> LCV =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+ if (std::optional<RegionBindingsRef> NewB =
+ tryBindSmallStruct(B, R, RD, *LCV))
return *NewB;
return bindAggregate(B, R, V);
}
@@ -2830,16 +2834,17 @@ void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR,
}
void RemoveDeadBindingsWorker::VisitBinding(SVal V) {
- // Is it a LazyCompoundVal? All referenced regions are live as well.
- if (Optional<nonloc::LazyCompoundVal> LCS =
- V.getAs<nonloc::LazyCompoundVal>()) {
-
- const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
-
- for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end();
- I != E; ++I)
- VisitBinding(*I);
+ // Is it a LazyCompoundVal? All referenced regions are live as well.
+ // The LazyCompoundVal itself is not live but should be readable.
+ if (auto LCS = V.getAs<nonloc::LazyCompoundVal>()) {
+ SymReaper.markLazilyCopied(LCS->getRegion());
+
+ for (SVal V : RM.getInterestingValues(*LCS)) {
+ if (auto DepLCS = V.getAs<nonloc::LazyCompoundVal>())
+ SymReaper.markLazilyCopied(DepLCS->getRegion());
+ else
+ VisitBinding(V);
+ }
return;
}
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index d90e869196eb..fed17c77f03d 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -34,11 +34,10 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/None.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include <cassert>
+#include <optional>
#include <tuple>
using namespace clang;
@@ -124,7 +123,8 @@ SVal SValBuilder::convertToArrayIndex(SVal val) {
return val;
// Common case: we have an appropriately sized integer.
- if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) {
+ if (std::optional<nonloc::ConcreteInt> CI =
+ val.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt& I = CI->getValue();
if (I.getBitWidth() == ArrayIndexWidth && I.isSigned())
return val;
@@ -297,11 +297,11 @@ DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
return loc::MemRegionVal(BD);
}
-Optional<loc::MemRegionVal>
+std::optional<loc::MemRegionVal>
SValBuilder::getCastedMemRegionVal(const MemRegion *R, QualType Ty) {
if (auto OptR = StateMgr.getStoreManager().castRegion(R, Ty))
return loc::MemRegionVal(*OptR);
- return None;
+ return std::nullopt;
}
/// Return a memory region for the 'this' object reference.
@@ -319,7 +319,7 @@ loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
}
-Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
+std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
E = E->IgnoreParens();
switch (E->getStmtClass()) {
@@ -389,21 +389,21 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
case CK_NoOp:
case CK_BitCast: {
const Expr *SE = CE->getSubExpr();
- Optional<SVal> Val = getConstantVal(SE);
+ std::optional<SVal> Val = getConstantVal(SE);
if (!Val)
- return None;
+ return std::nullopt;
return evalCast(*Val, CE->getType(), SE->getType());
}
}
// FALLTHROUGH
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
}
// If we don't have a special case, fall back to the AST's constant evaluator.
default: {
// Don't try to come up with a value for materialized temporaries.
if (E->isGLValue())
- return None;
+ return std::nullopt;
ASTContext &Ctx = getContext();
Expr::EvalResult Result;
@@ -414,7 +414,7 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull))
return makeNullWithType(E->getType());
- return None;
+ return std::nullopt;
}
}
}
@@ -434,11 +434,13 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
return makeNonLoc(symLHS, Op, symRHS, ResultTy);
if (symLHS && symLHS->computeComplexity() < MaxComp)
- if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> rInt =
+ RHS.getAs<nonloc::ConcreteInt>())
return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
if (symRHS && symRHS->computeComplexity() < MaxComp)
- if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> lInt =
+ LHS.getAs<nonloc::ConcreteInt>())
return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
return UnknownVal();
@@ -501,14 +503,14 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
return UnknownVal();
}
- if (Optional<Loc> LV = lhs.getAs<Loc>()) {
- if (Optional<Loc> RV = rhs.getAs<Loc>())
+ if (std::optional<Loc> LV = lhs.getAs<Loc>()) {
+ if (std::optional<Loc> RV = rhs.getAs<Loc>())
return evalBinOpLL(state, op, *LV, *RV, type);
return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type);
}
- if (const Optional<Loc> RV = rhs.getAs<Loc>()) {
+ if (const std::optional<Loc> RV = rhs.getAs<Loc>()) {
const auto IsCommutative = [](BinaryOperatorKind Op) {
return Op == BO_Mul || Op == BO_Add || Op == BO_And || Op == BO_Xor ||
Op == BO_Or;
diff --git a/clang/lib/StaticAnalyzer/Core/SVals.cpp b/clang/lib/StaticAnalyzer/Core/SVals.cpp
index 31725926cd0d..bc9c1e40d808 100644
--- a/clang/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -25,12 +25,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -44,7 +44,7 @@ using namespace ento;
//===----------------------------------------------------------------------===//
const FunctionDecl *SVal::getAsFunctionDecl() const {
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
+ if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>())
if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl()))
@@ -78,7 +78,7 @@ SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const {
/// Get the symbol in the SVal or its base region.
SymbolRef SVal::getLocSymbolInBase() const {
- Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>();
+ std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>();
if (!X)
return nullptr;
@@ -103,7 +103,7 @@ SymbolRef SVal::getLocSymbolInBase() const {
/// should continue to the base regions if the region is not symbolic.
SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
- if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
+ if (std::optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
return X->getSymbol();
return getAsLocSymbol(IncludeBaseRegions);
@@ -118,10 +118,10 @@ const llvm::APSInt *SVal::getAsInteger() const {
}
const MemRegion *SVal::getAsRegion() const {
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
+ if (std::optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
return X->getRegion();
- if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
+ if (std::optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
return X->getLoc().getAsRegion();
return nullptr;
@@ -251,9 +251,9 @@ bool SVal::isConstant() const {
}
bool SVal::isConstant(int I) const {
- if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>())
+ if (std::optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>())
return LV->getValue() == I;
- if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>())
return NV->getValue() == I;
return false;
}
diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index ad3110792592..fab520098f13 100644
--- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -13,6 +13,8 @@
#include "clang/Analysis/MacroExpansionContext.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/Sarif.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
@@ -30,10 +32,12 @@ namespace {
class SarifDiagnostics : public PathDiagnosticConsumer {
std::string OutputFile;
const LangOptions &LO;
+ SarifDocumentWriter SarifWriter;
public:
- SarifDiagnostics(const std::string &Output, const LangOptions &LO)
- : OutputFile(Output), LO(LO) {}
+ SarifDiagnostics(const std::string &Output, const LangOptions &LO,
+ const SourceManager &SM)
+ : OutputFile(Output), LO(LO), SarifWriter(SM) {}
~SarifDiagnostics() override = default;
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
@@ -56,250 +60,12 @@ void ento::createSarifDiagnosticConsumer(
if (Output.empty())
return;
- C.push_back(new SarifDiagnostics(Output, PP.getLangOpts()));
+ C.push_back(
+ new SarifDiagnostics(Output, PP.getLangOpts(), PP.getSourceManager()));
createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
CTU, MacroExpansions);
}
-static StringRef getFileName(const FileEntry &FE) {
- StringRef Filename = FE.tryGetRealPathName();
- if (Filename.empty())
- Filename = FE.getName();
- return Filename;
-}
-
-static std::string percentEncodeURICharacter(char C) {
- // RFC 3986 claims alpha, numeric, and this handful of
- // characters are not reserved for the path component and
- // should be written out directly. Otherwise, percent
- // encode the character and write that out instead of the
- // reserved character.
- if (llvm::isAlnum(C) ||
- StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C))
- return std::string(&C, 1);
- return "%" + llvm::toHex(StringRef(&C, 1));
-}
-
-static std::string fileNameToURI(StringRef Filename) {
- llvm::SmallString<32> Ret = StringRef("file://");
-
- // Get the root name to see if it has a URI authority.
- StringRef Root = sys::path::root_name(Filename);
- if (Root.startswith("//")) {
- // There is an authority, so add it to the URI.
- Ret += Root.drop_front(2).str();
- } else if (!Root.empty()) {
- // There is no authority, so end the component and add the root to the URI.
- Ret += Twine("/" + Root).str();
- }
-
- auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename);
- assert(Iter != End && "Expected there to be a non-root path component.");
- // Add the rest of the path components, encoding any reserved characters;
- // we skip past the first path component, as it was handled it above.
- for (StringRef Component : llvm::make_range(++Iter, End)) {
- // For reasons unknown to me, we may get a backslash with Windows native
- // paths for the initial backslash following the drive component, which
- // we need to ignore as a URI path part.
- if (Component == "\\")
- continue;
-
- // Add the separator between the previous path part and the one being
- // currently processed.
- Ret += "/";
-
- // URI encode the part.
- for (char C : Component) {
- Ret += percentEncodeURICharacter(C);
- }
- }
-
- return std::string(Ret);
-}
-
-static json::Object createArtifactLocation(const FileEntry &FE) {
- return json::Object{{"uri", fileNameToURI(getFileName(FE))}};
-}
-
-static json::Object createArtifact(const FileEntry &FE) {
- return json::Object{{"location", createArtifactLocation(FE)},
- {"roles", json::Array{"resultFile"}},
- {"length", FE.getSize()},
- {"mimeType", "text/plain"}};
-}
-
-static json::Object createArtifactLocation(const FileEntry &FE,
- json::Array &Artifacts) {
- std::string FileURI = fileNameToURI(getFileName(FE));
-
- // See if the Artifacts array contains this URI already. If it does not,
- // create a new artifact object to add to the array.
- auto I = llvm::find_if(Artifacts, [&](const json::Value &File) {
- if (const json::Object *Obj = File.getAsObject()) {
- if (const json::Object *FileLoc = Obj->getObject("location")) {
- Optional<StringRef> URI = FileLoc->getString("uri");
- return URI && URI->equals(FileURI);
- }
- }
- return false;
- });
-
- // Calculate the index within the artifact array so it can be stored in
- // the JSON object.
- auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I));
- if (I == Artifacts.end())
- Artifacts.push_back(createArtifact(FE));
-
- return json::Object{{"uri", FileURI}, {"index", Index}};
-}
-
-static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc,
- unsigned int TokenLen = 0) {
- assert(!Loc.isInvalid() && "invalid Loc when adjusting column position");
-
- std::pair<FileID, unsigned> LocInfo = SM.getDecomposedExpansionLoc(Loc);
- assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) &&
- "position in file is before column number?");
-
- Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first);
- assert(Buf && "got an invalid buffer for the location's file");
- assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
- "token extends past end of buffer?");
-
- // Adjust the offset to be the start of the line, since we'll be counting
- // Unicode characters from there until our column offset.
- unsigned int Off = LocInfo.second - (SM.getExpansionColumnNumber(Loc) - 1);
- unsigned int Ret = 1;
- while (Off < (LocInfo.second + TokenLen)) {
- Off += getNumBytesForUTF8(Buf->getBuffer()[Off]);
- Ret++;
- }
-
- return Ret;
-}
-
-static json::Object createTextRegion(const LangOptions &LO, SourceRange R,
- const SourceManager &SM) {
- json::Object Region{
- {"startLine", SM.getExpansionLineNumber(R.getBegin())},
- {"startColumn", adjustColumnPos(SM, R.getBegin())},
- };
- if (R.getBegin() == R.getEnd()) {
- Region["endColumn"] = adjustColumnPos(SM, R.getBegin());
- } else {
- Region["endLine"] = SM.getExpansionLineNumber(R.getEnd());
- Region["endColumn"] = adjustColumnPos(
- SM, R.getEnd(),
- Lexer::MeasureTokenLength(R.getEnd(), SM, LO));
- }
- return Region;
-}
-
-static json::Object createPhysicalLocation(const LangOptions &LO,
- SourceRange R, const FileEntry &FE,
- const SourceManager &SMgr,
- json::Array &Artifacts) {
- return json::Object{
- {{"artifactLocation", createArtifactLocation(FE, Artifacts)},
- {"region", createTextRegion(LO, R, SMgr)}}};
-}
-
-enum class Importance { Important, Essential, Unimportant };
-
-static StringRef importanceToStr(Importance I) {
- switch (I) {
- case Importance::Important:
- return "important";
- case Importance::Essential:
- return "essential";
- case Importance::Unimportant:
- return "unimportant";
- }
- llvm_unreachable("Fully covered switch is not so fully covered");
-}
-
-static json::Object createThreadFlowLocation(json::Object &&Location,
- Importance I) {
- return json::Object{{"location", std::move(Location)},
- {"importance", importanceToStr(I)}};
-}
-
-static json::Object createMessage(StringRef Text) {
- return json::Object{{"text", Text.str()}};
-}
-
-static json::Object createLocation(json::Object &&PhysicalLocation,
- StringRef Message = "") {
- json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}};
- if (!Message.empty())
- Ret.insert({"message", createMessage(Message)});
- return Ret;
-}
-
-static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
- switch (Piece.getKind()) {
- case PathDiagnosticPiece::Call:
- case PathDiagnosticPiece::Macro:
- case PathDiagnosticPiece::Note:
- case PathDiagnosticPiece::PopUp:
- // FIXME: What should be reported here?
- break;
- case PathDiagnosticPiece::Event:
- return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important
- : Importance::Essential;
- case PathDiagnosticPiece::ControlFlow:
- return Importance::Unimportant;
- }
- return Importance::Unimportant;
-}
-
-static json::Object createThreadFlow(const LangOptions &LO,
- const PathPieces &Pieces,
- json::Array &Artifacts) {
- const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
- json::Array Locations;
- for (const auto &Piece : Pieces) {
- const PathDiagnosticLocation &P = Piece->getLocation();
- Locations.push_back(createThreadFlowLocation(
- createLocation(createPhysicalLocation(
- LO, P.asRange(),
- *P.asLocation().getExpansionLoc().getFileEntry(),
- SMgr, Artifacts),
- Piece->getString()),
- calculateImportance(*Piece)));
- }
- return json::Object{{"locations", std::move(Locations)}};
-}
-
-static json::Object createCodeFlow(const LangOptions &LO,
- const PathPieces &Pieces,
- json::Array &Artifacts) {
- return json::Object{
- {"threadFlows", json::Array{createThreadFlow(LO, Pieces, Artifacts)}}};
-}
-
-static json::Object createResult(const LangOptions &LO,
- const PathDiagnostic &Diag,
- json::Array &Artifacts,
- const StringMap<unsigned> &RuleMapping) {
- const PathPieces &Path = Diag.path.flatten(false);
- const SourceManager &SMgr = Path.front()->getLocation().getManager();
-
- auto Iter = RuleMapping.find(Diag.getCheckerName());
- assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?");
-
- return json::Object{
- {"message", createMessage(Diag.getVerboseDescription())},
- {"codeFlows", json::Array{createCodeFlow(LO, Path, Artifacts)}},
- {"locations",
- json::Array{createLocation(createPhysicalLocation(
- LO, Diag.getLocation().asRange(),
- *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
- SMgr, Artifacts))}},
- {"ruleIndex", Iter->getValue()},
- {"ruleId", Diag.getCheckerName()}};
-}
-
static StringRef getRuleDescription(StringRef CheckName) {
return llvm::StringSwitch<StringRef>(CheckName)
#define GET_CHECKERS
@@ -322,60 +88,99 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) {
;
}
-static json::Object createRule(const PathDiagnostic &Diag) {
- StringRef CheckName = Diag.getCheckerName();
- json::Object Ret{
- {"fullDescription", createMessage(getRuleDescription(CheckName))},
- {"name", CheckName},
- {"id", CheckName}};
-
- std::string RuleURI = std::string(getRuleHelpURIStr(CheckName));
- if (!RuleURI.empty())
- Ret["helpUri"] = RuleURI;
-
- return Ret;
+static ThreadFlowImportance
+calculateImportance(const PathDiagnosticPiece &Piece) {
+ switch (Piece.getKind()) {
+ case PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::Macro:
+ case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
+ // FIXME: What should be reported here?
+ break;
+ case PathDiagnosticPiece::Event:
+ return Piece.getTagStr() == "ConditionBRVisitor"
+ ? ThreadFlowImportance::Important
+ : ThreadFlowImportance::Essential;
+ case PathDiagnosticPiece::ControlFlow:
+ return ThreadFlowImportance::Unimportant;
+ }
+ return ThreadFlowImportance::Unimportant;
+}
+
+/// Accepts a SourceRange corresponding to a pair of the first and last tokens
+/// and converts to a Character granular CharSourceRange.
+static CharSourceRange convertTokenRangeToCharRange(const SourceRange &R,
+ const SourceManager &SM,
+ const LangOptions &LO) {
+ // Caret diagnostics have the first and last locations pointed at the same
+ // location, return these as-is.
+ if (R.getBegin() == R.getEnd())
+ return CharSourceRange::getCharRange(R);
+
+ SourceLocation BeginCharLoc = R.getBegin();
+ // For token ranges, the raw end SLoc points at the first character of the
+ // last token in the range. This must be moved to one past the end of the
+ // last character using the lexer.
+ SourceLocation EndCharLoc =
+ Lexer::getLocForEndOfToken(R.getEnd(), /* Offset = */ 0, SM, LO);
+ return CharSourceRange::getCharRange(BeginCharLoc, EndCharLoc);
+}
+
+static SmallVector<ThreadFlow, 8> createThreadFlows(const PathDiagnostic *Diag,
+ const LangOptions &LO) {
+ SmallVector<ThreadFlow, 8> Flows;
+ const PathPieces &Pieces = Diag->path.flatten(false);
+ for (const auto &Piece : Pieces) {
+ auto Range = convertTokenRangeToCharRange(
+ Piece->getLocation().asRange(), Piece->getLocation().getManager(), LO);
+ auto Flow = ThreadFlow::create()
+ .setImportance(calculateImportance(*Piece))
+ .setRange(Range)
+ .setMessage(Piece->getString());
+ Flows.push_back(Flow);
+ }
+ return Flows;
}
-static json::Array createRules(std::vector<const PathDiagnostic *> &Diags,
- StringMap<unsigned> &RuleMapping) {
- json::Array Rules;
+static StringMap<uint32_t>
+createRuleMapping(const std::vector<const PathDiagnostic *> &Diags,
+ SarifDocumentWriter &SarifWriter) {
+ StringMap<uint32_t> RuleMapping;
llvm::StringSet<> Seen;
for (const PathDiagnostic *D : Diags) {
- StringRef RuleID = D->getCheckerName();
- std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID);
+ StringRef CheckName = D->getCheckerName();
+ std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(CheckName);
if (P.second) {
- RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index.
- Rules.push_back(createRule(*D));
+ auto Rule = SarifRule::create()
+ .setName(CheckName)
+ .setRuleId(CheckName)
+ .setDescription(getRuleDescription(CheckName))
+ .setHelpURI(getRuleHelpURIStr(CheckName));
+ size_t RuleIdx = SarifWriter.createRule(Rule);
+ RuleMapping[CheckName] = RuleIdx;
}
}
-
- return Rules;
+ return RuleMapping;
}
-static json::Object createTool(std::vector<const PathDiagnostic *> &Diags,
- StringMap<unsigned> &RuleMapping) {
- return json::Object{
- {"driver", json::Object{{"name", "clang"},
- {"fullName", "clang static analyzer"},
- {"language", "en-US"},
- {"version", getClangFullVersion()},
- {"rules", createRules(Diags, RuleMapping)}}}};
-}
-
-static json::Object createRun(const LangOptions &LO,
- std::vector<const PathDiagnostic *> &Diags) {
- json::Array Results, Artifacts;
- StringMap<unsigned> RuleMapping;
- json::Object Tool = createTool(Diags, RuleMapping);
+static SarifResult createResult(const PathDiagnostic *Diag,
+ const StringMap<uint32_t> &RuleMapping,
+ const LangOptions &LO) {
- for (const PathDiagnostic *D : Diags)
- Results.push_back(createResult(LO, *D, Artifacts, RuleMapping));
+ StringRef CheckName = Diag->getCheckerName();
+ uint32_t RuleIdx = RuleMapping.lookup(CheckName);
+ auto Range = convertTokenRangeToCharRange(
+ Diag->getLocation().asRange(), Diag->getLocation().getManager(), LO);
- return json::Object{{"tool", std::move(Tool)},
- {"results", std::move(Results)},
- {"artifacts", std::move(Artifacts)},
- {"columnKind", "unicodeCodePoints"}};
+ SmallVector<ThreadFlow, 8> Flows = createThreadFlows(Diag, LO);
+ auto Result = SarifResult::create(RuleIdx)
+ .setRuleId(CheckName)
+ .setDiagnosticMessage(Diag->getVerboseDescription())
+ .setDiagnosticLevel(SarifResultLevel::Warning)
+ .setLocations({Range})
+ .setThreadFlows(Flows);
+ return Result;
}
void SarifDiagnostics::FlushDiagnosticsImpl(
@@ -391,10 +196,14 @@ void SarifDiagnostics::FlushDiagnosticsImpl(
llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
return;
}
- json::Object Sarif{
- {"$schema",
- "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
- {"version", "2.1.0"},
- {"runs", json::Array{createRun(LO, Diags)}}};
- OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif)));
+
+ std::string ToolVersion = getClangFullVersion();
+ SarifWriter.createRun("clang", "clang static analyzer", ToolVersion);
+ StringMap<uint32_t> RuleMapping = createRuleMapping(Diags, SarifWriter);
+ for (const PathDiagnostic *D : Diags) {
+ SarifResult Result = createResult(D, RuleMapping, LO);
+ SarifWriter.appendResult(Result);
+ }
+ auto Document = SarifWriter.createDocument();
+ OS << llvm::formatv("{0:2}\n", json::Value(std::move(Document)));
}
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index dcb6043e9df3..3286d7f468f0 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include <optional>
namespace clang {
@@ -26,7 +27,7 @@ ProgramStateRef SimpleConstraintManager::assumeInternal(ProgramStateRef State,
DefinedSVal Cond,
bool Assumption) {
// If we have a Loc value, cast it to a bool NonLoc first.
- if (Optional<Loc> LV = Cond.getAs<Loc>()) {
+ if (std::optional<Loc> LV = Cond.getAs<Loc>()) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
QualType T;
const MemRegion *MR = LV->getAsRegion();
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 762ecc18ecea..58d360a2e2db 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+#include <optional>
using namespace clang;
using namespace ento;
@@ -348,9 +349,9 @@ static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op,
isWithinConstantOverflowBounds(Int)));
}
-static Optional<NonLoc> tryRearrange(ProgramStateRef State,
- BinaryOperator::Opcode Op, NonLoc Lhs,
- NonLoc Rhs, QualType ResultTy) {
+static std::optional<NonLoc> tryRearrange(ProgramStateRef State,
+ BinaryOperator::Opcode Op, NonLoc Lhs,
+ NonLoc Rhs, QualType ResultTy) {
ProgramStateManager &StateMgr = State->getStateManager();
SValBuilder &SVB = StateMgr.getSValBuilder();
@@ -361,35 +362,35 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State,
// rearrange additive operations but rearrange comparisons only if
// option is set.
if (!SVB.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation)
- return None;
+ return std::nullopt;
SymbolRef LSym = Lhs.getAsSymbol();
if (!LSym)
- return None;
+ return std::nullopt;
if (BinaryOperator::isComparisonOp(Op)) {
SingleTy = LSym->getType();
if (ResultTy != SVB.getConditionType())
- return None;
+ return std::nullopt;
// Initialize SingleTy later with a symbol's type.
} else if (BinaryOperator::isAdditiveOp(Op)) {
SingleTy = ResultTy;
if (LSym->getType() != SingleTy)
- return None;
+ return std::nullopt;
} else {
// Don't rearrange other operations.
- return None;
+ return std::nullopt;
}
assert(!SingleTy.isNull() && "We should have figured out the type by now!");
// Rearrange signed symbolic expressions only
if (!SingleTy->isSignedIntegerOrEnumerationType())
- return None;
+ return std::nullopt;
SymbolRef RSym = Rhs.getAsSymbol();
if (!RSym || RSym->getType() != SingleTy)
- return None;
+ return std::nullopt;
BasicValueFactory &BV = State->getBasicVals();
llvm::APSInt LInt, RInt;
@@ -397,7 +398,7 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State,
std::tie(RSym, RInt) = decomposeSymbol(RSym, BV);
if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) ||
!shouldRearrange(State, Op, RSym, RInt, SingleTy))
- return None;
+ return std::nullopt;
// We know that no overflows can occur anymore.
return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt);
@@ -544,7 +545,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
case BO_LE:
case BO_GE:
op = BinaryOperator::reverseComparisonOp(op);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case BO_EQ:
case BO_NE:
case BO_Add:
@@ -558,7 +559,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
// (~0)>>a
if (LHSValue.isAllOnes() && LHSValue.isSigned())
return evalCast(lhs, resultTy, QualType{});
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
case BO_Shl:
// 0<<a and 0>>a
if (LHSValue == 0)
@@ -570,7 +571,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
// 0 % x == 0
if (LHSValue == 0)
return makeZeroVal(resultTy);
- LLVM_FALLTHROUGH;
+ [[fallthrough]];
default:
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
@@ -680,7 +681,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
if (const llvm::APSInt *RHSValue = getConstValue(state, rhs))
return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
- if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
+ if (std::optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
return *V;
// Give up -- this is not a symbolic expression we can handle.
@@ -843,7 +844,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
}
// If both operands are constants, just perform the operation.
- if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
assert(BinaryOperator::isComparisonOp(op) || op == BO_Sub);
if (const auto *ResultInt =
@@ -877,7 +878,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
return UnknownVal();
}
case loc::MemRegionValKind: {
- if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ if (std::optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
// If one of the operands is a symbol and the other is a constant,
// build an expression for use by the constraint manager.
if (SymbolRef lSym = lhs.getAsLocSymbol(true)) {
@@ -974,7 +975,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// Get the left index and cast it to the correct type.
// If the index is unknown or undefined, bail out here.
SVal LeftIndexVal = LeftER->getIndex();
- Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>();
+ std::optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>();
if (!LeftIndex)
return UnknownVal();
LeftIndexVal = evalCast(*LeftIndex, ArrayIndexTy, QualType{});
@@ -984,7 +985,7 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
// Do the same for the right index.
SVal RightIndexVal = RightER->getIndex();
- Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>();
+ std::optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>();
if (!RightIndex)
return UnknownVal();
RightIndexVal = evalCast(*RightIndex, ArrayIndexTy, QualType{});
@@ -1092,8 +1093,10 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
// We are dealing with pointer arithmetic.
// Handle pointer arithmetic on constant values.
- if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) {
- if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) {
+ if (std::optional<nonloc::ConcreteInt> rhsInt =
+ rhs.getAs<nonloc::ConcreteInt>()) {
+ if (std::optional<loc::ConcreteInt> lhsInt =
+ lhs.getAs<loc::ConcreteInt>()) {
const llvm::APSInt &leftI = lhsInt->getValue();
assert(leftI.isUnsigned());
llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true);
@@ -1157,7 +1160,7 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
if (elementType->isVoidType())
elementType = getContext().CharTy;
- if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) {
+ if (std::optional<NonLoc> indexV = index.getAs<NonLoc>()) {
return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV,
superR, getContext()));
}
@@ -1170,10 +1173,10 @@ const llvm::APSInt *SimpleSValBuilder::getConstValue(ProgramStateRef state,
if (V.isUnknownOrUndef())
return nullptr;
- if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>())
+ if (std::optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>())
return &X->getValue();
- if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>())
+ if (std::optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>())
return &X->getValue();
if (SymbolRef Sym = V.getAsSymbol())
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index 96e8878da616..fe1fa22af7ab 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -29,12 +29,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/APSInt.h"
-#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
#include <cstdint>
+#include <optional>
using namespace clang;
using namespace ento;
@@ -71,8 +71,8 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
return MRMgr.getElementRegion(T, idx, R, Ctx);
}
-Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
- QualType CastToTy) {
+std::optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
+ QualType CastToTy) {
ASTContext &Ctx = StateMgr.getContext();
// Handle casts to Objective-C objects.
@@ -89,7 +89,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// We don't know what to make of it. Return a NULL region, which
// will be interpreted as UnknownVal.
- return None;
+ return std::nullopt;
}
// Now assume we are casting from pointer to pointer. Other cases should
@@ -175,7 +175,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// If we cannot compute a raw offset, throw up our hands and return
// a NULL MemRegion*.
if (!baseR)
- return None;
+ return std::nullopt;
CharUnits off = rawOff.getOffset();
@@ -314,7 +314,8 @@ static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) {
return nullptr;
}
-Optional<SVal> StoreManager::evalBaseToDerived(SVal Base, QualType TargetType) {
+std::optional<SVal> StoreManager::evalBaseToDerived(SVal Base,
+ QualType TargetType) {
const MemRegion *MR = Base.getAsRegion();
if (!MR)
return UnknownVal();
@@ -390,7 +391,7 @@ Optional<SVal> StoreManager::evalBaseToDerived(SVal Base, QualType TargetType) {
// We failed if the region we ended up with has perfect type info.
if (isa<TypedValueRegion>(MR))
- return None;
+ return std::nullopt;
return UnknownVal();
}
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 2227bd324adc..3e97f0c95fc3 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -411,10 +411,14 @@ void SymbolReaper::markLive(SymbolRef sym) {
}
void SymbolReaper::markLive(const MemRegion *region) {
- RegionRoots.insert(region->getBaseRegion());
+ LiveRegionRoots.insert(region->getBaseRegion());
markElementIndicesLive(region);
}
+void SymbolReaper::markLazilyCopied(const clang::ento::MemRegion *region) {
+ LazilyCopiedRegionRoots.insert(region->getBaseRegion());
+}
+
void SymbolReaper::markElementIndicesLive(const MemRegion *region) {
for (auto SR = dyn_cast<SubRegion>(region); SR;
SR = dyn_cast<SubRegion>(SR->getSuperRegion())) {
@@ -437,8 +441,7 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
// is not used later in the path, we can diagnose a leak of a value within
// that field earlier than, say, the variable that contains the field dies.
MR = MR->getBaseRegion();
-
- if (RegionRoots.count(MR))
+ if (LiveRegionRoots.count(MR))
return true;
if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
@@ -454,6 +457,15 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
return isa<AllocaRegion, CXXThisRegion, MemSpaceRegion, CodeTextRegion>(MR);
}
+bool SymbolReaper::isLazilyCopiedRegion(const MemRegion *MR) const {
+ // TODO: See comment in isLiveRegion.
+ return LazilyCopiedRegionRoots.count(MR->getBaseRegion());
+}
+
+bool SymbolReaper::isReadableRegion(const MemRegion *MR) {
+ return isLiveRegion(MR) || isLazilyCopiedRegion(MR);
+}
+
bool SymbolReaper::isLive(SymbolRef sym) {
if (TheLiving.count(sym)) {
markDependentsLive(sym);
@@ -464,7 +476,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
switch (sym->getKind()) {
case SymExpr::SymbolRegionValueKind:
- KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion());
+ KnownLive = isReadableRegion(cast<SymbolRegionValue>(sym)->getRegion());
break;
case SymExpr::SymbolConjuredKind:
KnownLive = false;
diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
index 48c82cfb82b2..05f4d19ebda0 100644
--- a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -31,8 +31,8 @@ using namespace ento;
using namespace tooling;
namespace {
-/// Emitsd minimal diagnostics (report message + notes) for the 'none' output
-/// type to the standard error, or to to compliment many others. Emits detailed
+/// Emits minimal diagnostics (report message + notes) for the 'none' output
+/// type to the standard error, or to complement many others. Emits detailed
/// diagnostics in textual format for the 'text' output type.
class TextDiagnostics : public PathDiagnosticConsumer {
PathDiagnosticConsumerOptions DiagOpts;
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index ca0a66f54ced..54e0f4bee5eb 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -27,7 +27,6 @@
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Rewrite/Core/Rewriter.h"
-#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
diff --git a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index 9ee6ef4f9519..4618d17577dd 100644
--- a/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -15,7 +15,6 @@
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DynamicLibrary.h"