aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
-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.cpp153
-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.cpp307
-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.cpp385
-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
30 files changed, 1658 insertions, 1063 deletions
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;
+ }
- auto &Context = getContext();
+ 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 ";
+ }
+}
+
+/// 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);
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(CE->getType())) {
+ unsigned ArrSize = getContext().getConstantArrayElementCount(CAT);
- return Size <= AMgr.options.maxBlockVisitOnPath;
+ // 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);
+ // 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 (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
- E = Vals.end();
- I != E; ++I)
- VisitBinding(*I);
+ 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}};
+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);
- std::string RuleURI = std::string(getRuleHelpURIStr(CheckName));
- if (!RuleURI.empty())
- Ret["helpUri"] = RuleURI;
+ 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);
+}
- return Ret;
+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;