aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2022-07-27 19:50:45 +0000
committerDimitry Andric <dim@FreeBSD.org>2022-07-27 19:50:54 +0000
commit08e8dd7b9db7bb4a9de26d44c1cbfd24e869c014 (patch)
tree041e72e32710b1e742516d8c9f1575bf0116d3e3 /clang/lib/StaticAnalyzer
parent4b4fe385e49bd883fd183b5f21c1ea486c722e61 (diff)
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp100
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp11
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp20
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp26
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp23
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp91
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp124
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp67
12 files changed, 373 insertions, 127 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 987cf65d6fec..9a6c013bcf66 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -26,9 +26,11 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/raw_ostream.h"
+#include <functional>
using namespace clang;
using namespace ento;
+using namespace std::placeholders;
namespace {
struct AnyArgExpr {
@@ -118,10 +120,14 @@ public:
const LocationContext *LCtx,
const CallEvent *Call) const;
- typedef void (CStringChecker::*FnCheck)(CheckerContext &,
- const CallExpr *) const;
+ using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,
+ const CallExpr *)>;
+
CallDescriptionMap<FnCheck> Callbacks = {
- {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy},
+ {{CDF_MaybeBuiltin, "memcpy", 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)},
+ {{CDF_MaybeBuiltin, "wmemcpy", 3},
+ std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)},
{{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
{{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
{{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
@@ -135,7 +141,9 @@ public:
{{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
{{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
{{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength},
{{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength},
{{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
{{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
{{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
@@ -152,14 +160,14 @@ public:
StdCopyBackward{{"std", "copy_backward"}, 3};
FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
- void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) const;
void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest, SourceArgExpr Source,
- bool Restricted, bool IsMempcpy) const;
+ bool Restricted, bool IsMempcpy, bool IsWide) const;
void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
@@ -240,13 +248,14 @@ public:
AnyArgExpr Arg, SVal l) const;
ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
- AccessKind Access) const;
+ AccessKind Access, bool IsWide = false) const;
ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
AnyArgExpr Buffer, SizeArgExpr Size,
- AccessKind Access) const;
+ AccessKind Access,
+ bool IsWide = false) const;
ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
- AnyArgExpr Second) const;
+ AnyArgExpr Second, bool IsWide = false) const;
void emitOverlapBug(CheckerContext &C,
ProgramStateRef state,
const Stmt *First,
@@ -329,7 +338,8 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef state,
AnyArgExpr Buffer, SVal Element,
- AccessKind Access) const {
+ AccessKind Access,
+ bool IsWide) const {
// If a previous check has failed, propagate the failure.
if (!state)
@@ -344,17 +354,36 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
if (!ER)
return state;
- if (ER->getValueType() != C.getASTContext().CharTy)
- return state;
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+
+ // Get the index of the accessed element.
+ NonLoc Idx = ER->getIndex();
+
+ if (!IsWide) {
+ if (ER->getValueType() != Ctx.CharTy)
+ return state;
+ } else {
+ if (ER->getValueType() != Ctx.WideCharTy)
+ return state;
+
+ QualType SizeTy = Ctx.getSizeType();
+ NonLoc WideSize =
+ svalBuilder
+ .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),
+ SizeTy)
+ .castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy);
+ if (Offset.isUnknown())
+ return state;
+ Idx = Offset.castAs<NonLoc>();
+ }
// Get the size of the array.
const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
DefinedOrUnknownSVal Size =
getDynamicExtent(state, superReg, C.getSValBuilder());
- // Get the index of the accessed element.
- DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
-
ProgramStateRef StInBound, StOutBound;
std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size);
if (StOutBound && !StInBound) {
@@ -385,11 +414,10 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
return StInBound;
}
-ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
- ProgramStateRef State,
- AnyArgExpr Buffer,
- SizeArgExpr Size,
- AccessKind Access) const {
+ProgramStateRef
+CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
+ AnyArgExpr Buffer, SizeArgExpr Size,
+ AccessKind Access, bool IsWide) const {
// If a previous check has failed, propagate the failure.
if (!State)
return nullptr;
@@ -398,7 +426,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
ASTContext &Ctx = svalBuilder.getContext();
QualType SizeTy = Size.Expression->getType();
- QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
// Check that the first buffer is non-null.
SVal BufVal = C.getSVal(Buffer.Expression);
@@ -432,7 +460,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
SVal BufEnd =
svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
- State = CheckLocation(C, State, Buffer, BufEnd, Access);
+ State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide);
// If the buffer isn't large enough, abort.
if (!State)
@@ -446,7 +474,8 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
ProgramStateRef state,
SizeArgExpr Size, AnyArgExpr First,
- AnyArgExpr Second) const {
+ AnyArgExpr Second,
+ bool IsWide) const {
if (!Filter.CheckCStringBufferOverlap)
return state;
@@ -525,7 +554,7 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
// Convert the first buffer's start address to char*.
// Bail out if the cast fails.
ASTContext &Ctx = svalBuilder.getContext();
- QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
SVal FirstStart =
svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
@@ -1161,7 +1190,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, SizeArgExpr Size,
DestinationArgExpr Dest,
SourceArgExpr Source, bool Restricted,
- bool IsMempcpy) const {
+ bool IsMempcpy, bool IsWide) const {
CurrentFunctionDescription = "memory copy function";
// See if the size argument is zero.
@@ -1204,11 +1233,11 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
return;
// Ensure the accesses are valid and that the buffers do not overlap.
- state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write);
- state = CheckBufferAccess(C, state, Source, Size, AccessKind::read);
+ state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide);
+ state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide);
if (Restricted)
- state = CheckOverlap(C, state, Size, Dest, Source);
+ state = CheckOverlap(C, state, Size, Dest, Source, IsWide);
if (!state)
return;
@@ -1258,7 +1287,8 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
}
}
-void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE,
+ bool IsWide) const {
// void *memcpy(void *restrict dst, const void *restrict src, size_t n);
// The return value is the address of the destination buffer.
DestinationArgExpr Dest = {CE->getArg(0), 0};
@@ -1269,7 +1299,8 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy,
+ IsWide);
}
void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
@@ -1281,7 +1312,8 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = true;
constexpr bool IsMempcpy = true;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+ false);
}
void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
@@ -1293,7 +1325,8 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+ false);
}
void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
@@ -1304,7 +1337,8 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
constexpr bool IsRestricted = false;
constexpr bool IsMempcpy = false;
- evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+ evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+ false);
}
void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
@@ -2336,7 +2370,7 @@ bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
// Check and evaluate the call.
const auto *CE = cast<CallExpr>(Call.getOriginExpr());
- (this->*Callback)(C, CE);
+ Callback(this, C, CE);
// If the evaluate call resulted in no change, chain to the next eval call
// handler.
diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index 895212d134b8..b673b51c4623 100644
--- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -55,9 +55,9 @@ public:
ID.AddPointer(getTag());
}
- virtual PathDiagnosticPieceRef
- VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
- PathSensitiveBugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
// FIXME: Scan the map once in the visitor's constructor and do a direct
// lookup by region.
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 36464707d06a..adedc9c30fad 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -852,9 +852,8 @@ protected:
return false;
}
- virtual bool
- wasModifiedInFunction(const ExplodedNode *CallEnterN,
- const ExplodedNode *CallExitEndN) override {
+ bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
+ const ExplodedNode *CallExitEndN) override {
if (!doesFnIntendToHandleOwnership(
CallExitEndN->getFirstPred()->getLocationContext()->getDecl(),
CallExitEndN->getState()->getAnalysisManager().getASTContext()))
@@ -885,7 +884,7 @@ protected:
"later deallocation");
}
- virtual PathDiagnosticPieceRef
+ PathDiagnosticPieceRef
maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
const ObjCMethodCall &Call,
const ExplodedNode *N) override {
@@ -893,7 +892,7 @@ protected:
return nullptr;
}
- virtual PathDiagnosticPieceRef
+ PathDiagnosticPieceRef
maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
const CXXConstructorCall &Call,
const ExplodedNode *N) override {
@@ -901,7 +900,7 @@ protected:
return nullptr;
}
- virtual PathDiagnosticPieceRef
+ PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
const ExplodedNode *N) override {
// TODO: Factor the logic of "what constitutes as an entity being passed
diff --git a/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index cddf206728b1..27fd40a441fa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -182,7 +182,7 @@ public:
return false;
};
- if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
+ if (llvm::any_of(RD->fields(), IsTrickyField))
return true;
return false;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index ef673ae41a3d..5897e5096461 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -240,7 +240,7 @@ class StdLibraryFunctionsChecker
ArgNo OtherArgN;
public:
- virtual StringRef getName() const override { return "Comparison"; };
+ StringRef getName() const override { return "Comparison"; };
ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode,
ArgNo OtherArgN)
: ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 38e69e81d800..cd91fa9b090c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -57,19 +57,17 @@ class RegularField final : public FieldNode {
public:
RegularField(const FieldRegion *FR) : FieldNode(FR) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "uninitialized field ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
- Out << '.';
- }
+ void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; }
};
/// Represents that the FieldNode that comes after this is declared in a base
@@ -85,20 +83,20 @@ public:
assert(T->getAsCXXRecordDecl());
}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
llvm_unreachable("This node can never be the final node in the "
"fieldchain!");
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {}
+ void printSeparator(llvm::raw_ostream &Out) const override {}
- virtual bool isBase() const override { return true; }
+ bool isBase() const override { return true; }
};
} // end of anonymous namespace
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index a6e81b3657a2..f5bd765ff679 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -34,20 +34,20 @@ public:
LocField(const FieldRegion *FR, const bool IsDereferenced = true)
: FieldNode(FR), IsDereferenced(IsDereferenced) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
if (IsDereferenced)
Out << "uninitialized pointee ";
else
Out << "uninitialized pointer ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ void printSeparator(llvm::raw_ostream &Out) const override {
if (getDecl()->getType()->isPointerType())
Out << "->";
else
@@ -64,11 +64,11 @@ public:
NeedsCastLocField(const FieldRegion *FR, const QualType &T)
: FieldNode(FR), CastBackType(T) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "uninitialized pointee ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {
+ void printPrefix(llvm::raw_ostream &Out) const override {
// If this object is a nonloc::LocAsInteger.
if (getDecl()->getType()->isIntegerType())
Out << "reinterpret_cast";
@@ -78,13 +78,11 @@ public:
Out << '<' << CastBackType.getAsString() << ">(";
}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl()) << ')';
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
- Out << "->";
- }
+ void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; }
};
/// Represents a Loc field that points to itself.
@@ -93,17 +91,17 @@ class CyclicLocField final : public FieldNode {
public:
CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
- virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ void printNoteMsg(llvm::raw_ostream &Out) const override {
Out << "object references itself ";
}
- virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+ void printPrefix(llvm::raw_ostream &Out) const override {}
- virtual void printNode(llvm::raw_ostream &Out) const override {
+ void printNode(llvm::raw_ostream &Out) const override {
Out << getVariableName(getDecl());
}
- virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ void printSeparator(llvm::raw_ostream &Out) const override {
llvm_unreachable("CyclicLocField objects must be the last node of the "
"fieldchain!");
}
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 2caa5bbc16df..3a90c37a36da 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -530,9 +530,8 @@ public:
private:
/// \return Whether \c RegionOfInterest was modified at \p CurrN compared to
/// the value it holds in \p CallExitBeginN.
- virtual bool
- wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
- const ExplodedNode *CallExitBeginN) override;
+ bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
+ const ExplodedNode *CallExitBeginN) override;
/// Attempts to find the region of interest in a given record decl,
/// by either following the base classes or fields.
@@ -547,19 +546,17 @@ private:
// Region of interest corresponds to an IVar, exiting a method
// which could have written into that IVar, but did not.
- virtual PathDiagnosticPieceRef
- maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
- const ObjCMethodCall &Call,
- const ExplodedNode *N) override final;
+ PathDiagnosticPieceRef maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) final;
- virtual PathDiagnosticPieceRef
- maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
- const CXXConstructorCall &Call,
- const ExplodedNode *N) override final;
+ PathDiagnosticPieceRef maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) final;
- virtual PathDiagnosticPieceRef
+ PathDiagnosticPieceRef
maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
- const ExplodedNode *N) override final;
+ const ExplodedNode *N) final;
/// Consume the information on the no-store stack frame in order to
/// either emit a note or suppress the report enirely.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index d8f56f2f8cff..19149d079822 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -196,6 +196,14 @@ typedef llvm::ImmutableMap<
IndexOfElementToConstructMap;
REGISTER_TRAIT_WITH_PROGRAMSTATE(IndexOfElementToConstruct,
IndexOfElementToConstructMap)
+
+// This trait is responsible for holding our pending ArrayInitLoopExprs.
+// It pairs the LocationContext and the initializer CXXConstructExpr with
+// the size of the array that's being copy initialized.
+typedef llvm::ImmutableMap<
+ std::pair<const CXXConstructExpr *, const LocationContext *>, unsigned>
+ PendingInitLoopMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PendingInitLoop, PendingInitLoopMap)
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
//===----------------------------------------------------------------------===//
@@ -462,6 +470,34 @@ 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()}));
+}
+
+ProgramStateRef ExprEngine::removePendingInitLoop(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(E && State->contains<PendingInitLoop>(Key));
+ return State->remove<PendingInitLoop>(Key);
+}
+
+ProgramStateRef ExprEngine::setPendingInitLoop(ProgramStateRef State,
+ const CXXConstructExpr *E,
+ const LocationContext *LCtx,
+ unsigned Size) {
+ auto Key = std::make_pair(E, LCtx->getStackFrame());
+
+ assert(!State->contains<PendingInitLoop>(Key) && Size > 0);
+
+ return State->set<PendingInitLoop>(Key, Size);
+}
+
Optional<unsigned>
ExprEngine::getIndexOfElementToConstruct(ProgramStateRef State,
const CXXConstructExpr *E,
@@ -487,17 +523,23 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
const LocationContext *LC, SVal V) {
ConstructedObjectKey Key(Item, LC->getStackFrame());
- const CXXConstructExpr *E = nullptr;
+ const Expr *Init = nullptr;
if (auto DS = dyn_cast_or_null<DeclStmt>(Item.getStmtOrNull())) {
if (auto VD = dyn_cast_or_null<VarDecl>(DS->getSingleDecl()))
- E = dyn_cast<CXXConstructExpr>(VD->getInit());
+ Init = VD->getInit();
}
- if (!E && !Item.getStmtOrNull()) {
- auto CtorInit = Item.getCXXCtorInitializer();
- E = dyn_cast<CXXConstructExpr>(CtorInit->getInit());
- }
+ if (auto LE = dyn_cast_or_null<LambdaExpr>(Item.getStmtOrNull()))
+ Init = *(LE->capture_init_begin() + Item.getIndex());
+
+ if (!Init && !Item.getStmtOrNull())
+ Init = Item.getCXXCtorInitializer()->getInit();
+
+ // In an ArrayInitLoopExpr the real initializer is returned by
+ // getSubExpr().
+ if (const auto *AILE = dyn_cast_or_null<ArrayInitLoopExpr>(Init))
+ Init = AILE->getSubExpr();
// FIXME: Currently the state might already contain the marker due to
// incorrect handling of temporaries bound to default parameters.
@@ -508,7 +550,8 @@ ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
assert((!State->get<ObjectsUnderConstruction>(Key) ||
Key.getItem().getKind() ==
ConstructionContextItem::TemporaryDestructorKind ||
- State->contains<IndexOfElementToConstruct>({E, LC})) &&
+ State->contains<IndexOfElementToConstruct>(
+ {dyn_cast_or_null<CXXConstructExpr>(Init), LC})) &&
"The object is already marked as `UnderConstruction`, when it's not "
"supposed to!");
return State->set<ObjectsUnderConstruction>(Key, V);
@@ -2744,7 +2787,10 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
SVal Base = state->getLValue(DD, LCtx);
if (DD->getType()->isReferenceType()) {
- Base = state->getSVal(Base.getAsRegion());
+ if (const MemRegion *R = Base.getAsRegion())
+ Base = state->getSVal(R);
+ else
+ Base = UnknownVal();
}
SVal V = UnknownVal();
@@ -2765,15 +2811,27 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
V = state->getLValue(BD->getType(), Idx, Base);
}
- // Handle binding to tuple-like strcutures
- else if (BD->getHoldingVar()) {
- // FIXME: handle tuples
- return;
+ // Handle binding to tuple-like structures
+ else if (const auto *HV = BD->getHoldingVar()) {
+ V = state->getLValue(HV, LCtx);
+
+ if (HV->getType()->isReferenceType()) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
} else
llvm_unreachable("An unknown case of structured binding encountered!");
- if (BD->getType()->isReferenceType())
- V = state->getSVal(V.getAsRegion());
+ // In case of tuple-like types the references are already handled, so we
+ // don't want to handle them again.
+ if (BD->getType()->isReferenceType() && !BD->getHoldingVar()) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
@@ -2797,6 +2855,11 @@ void ExprEngine::VisitArrayInitLoopExpr(const ArrayInitLoopExpr *Ex,
const Expr *Arr = Ex->getCommonExpr()->getSourceExpr();
for (auto *Node : CheckerPreStmt) {
+
+ // The constructor visitior has already taken care of everything.
+ if (auto *CE = dyn_cast<CXXConstructExpr>(Ex->getSubExpr()))
+ break;
+
const LocationContext *LCtx = Node->getLocationContext();
ProgramStateRef state = Node->getState();
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 08fac9fb2e69..04e00274b2a7 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -290,6 +290,23 @@ SVal ExprEngine::computeObjectUnderConstruction(
return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
}
+ case ConstructionContext::LambdaCaptureKind: {
+ CallOpts.IsTemporaryCtorOrDtor = true;
+
+ const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
+
+ SVal Base = loc::MemRegionVal(
+ MRMgr.getCXXTempObjectRegion(LCC->getInitializer(), LCtx));
+
+ const auto *CE = dyn_cast_or_null<CXXConstructExpr>(E);
+ if (getIndexOfElementToConstruct(State, CE, LCtx)) {
+ CallOpts.IsArrayCtorOrDtor = true;
+ Base = State->getLValue(E->getType(), svalBuilder.makeArrayIndex(Idx),
+ Base);
+ }
+
+ return Base;
+ }
case ConstructionContext::ArgumentKind: {
// Arguments are technically temporaries.
CallOpts.IsTemporaryCtorOrDtor = true;
@@ -450,6 +467,17 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
return State;
}
+ case ConstructionContext::LambdaCaptureKind: {
+ const auto *LCC = cast<LambdaCaptureConstructionContext>(CC);
+
+ // If we capture and array, we want to store the super region, not a
+ // sub-region.
+ if (const auto *EL = dyn_cast_or_null<ElementRegion>(V.getAsRegion()))
+ V = loc::MemRegionVal(EL->getSuperRegion());
+
+ return addObjectUnderConstruction(
+ State, {LCC->getLambdaExpr(), LCC->getIndex()}, LCtx, V);
+ }
case ConstructionContext::ArgumentKind: {
const auto *ACC = cast<ArgumentConstructionContext>(CC);
if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
@@ -462,6 +490,59 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
llvm_unreachable("Unhandled construction context!");
}
+static ProgramStateRef
+bindRequiredArrayElementToEnvironment(ProgramStateRef State,
+ const ArrayInitLoopExpr *AILE,
+ const LocationContext *LCtx, SVal Idx) {
+ // The ctor in this case is guaranteed to be a copy ctor, otherwise we hit a
+ // compile time error.
+ //
+ // -ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-DeclRefExpr <-- match this
+ // `-CXXConstructExpr
+ // `-ImplicitCastExpr
+ // `-ArraySubscriptExpr
+ // |-ImplicitCastExpr
+ // | `-OpaqueValueExpr
+ // | `-DeclRefExpr
+ // `-ArrayInitIndexExpr
+ //
+ // The resulting expression might look like the one below in an implicit
+ // copy/move ctor.
+ //
+ // ArrayInitLoopExpr <-- we're here
+ // |-OpaqueValueExpr
+ // | `-MemberExpr <-- match this
+ // | (`-CXXStaticCastExpr) <-- move ctor only
+ // | `-DeclRefExpr
+ // `-CXXConstructExpr
+ // `-ArraySubscriptExpr
+ // |-ImplicitCastExpr
+ // | `-OpaqueValueExpr
+ // | `-MemberExpr
+ // | `-DeclRefExpr
+ // `-ArrayInitIndexExpr
+ //
+ // 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();
+
+ SVal Base = UnknownVal();
+ if (const auto *ME = dyn_cast<MemberExpr>(OVESrc))
+ Base = State->getSVal(ME, LCtx);
+ else if (const auto *DRE = cast<DeclRefExpr>(OVESrc))
+ Base = State->getLValue(cast<VarDecl>(DRE->getDecl()), LCtx);
+ else
+ llvm_unreachable("ArrayInitLoopExpr contains unexpected source expression");
+
+ SVal NthElem = State->getLValue(CE->getType(), Idx, Base);
+
+ return State->BindExpr(CE->getArg(0), LCtx, NthElem);
+}
+
void ExprEngine::handleConstructor(const Expr *E,
ExplodedNode *Pred,
ExplodedNodeSet &destNodes) {
@@ -502,12 +583,26 @@ void ExprEngine::handleConstructor(const Expr *E,
// Inherited constructors are always base class constructors.
assert(CE && !CIE && "A complete constructor is inherited?!");
+ // If the ctor is part of an ArrayInitLoopExpr, we want to handle it
+ // differently.
+ auto *AILE = CC ? CC->getArrayInitLoop() : nullptr;
+
unsigned Idx = 0;
- if (CE->getType()->isArrayType()) {
+ if (CE->getType()->isArrayType() || AILE) {
Idx = getIndexOfElementToConstruct(State, CE, LCtx).value_or(0u);
State = setIndexOfElementToConstruct(State, CE, LCtx, Idx + 1);
}
+ 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 = 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);
@@ -908,7 +1003,7 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// values are properly placed inside the required region, however if an
// initializer list is used, this doesn't happen automatically.
auto *Init = CNE->getInitializer();
- bool isInitList = dyn_cast_or_null<InitListExpr>(Init);
+ bool isInitList = isa_and_nonnull<InitListExpr>(Init);
QualType ObjTy =
isInitList ? Init->getType() : CNE->getType()->getPointeeType();
@@ -1038,19 +1133,40 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
// If we created a new MemRegion for the lambda, we should explicitly bind
// the captures.
+ unsigned Idx = 0;
CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
e = LE->capture_init_end();
- i != e; ++i, ++CurField) {
+ i != e; ++i, ++CurField, ++Idx) {
FieldDecl *FieldForCapture = *CurField;
SVal FieldLoc = State->getLValue(FieldForCapture, V);
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();
+ }
+
assert(InitExpr && "Capture missing initialization expression");
- InitVal = State->getSVal(InitExpr, LocCtxt);
+
+ if (dyn_cast<CXXConstructExpr>(InitExpr)) {
+ InitVal = *getObjectUnderConstruction(State, {LE, Idx}, LocCtxt);
+ InitVal = State->getSVal(InitVal.getAsRegion());
+
+ State = finishObjectConstruction(State, {LE, Idx}, LocCtxt);
+ } else
+ InitVal = State->getSVal(InitExpr, LocCtxt);
+
} else {
+
+ assert(!getObjectUnderConstruction(State, {LE, Idx}, LocCtxt) &&
+ "VLA capture by value is a compile time error!");
+
// The field stores the length of a captured variable-length array.
// These captures don't have initialization expressions; instead we
// get the length from the VLAType size expression.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index ebcca92a3e4e..8fb2ce9cd18f 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -265,9 +265,13 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
ShouldRepeatCall = shouldRepeatCtorCall(state, CCE, callerCtx);
- if (!ShouldRepeatCall &&
- getIndexOfElementToConstruct(state, CCE, callerCtx))
- state = removeIndexOfElementToConstruct(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)) {
@@ -815,8 +819,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
// We still allow construction into ElementRegion targets when they don't
// represent array elements.
if (CallOpts.IsArrayCtorOrDtor) {
- if (!shouldInlineArrayConstruction(
- dyn_cast<ArrayType>(CtorExpr->getType())))
+ if (!shouldInlineArrayConstruction(Pred->getState(), CtorExpr, CurLC))
return CIP_DisallowedOnce;
}
@@ -1082,10 +1085,14 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
return true;
}
-bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) {
- if (!Type)
+bool ExprEngine::shouldInlineArrayConstruction(const ProgramStateRef State,
+ const CXXConstructExpr *CE,
+ const LocationContext *LCtx) {
+ 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);
@@ -1093,6 +1100,10 @@ bool ExprEngine::shouldInlineArrayConstruction(const ArrayType *Type) {
return Size <= AMgr.options.maxBlockVisitOnPath;
}
+ // 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 false;
}
@@ -1111,6 +1122,9 @@ bool ExprEngine::shouldRepeatCtorCall(ProgramStateRef State,
return Size > getIndexOfElementToConstruct(State, E, LCtx);
}
+ if (auto Size = getPendingInitLoop(State, E, LCtx))
+ return Size > getIndexOfElementToConstruct(State, E, LCtx);
+
return false;
}
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 5e946483a93d..d8ece9f39a25 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1888,6 +1888,30 @@ SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL,
return svalBuilder.makeIntVal(Code, ElemT);
}
+static Optional<SVal> getDerivedSymbolForBinding(
+ RegionBindingsConstRef B, const TypedValueRegion *BaseRegion,
+ const TypedValueRegion *SubReg, const ASTContext &Ctx, SValBuilder &SVB) {
+ assert(BaseRegion);
+ QualType BaseTy = BaseRegion->getValueType();
+ 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 (SymbolRef ParentValueAsSym = ParentValue->getAsSymbol())
+ return SVB.getDerivedRegionValueSymbolVal(ParentValueAsSym, SubReg);
+
+ if (ParentValue->isUndef())
+ return UndefinedVal();
+
+ // Other cases: give up. We are indexing into a larger object
+ // that has some value, but we don't know how to handle that yet.
+ return UnknownVal();
+ }
+ }
+ }
+ return None;
+}
+
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
// Check if the region has a binding.
@@ -1932,27 +1956,10 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
if (!O.getRegion())
return UnknownVal();
- if (const TypedValueRegion *baseR =
- dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
- QualType baseT = baseR->getValueType();
- if (baseT->isScalarType()) {
- QualType elemT = R->getElementType();
- if (elemT->isScalarType()) {
- if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) {
- if (const Optional<SVal> &V = B.getDirectBinding(superR)) {
- if (SymbolRef parentSym = V->getAsSymbol())
- return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
+ if (const TypedValueRegion *baseR = dyn_cast<TypedValueRegion>(O.getRegion()))
+ if (auto V = getDerivedSymbolForBinding(B, baseR, R, Ctx, svalBuilder))
+ return *V;
- if (V->isUnknownOrUndef())
- return *V;
- // Other cases: give up. We are indexing into a larger object
- // that has some value, but we don't know how to handle that yet.
- return UnknownVal();
- }
- }
- }
- }
- }
return getBindingForFieldOrElementCommon(B, R, R->getElementType());
}
@@ -1988,6 +1995,26 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
}
}
+ // Handle the case where we are accessing into a larger scalar object.
+ // For example, this handles:
+ // struct header {
+ // unsigned a : 1;
+ // unsigned b : 1;
+ // };
+ // struct parse_t {
+ // unsigned bits0 : 1;
+ // unsigned bits2 : 2; // <-- header
+ // unsigned bits4 : 4;
+ // };
+ // int parse(parse_t *p) {
+ // unsigned copy = p->bits2;
+ // header *bits = (header *)&copy;
+ // return bits->b; <-- here
+ // }
+ if (const auto *Base = dyn_cast<TypedValueRegion>(R->getBaseRegion()))
+ if (auto V = getDerivedSymbolForBinding(B, Base, R, Ctx, svalBuilder))
+ return *V;
+
return getBindingForFieldOrElementCommon(B, R, Ty);
}