summaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-07-26 19:36:28 +0000
commitcfca06d7963fa0909f90483b42a6d7d194d01e08 (patch)
tree209fb2a2d68f8f277793fc8df46c753d31bc853b /clang/lib/StaticAnalyzer/Core
parent706b4fc47bbc608932d3b491ae19a3b9cde9497b (diff)
Notes
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp71
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp45
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp363
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp42
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerManager.cpp59
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp241
-rw-r--r--clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp23
-rw-r--r--clang/lib/StaticAnalyzer/Core/CoreEngine.cpp43
-rw-r--r--clang/lib/StaticAnalyzer/Core/DynamicSize.cpp71
-rw-r--r--clang/lib/StaticAnalyzer/Core/DynamicType.cpp206
-rw-r--r--clang/lib/StaticAnalyzer/Core/Environment.cpp12
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp51
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp354
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp36
-rw-r--r--clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp67
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp18
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopWidening.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Core/MemRegion.cpp296
-rw-r--r--clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp30
-rw-r--r--clang/lib/StaticAnalyzer/Core/ProgramState.cpp17
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp842
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp51
-rw-r--r--clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/SValBuilder.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/Store.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/SubEngine.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/SymbolManager.cpp71
-rw-r--r--clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp156
35 files changed, 2353 insertions, 887 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index fdd03c75920d..ecfc7106560e 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -13,7 +13,7 @@ using namespace ento;
void AnalysisManager::anchor() { }
-AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
+AnalysisManager::AnalysisManager(ASTContext &ASTCtx, Preprocessor &PP,
const PathDiagnosticConsumers &PDC,
StoreManagerCreator storemgr,
ConstraintManagerCreator constraintmgr,
@@ -38,7 +38,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
Options.ShouldElideConstructors,
/*addVirtualBaseBranches=*/true,
injector),
- Ctx(ASTCtx), LangOpts(ASTCtx.getLangOpts()),
+ Ctx(ASTCtx), PP(PP), LangOpts(ASTCtx.getLangOpts()),
PathConsumers(PDC), CreateStoreMgr(storemgr),
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
options(Options) {
diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 7cd48bf44374..73f057f09550 100644
--- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -239,7 +239,7 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
if (Amt >= V1.getBitWidth())
return nullptr;
- if (!Ctx.getLangOpts().CPlusPlus2a) {
+ if (!Ctx.getLangOpts().CPlusPlus20) {
if (V1.isSigned() && V1.isNegative())
return nullptr;
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 1864bcef9b87..72be4e81c83d 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -34,10 +34,12 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/ArrayRef.h"
@@ -51,6 +53,7 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Casting.h"
@@ -2105,6 +2108,53 @@ void BuiltinBug::anchor() {}
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
+LLVM_ATTRIBUTE_USED static bool
+isDependency(const CheckerRegistryData &Registry, StringRef CheckerName) {
+ for (const std::pair<StringRef, StringRef> &Pair : Registry.Dependencies) {
+ if (Pair.second == CheckerName)
+ return true;
+ }
+ return false;
+}
+
+LLVM_ATTRIBUTE_USED static bool isHidden(const CheckerRegistryData &Registry,
+ StringRef CheckerName) {
+ for (const CheckerInfo &Checker : Registry.Checkers) {
+ if (Checker.FullName == CheckerName)
+ return Checker.IsHidden;
+ }
+ llvm_unreachable(
+ "Checker name not found in CheckerRegistry -- did you retrieve it "
+ "correctly from CheckerManager::getCurrentCheckerName?");
+}
+
+PathSensitiveBugReport::PathSensitiveBugReport(
+ const BugType &bt, StringRef shortDesc, StringRef desc,
+ const ExplodedNode *errorNode, PathDiagnosticLocation LocationToUnique,
+ const Decl *DeclToUnique)
+ : BugReport(Kind::PathSensitive, bt, shortDesc, desc), ErrorNode(errorNode),
+ ErrorNodeRange(getStmt() ? getStmt()->getSourceRange() : SourceRange()),
+ UniqueingLocation(LocationToUnique), UniqueingDecl(DeclToUnique) {
+ assert(!isDependency(ErrorNode->getState()
+ ->getAnalysisManager()
+ .getCheckerManager()
+ ->getCheckerRegistryData(),
+ bt.getCheckerName()) &&
+ "Some checkers depend on this one! We don't allow dependency "
+ "checkers to emit warnings, because checkers should depend on "
+ "*modeling*, not *diagnostics*.");
+
+ assert(
+ (bt.getCheckerName().startswith("debug") ||
+ !isHidden(ErrorNode->getState()
+ ->getAnalysisManager()
+ .getCheckerManager()
+ ->getCheckerRegistryData(),
+ bt.getCheckerName())) &&
+ "Hidden checkers musn't emit diagnostics as they are by definition "
+ "non-user facing!");
+}
+
void PathSensitiveBugReport::addVisitor(
std::unique_ptr<BugReporterVisitor> visitor) {
if (!visitor)
@@ -2193,12 +2243,12 @@ static void insertToInterestingnessMap(
return;
case bugreporter::TrackingKind::Condition:
return;
- }
+ }
- llvm_unreachable(
- "BugReport::markInteresting currently can only handle 2 different "
- "tracking kinds! Please define what tracking kind should this entitiy"
- "have, if it was already marked as interesting with a different kind!");
+ llvm_unreachable(
+ "BugReport::markInteresting currently can only handle 2 different "
+ "tracking kinds! Please define what tracking kind should this entitiy"
+ "have, if it was already marked as interesting with a different kind!");
}
void PathSensitiveBugReport::markInteresting(SymbolRef sym,
@@ -2389,6 +2439,7 @@ ProgramStateManager &PathSensitiveBugReporter::getStateManager() const {
return Eng.getStateManager();
}
+BugReporter::BugReporter(BugReporterData &d) : D(d) {}
BugReporter::~BugReporter() {
// Make sure reports are flushed.
assert(StrBugTypes.empty() &&
@@ -2409,7 +2460,7 @@ void BugReporter::FlushReports() {
// EmitBasicReport.
// FIXME: There are leaks from checkers that assume that the BugTypes they
// create will be destroyed by the BugReporter.
- llvm::DeleteContainerSeconds(StrBugTypes);
+ StrBugTypes.clear();
}
//===----------------------------------------------------------------------===//
@@ -2781,7 +2832,7 @@ Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
R->clearVisitors();
R->addVisitor(std::make_unique<FalsePositiveRefutationBRVisitor>());
- // We don't overrite the notes inserted by other visitors because the
+ // We don't overwrite the notes inserted by other visitors because the
// refutation manager does not add any new note to the path
generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC);
}
@@ -3262,8 +3313,8 @@ BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName,
SmallString<136> fullDesc;
llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
<< ":" << category;
- BugType *&BT = StrBugTypes[fullDesc];
+ std::unique_ptr<BugType> &BT = StrBugTypes[fullDesc];
if (!BT)
- BT = new BugType(CheckName, name, category);
- return BT;
+ BT = std::make_unique<BugType>(CheckName, name, category);
+ return BT.get();
}
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 0525b5c41e34..ef4d38ff498f 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -45,7 +45,6 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
@@ -358,7 +357,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
public:
NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
- : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
+ : RegionOfInterest(R), MmrMgr(R->getMemRegionManager()),
SM(MmrMgr.getContext().getSourceManager()),
PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {}
@@ -813,7 +812,7 @@ public:
const SourceManager &SMgr = BRC.getSourceManager();
if (auto Loc = matchAssignment(N)) {
if (isFunctionMacroExpansion(*Loc, SMgr)) {
- std::string MacroName = getMacroName(*Loc, BRC);
+ std::string MacroName = std::string(getMacroName(*Loc, BRC));
SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName)
BR.markInvalid(getTag(), MacroName.c_str());
@@ -1735,10 +1734,9 @@ constructDebugPieceForTrackedCondition(const Expr *Cond,
!BRC.getAnalyzerOptions().ShouldTrackConditionsDebug)
return nullptr;
- std::string ConditionText = Lexer::getSourceText(
+ std::string ConditionText = std::string(Lexer::getSourceText(
CharSourceRange::getTokenRange(Cond->getSourceRange()),
- BRC.getSourceManager(),
- BRC.getASTContext().getLangOpts());
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts()));
return std::make_shared<PathDiagnosticEventPiece>(
PathDiagnosticLocation::createBegin(
@@ -2494,7 +2492,7 @@ PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
Out << WillBeUsedForACondition;
// Convert 'field ...' to 'Field ...' if it is a MemberExpr.
- std::string Message = Out.str();
+ std::string Message = std::string(Out.str());
Message[0] = toupper(Message[0]);
// If we know the value create a pop-up note to the value part of 'BExpr'.
@@ -2821,7 +2819,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *EndPathNode,
PathSensitiveBugReport &BR) {
// Collect new constraints
- VisitNode(EndPathNode, BRC, BR);
+ addConstraints(EndPathNode, /*OverwriteConstraintsOnExistingSyms=*/true);
// Create a refutation manager
llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver();
@@ -2832,30 +2830,30 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
const SymbolRef Sym = I.first;
auto RangeIt = I.second.begin();
- llvm::SMTExprRef Constraints = SMTConv::getRangeExpr(
+ llvm::SMTExprRef SMTConstraints = SMTConv::getRangeExpr(
RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(),
/*InRange=*/true);
while ((++RangeIt) != I.second.end()) {
- Constraints = RefutationSolver->mkOr(
- Constraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
- RangeIt->From(), RangeIt->To(),
- /*InRange=*/true));
+ SMTConstraints = RefutationSolver->mkOr(
+ SMTConstraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
+ RangeIt->From(), RangeIt->To(),
+ /*InRange=*/true));
}
- RefutationSolver->addConstraint(Constraints);
+ RefutationSolver->addConstraint(SMTConstraints);
}
// And check for satisfiability
- Optional<bool> isSat = RefutationSolver->check();
- if (!isSat.hasValue())
+ Optional<bool> IsSAT = RefutationSolver->check();
+ if (!IsSAT.hasValue())
return;
- if (!isSat.getValue())
+ if (!IsSAT.getValue())
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
}
-PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
+void FalsePositiveRefutationBRVisitor::addConstraints(
+ const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) {
// Collect new constraints
const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
ConstraintRangeTy::Factory &CF =
@@ -2865,10 +2863,19 @@ PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
for (auto const &C : NewCs) {
const SymbolRef &Sym = C.first;
if (!Constraints.contains(Sym)) {
+ // This symbol is new, just add the constraint.
+ Constraints = CF.add(Constraints, Sym, C.second);
+ } else if (OverwriteConstraintsOnExistingSyms) {
+ // Overwrite the associated constraint of the Symbol.
+ Constraints = CF.remove(Constraints, Sym);
Constraints = CF.add(Constraints, Sym, C.second);
}
}
+}
+PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
+ addConstraints(N, /*OverwriteConstraintsOnExistingSyms=*/false);
return nullptr;
}
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 168d6fe6ec48..78d13ddfb773 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -172,23 +172,9 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
if (!D)
return nullptr;
- // TODO: For now we skip functions without definitions, even if we have
- // our own getDecl(), because it's hard to find out which re-declaration
- // is going to be used, and usually clients don't really care about this
- // situation because there's a loss of precision anyway because we cannot
- // inline the call.
- RuntimeDefinition RD = getRuntimeDefinition();
- if (!RD.getDecl())
- return nullptr;
-
AnalysisDeclContext *ADC =
LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
- // TODO: For now we skip virtual functions, because this also rises
- // the problem of which decl to use, but now it's across different classes.
- if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
- return nullptr;
-
return ADC;
}
@@ -222,39 +208,17 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
}
-const VarRegion *CallEvent::getParameterLocation(unsigned Index,
- unsigned BlockCount) const {
+const ParamVarRegion
+*CallEvent::getParameterLocation(unsigned Index, unsigned BlockCount) const {
const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;
- // Retrieve parameters of the definition, which are different from
- // CallEvent's parameters() because getDecl() isn't necessarily
- // the definition. SFC contains the definition that would be used
- // during analysis.
- const Decl *D = SFC->getDecl();
-
- // TODO: Refactor into a virtual method of CallEvent, like parameters().
- const ParmVarDecl *PVD = nullptr;
- if (const auto *FD = dyn_cast<FunctionDecl>(D))
- PVD = FD->parameters()[Index];
- else if (const auto *BD = dyn_cast<BlockDecl>(D))
- PVD = BD->parameters()[Index];
- else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
- PVD = MD->parameters()[Index];
- else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
- PVD = CD->parameters()[Index];
- assert(PVD && "Unexpected Decl kind!");
-
- const VarRegion *VR =
- State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
-
- // This sanity check would fail if our parameter declaration doesn't
- // correspond to the stack frame's function declaration.
- assert(VR->getStackFrame() == SFC);
-
- return VR;
+ const ParamVarRegion *PVR =
+ State->getStateManager().getRegionManager().getParamVarRegion(
+ getOriginExpr(), Index, SFC);
+ return PVR;
}
/// Returns true if a type is a pointer-to-const or reference-to-const
@@ -325,8 +289,9 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
if (getKind() != CE_CXXAllocator)
if (isArgumentConstructedDirectly(Idx))
if (auto AdjIdx = getAdjustedParameterIndex(Idx))
- if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
- ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
+ if (const TypedValueRegion *TVR =
+ getParameterLocation(*AdjIdx, BlockCount))
+ ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
}
// Invalidate designated regions using the batch invalidation API.
@@ -450,8 +415,7 @@ void CallEvent::dump(raw_ostream &Out) const {
return;
}
- // FIXME: a string representation of the kind would be nice.
- Out << "Unknown call (type " << getKind() << ")";
+ Out << "Unknown call (type " << getKindAsString() << ")";
}
bool CallEvent::isCallStmt(const Stmt *S) {
@@ -515,8 +479,7 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
unsigned Idx = 0;
ArrayRef<ParmVarDecl*>::iterator I = parameters.begin(), E = parameters.end();
for (; I != E && Idx < NumArgs; ++I, ++Idx) {
- const ParmVarDecl *ParamDecl = *I;
- assert(ParamDecl && "Formal parameter has no decl?");
+ assert(*I && "Formal parameter has no decl?");
// TODO: Support allocator calls.
if (Call.getKind() != CE_CXXAllocator)
@@ -528,7 +491,8 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// which makes getArgSVal() fail and return UnknownVal.
SVal ArgVal = Call.getArgSVal(Idx);
if (!ArgVal.isUnknown()) {
- Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Loc ParamLoc = SVB.makeLoc(
+ MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
}
}
@@ -536,6 +500,37 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// FIXME: Variadic arguments are not handled at all right now.
}
+const ConstructionContext *CallEvent::getConstructionContext() const {
+ const StackFrameContext *StackFrame = getCalleeStackFrame(0);
+ if (!StackFrame)
+ return nullptr;
+
+ const CFGElement Element = StackFrame->getCallSiteCFGElement();
+ if (const auto Ctor = Element.getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = Element.getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction() const {
+ const auto *CC = getConstructionContext();
+ if (!CC)
+ return None;
+
+ EvalCallOptions CallOpts;
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
+ SVal RetVal =
+ Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
+ getLocationContext(), CC, CallOpts);
+ return RetVal;
+}
+
ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
@@ -565,7 +560,7 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
return RuntimeDefinition(Decl);
}
- SubEngine &Engine = getState()->getStateManager().getOwningEngine();
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
AnalyzerOptions &Opts = Engine.getAnalysisManager().options;
// Try to get CTU definition only if CTUDir is provided.
@@ -889,24 +884,22 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
Params);
}
-SVal CXXConstructorCall::getCXXThisVal() const {
+SVal AnyCXXConstructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
return UnknownVal();
}
-void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
+void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
- if (Data) {
- loc::MemRegionVal MV(static_cast<const MemRegion *>(Data));
- if (SymbolRef Sym = MV.getAsSymbol(true))
- ETraits->setTrait(Sym,
- RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
- Values.push_back(MV);
- }
+ SVal V = getCXXThisVal();
+ if (SymbolRef Sym = V.getAsSymbol(true))
+ ETraits->setTrait(Sym,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(V);
}
-void CXXConstructorCall::getInitialStackFrameContents(
+void AnyCXXConstructorCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
@@ -920,6 +913,14 @@ void CXXConstructorCall::getInitialStackFrameContents(
}
}
+const StackFrameContext *
+CXXInheritedConstructorCall::getInheritingStackFrame() const {
+ const StackFrameContext *SFC = getLocationContext()->getStackFrame();
+ while (isa<CXXInheritedCtorInitExpr>(SFC->getCallSite()))
+ SFC = SFC->getParent()->getStackFrame();
+ return SFC;
+}
+
SVal CXXDestructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
@@ -967,14 +968,6 @@ void ObjCMethodCall::getExtraInvalidatedValues(
Values.push_back(getReceiverSVal());
}
-SVal ObjCMethodCall::getSelfSVal() const {
- const LocationContext *LCtx = getLocationContext();
- const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
- if (!SelfDecl)
- return SVal();
- return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
-}
-
SVal ObjCMethodCall::getReceiverSVal() const {
// FIXME: Is this the best way to handle class receivers?
if (!isInstanceMessage())
@@ -986,7 +979,7 @@ SVal ObjCMethodCall::getReceiverSVal() const {
// An instance message with no expression means we are sending to super.
// In this case the object reference is the same as 'self'.
assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
- SVal SelfVal = getSelfSVal();
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
assert(SelfVal.isValid() && "Calling super but not in ObjC method");
return SelfVal;
}
@@ -1000,8 +993,9 @@ bool ObjCMethodCall::isReceiverSelfOrSuper() const {
return false;
SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
- return (RecVal == getSelfSVal());
+ return (RecVal == SelfVal);
}
SourceRange ObjCMethodCall::getSourceRange() const {
@@ -1168,23 +1162,77 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
return MD;
}
-static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
- const Expr* InstRec = ME->getInstanceReceiver();
- if (!InstRec)
- return false;
- const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+struct PrivateMethodKey {
+ const ObjCInterfaceDecl *Interface;
+ Selector LookupSelector;
+ bool IsClassMethod;
+};
- // Check that receiver is called 'self'.
- if (!InstRecIg || !InstRecIg->getFoundDecl() ||
- !InstRecIg->getFoundDecl()->getName().equals("self"))
- return false;
+namespace llvm {
+template <> struct DenseMapInfo<PrivateMethodKey> {
+ using InterfaceInfo = DenseMapInfo<const ObjCInterfaceDecl *>;
+ using SelectorInfo = DenseMapInfo<Selector>;
- // Check that the method name is 'class'.
- if (ME->getSelector().getNumArgs() != 0 ||
- !ME->getSelector().getNameForSlot(0).equals("class"))
- return false;
+ static inline PrivateMethodKey getEmptyKey() {
+ return {InterfaceInfo::getEmptyKey(), SelectorInfo::getEmptyKey(), false};
+ }
- return true;
+ static inline PrivateMethodKey getTombstoneKey() {
+ return {InterfaceInfo::getTombstoneKey(), SelectorInfo::getTombstoneKey(),
+ true};
+ }
+
+ static unsigned getHashValue(const PrivateMethodKey &Key) {
+ return llvm::hash_combine(
+ llvm::hash_code(InterfaceInfo::getHashValue(Key.Interface)),
+ llvm::hash_code(SelectorInfo::getHashValue(Key.LookupSelector)),
+ Key.IsClassMethod);
+ }
+
+ static bool isEqual(const PrivateMethodKey &LHS,
+ const PrivateMethodKey &RHS) {
+ return InterfaceInfo::isEqual(LHS.Interface, RHS.Interface) &&
+ SelectorInfo::isEqual(LHS.LookupSelector, RHS.LookupSelector) &&
+ LHS.IsClassMethod == RHS.IsClassMethod;
+ }
+};
+} // end namespace llvm
+
+static const ObjCMethodDecl *
+lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
+ Selector LookupSelector, bool InstanceMethod) {
+ // Repeatedly calling lookupPrivateMethod() is expensive, especially
+ // when in many cases it returns null. We cache the results so
+ // that repeated queries on the same ObjCIntefaceDecl and Selector
+ // don't incur the same cost. On some test cases, we can see the
+ // same query being issued thousands of times.
+ //
+ // NOTE: This cache is essentially a "global" variable, but it
+ // only gets lazily created when we get here. The value of the
+ // cache probably comes from it being global across ExprEngines,
+ // where the same queries may get issued. If we are worried about
+ // concurrency, or possibly loading/unloading ASTs, etc., we may
+ // need to revisit this someday. In terms of memory, this table
+ // stays around until clang quits, which also may be bad if we
+ // need to release memory.
+ using PrivateMethodCache =
+ llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+
+ static PrivateMethodCache PMC;
+ Optional<const ObjCMethodDecl *> &Val =
+ PMC[{Interface, LookupSelector, InstanceMethod}];
+
+ // Query lookupPrivateMethod() if the cache does not hit.
+ if (!Val.hasValue()) {
+ Val = Interface->lookupPrivateMethod(LookupSelector, InstanceMethod);
+
+ if (!*Val) {
+ // Query 'lookupMethod' as a backup.
+ Val = Interface->lookupMethod(LookupSelector, InstanceMethod);
+ }
+ }
+
+ return Val.getValue();
}
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
@@ -1194,8 +1242,9 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
if (E->isInstanceMessage()) {
// Find the receiver type.
- const ObjCObjectPointerType *ReceiverT = nullptr;
+ const ObjCObjectType *ReceiverT = nullptr;
bool CanBeSubClassed = false;
+ bool LookingForInstanceMethod = true;
QualType SupersType = E->getSuperType();
const MemRegion *Receiver = nullptr;
@@ -1203,7 +1252,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
// The receiver is guaranteed to be 'super' in this case.
// Super always means the type of immediate predecessor to the method
// where the call occurs.
- ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType)->getObjectType();
} else {
Receiver = getReceiverSVal().getAsRegion();
if (!Receiver)
@@ -1218,100 +1267,59 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
QualType DynType = DTI.getType();
CanBeSubClassed = DTI.canBeASubClass();
- ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
- if (ReceiverT && CanBeSubClassed)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
- if (!canBeOverridenInSubclass(IDecl, Sel))
- CanBeSubClassed = false;
- }
+ const auto *ReceiverDynT =
+ dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
+
+ if (ReceiverDynT) {
+ ReceiverT = ReceiverDynT->getObjectType();
- // Handle special cases of '[self classMethod]' and
- // '[[self class] classMethod]', which are treated by the compiler as
- // instance (not class) messages. We will statically dispatch to those.
- if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
- // For [self classMethod], return the compiler visible declaration.
- if (PT->getObjectType()->isObjCClass() &&
- Receiver == getSelfSVal().getAsRegion())
- return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
-
- // Similarly, handle [[self class] classMethod].
- // TODO: We are currently doing a syntactic match for this pattern with is
- // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
- // shows. A better way would be to associate the meta type with the symbol
- // using the dynamic type info tracking and use it here. We can add a new
- // SVal for ObjC 'Class' values that know what interface declaration they
- // come from. Then 'self' in a class method would be filled in with
- // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
- // do proper dynamic dispatch for class methods just like we do for
- // instance methods now.
- if (E->getInstanceReceiver())
- if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
- if (isCallToSelfClass(M))
+ // It can be actually class methods called with Class object as a
+ // receiver. This type of messages is treated by the compiler as
+ // instance (not class).
+ if (ReceiverT->isObjCClass()) {
+
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
+ // For [self classMethod], return compiler visible declaration.
+ if (Receiver == SelfVal.getAsRegion()) {
return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Otherwise, let's check if we know something about the type
+ // inside of this class object.
+ if (SymbolRef ReceiverSym = getReceiverSVal().getAsSymbol()) {
+ DynamicTypeInfo DTI =
+ getClassObjectDynamicTypeInfo(getState(), ReceiverSym);
+ if (DTI.isValid()) {
+ // Let's use this type for lookup.
+ ReceiverT =
+ cast<ObjCObjectType>(DTI.getType().getCanonicalType());
+
+ CanBeSubClassed = DTI.canBeASubClass();
+ // And it should be a class method instead.
+ LookingForInstanceMethod = false;
+ }
+ }
+ }
+
+ if (CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface())
+ // Even if `DynamicTypeInfo` told us that it can be
+ // not necessarily this type, but its descendants, we still want
+ // to check again if this selector can be actually overridden.
+ CanBeSubClassed = canBeOverridenInSubclass(IDecl, Sel);
+ }
}
// Lookup the instance method implementation.
if (ReceiverT)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
- // Repeatedly calling lookupPrivateMethod() is expensive, especially
- // when in many cases it returns null. We cache the results so
- // that repeated queries on the same ObjCIntefaceDecl and Selector
- // don't incur the same cost. On some test cases, we can see the
- // same query being issued thousands of times.
- //
- // NOTE: This cache is essentially a "global" variable, but it
- // only gets lazily created when we get here. The value of the
- // cache probably comes from it being global across ExprEngines,
- // where the same queries may get issued. If we are worried about
- // concurrency, or possibly loading/unloading ASTs, etc., we may
- // need to revisit this someday. In terms of memory, this table
- // stays around until clang quits, which also may be bad if we
- // need to release memory.
- using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>;
- using PrivateMethodCache =
- llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
-
- static PrivateMethodCache PMC;
- Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)];
-
- // Query lookupPrivateMethod() if the cache does not hit.
- if (!Val.hasValue()) {
- Val = IDecl->lookupPrivateMethod(Sel);
-
- // If the method is a property accessor, we should try to "inline" it
- // even if we don't actually have an implementation.
- if (!*Val)
- if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl())
- if (CompileTimeMD->isPropertyAccessor()) {
- if (!CompileTimeMD->getSelfDecl() &&
- isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) {
- // If the method is an accessor in a category, and it doesn't
- // have a self declaration, first
- // try to find the method in a class extension. This
- // works around a bug in Sema where multiple accessors
- // are synthesized for properties in class
- // extensions that are redeclared in a category and the
- // the implicit parameters are not filled in for
- // the method on the category.
- // This ensures we find the accessor in the extension, which
- // has the implicit parameters filled in.
- auto *ID = CompileTimeMD->getClassInterface();
- for (auto *CatDecl : ID->visible_extensions()) {
- Val = CatDecl->getMethod(Sel,
- CompileTimeMD->isInstanceMethod());
- if (*Val)
- break;
- }
- }
- if (!*Val)
- Val = IDecl->lookupInstanceMethod(Sel);
- }
- }
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface()) {
+ const ObjCMethodDecl *MD =
+ lookupRuntimeDefinition(IDecl, Sel, LookingForInstanceMethod);
- const ObjCMethodDecl *MD = Val.getValue();
if (MD && !MD->hasBody())
MD = MD->getCanonicalDecl();
+
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else
@@ -1392,17 +1400,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
return Out;
- // All other cases are handled by getCall.
- assert(isa<CXXConstructExpr>(CallSite) &&
- "This is not an inlineable statement");
-
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
- return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
- ThisVal.getAsRegion(), State, CallerCtx);
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
+ return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
+ else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
+ return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
+ CallerCtx);
+ else {
+ // All other cases are handled by getCall.
+ llvm_unreachable("This is not an inlineable statement");
+ }
}
// Fall back to the CFG. The only thing we haven't handled yet is
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index 11693132de68..cae728815b41 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+#include "clang/Lex/Preprocessor.h"
namespace clang {
@@ -109,6 +110,43 @@ Nullability getNullabilityAnnotation(QualType Type) {
return Nullability::Unspecified;
}
+llvm::Optional<int> tryExpandAsInteger(StringRef Macro,
+ const Preprocessor &PP) {
+ const auto *MacroII = PP.getIdentifierInfo(Macro);
+ if (!MacroII)
+ return llvm::None;
+ const MacroInfo *MI = PP.getMacroInfo(MacroII);
+ if (!MI)
+ return llvm::None;
+
+ // Filter out parens.
+ std::vector<Token> FilteredTokens;
+ FilteredTokens.reserve(MI->tokens().size());
+ for (auto &T : MI->tokens())
+ if (!T.isOneOf(tok::l_paren, tok::r_paren))
+ FilteredTokens.push_back(T);
+
+ // Parse an integer at the end of the macro definition.
+ const Token &T = FilteredTokens.back();
+ // 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;
+ StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength());
+ llvm::APInt IntValue;
+ constexpr unsigned AutoSenseRadix = 0;
+ if (ValueStr.getAsInteger(AutoSenseRadix, IntValue))
+ return llvm::None;
+
+ // Parse an optional minus sign.
+ size_t Size = FilteredTokens.size();
+ if (Size >= 2) {
+ if (FilteredTokens[Size - 2].is(tok::minus))
+ IntValue = -IntValue;
+ }
+
+ return IntValue.getSExtValue();
+}
-} // end namespace ento
-} // end namespace clang
+} // namespace ento
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index a9361837cf68..86cecf6524f0 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -61,12 +61,12 @@ void CheckerManager::finishedCheckerRegistration() {
}
void CheckerManager::reportInvalidCheckerOptionValue(
- const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) {
+ const CheckerBase *C, StringRef OptionName,
+ StringRef ExpectedValueDesc) const {
- Context.getDiagnostics()
- .Report(diag::err_analyzer_checker_option_invalid_input)
- << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
- << ExpectedValueDesc;
+ getDiagnostics().Report(diag::err_analyzer_checker_option_invalid_input)
+ << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
+ << ExpectedValueDesc;
}
//===----------------------------------------------------------------------===//
@@ -243,13 +243,13 @@ void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
const ObjCMethodCall &msg,
ExprEngine &Eng,
bool WasInlined) {
- auto &checkers = getObjCMessageCheckers(visitKind);
+ const auto &checkers = getObjCMessageCheckers(visitKind);
CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined);
expandGraphWithCheckers(C, Dst, Src);
}
const std::vector<CheckerManager::CheckObjCMessageFunc> &
-CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) {
+CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) const {
switch (Kind) {
case ObjCMessageVisitKind::Pre:
return PreObjCMessageCheckers;
@@ -507,35 +507,38 @@ namespace {
using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
const CheckersTy &Checkers;
- const CXXNewExpr *NE;
- SVal Target;
+ const CXXAllocatorCall &Call;
bool WasInlined;
ExprEngine &Eng;
- CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE,
- SVal Target, bool WasInlined, ExprEngine &Eng)
- : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined),
- Eng(Eng) {}
+ CheckNewAllocatorContext(const CheckersTy &Checkers,
+ const CXXAllocatorCall &Call, bool WasInlined,
+ ExprEngine &Eng)
+ : Checkers(Checkers), Call(Call), WasInlined(WasInlined), Eng(Eng) {}
CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
- ProgramPoint L = PostAllocatorCall(NE, Pred->getLocationContext());
+ ProgramPoint L =
+ PostAllocatorCall(Call.getOriginExpr(), Pred->getLocationContext());
CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
- checkFn(NE, Target, C);
+ checkFn(cast<CXXAllocatorCall>(*Call.cloneWithState(Pred->getState())),
+ C);
}
};
} // namespace
-void CheckerManager::runCheckersForNewAllocator(
- const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
- ExprEngine &Eng, bool WasInlined) {
+void CheckerManager::runCheckersForNewAllocator(const CXXAllocatorCall &Call,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ bool WasInlined) {
ExplodedNodeSet Src;
Src.insert(Pred);
- CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, Call, WasInlined, Eng);
expandGraphWithCheckers(C, Dst, Src);
}
@@ -650,8 +653,9 @@ CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call,
- ExprEngine &Eng) {
- for (const auto Pred : Src) {
+ ExprEngine &Eng,
+ const EvalCallOptions &CallOpts) {
+ for (auto *const Pred : Src) {
bool anyEvaluated = false;
ExplodedNodeSet checkDst;
@@ -662,10 +666,8 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// TODO: Support the situation when the call doesn't correspond
// to any Expr.
ProgramPoint L = ProgramPoint::getProgramPoint(
- cast<CallExpr>(Call.getOriginExpr()),
- ProgramPoint::PostStmtKind,
- Pred->getLocationContext(),
- EvalCallChecker.Checker);
+ Call.getOriginExpr(), ProgramPoint::PostStmtKind,
+ Pred->getLocationContext(), EvalCallChecker.Checker);
bool evaluated = false;
{ // CheckerContext generates transitions(populates checkDest) on
// destruction, so introduce the scope to make sure it gets properly
@@ -687,7 +689,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!anyEvaluated) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
- Eng.defaultEvalCall(B, Pred, Call);
+ Eng.defaultEvalCall(B, Pred, Call, CallOpts);
}
}
}
@@ -902,8 +904,3 @@ CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
Checkers.push_back(Info.CheckFn);
return Checkers;
}
-
-CheckerManager::~CheckerManager() {
- for (const auto &CheckerDtor : CheckerDtors)
- CheckerDtor();
-}
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp b/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
new file mode 100644
index 000000000000..1b3e8b11549d
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/CheckerRegistryData.cpp
@@ -0,0 +1,241 @@
+//===- CheckerRegistry.h - Maintains all available checkers -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerRegistryData.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "llvm/ADT/Twine.h"
+#include <map>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Methods of CmdLineOption, PackageInfo and CheckerInfo.
+//===----------------------------------------------------------------------===//
+
+LLVM_DUMP_METHOD void CmdLineOption::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+LLVM_DUMP_METHOD void
+CmdLineOption::dumpToStream(llvm::raw_ostream &Out) const {
+ // The description can be just checked in Checkers.inc, the point here is to
+ // debug whether we succeeded in parsing it.
+ Out << OptionName << " (" << OptionType << ", "
+ << (IsHidden ? "hidden, " : "") << DevelopmentStatus << ") default: \""
+ << DefaultValStr;
+}
+
+static StringRef toString(StateFromCmdLine Kind) {
+ switch (Kind) {
+ case StateFromCmdLine::State_Disabled:
+ return "Disabled";
+ case StateFromCmdLine::State_Enabled:
+ return "Enabled";
+ case StateFromCmdLine::State_Unspecified:
+ return "Unspecified";
+ }
+ llvm_unreachable("Unhandled StateFromCmdLine enum");
+}
+
+LLVM_DUMP_METHOD void CheckerInfo::dump() const { dumpToStream(llvm::errs()); }
+
+LLVM_DUMP_METHOD void CheckerInfo::dumpToStream(llvm::raw_ostream &Out) const {
+ // The description can be just checked in Checkers.inc, the point here is to
+ // debug whether we succeeded in parsing it. Same with documentation uri.
+ Out << FullName << " (" << toString(State) << (IsHidden ? ", hidden" : "")
+ << ")\n";
+ Out << " Options:\n";
+ for (const CmdLineOption &Option : CmdLineOptions) {
+ Out << " ";
+ Option.dumpToStream(Out);
+ Out << '\n';
+ }
+ Out << " Dependencies:\n";
+ for (const CheckerInfo *Dependency : Dependencies) {
+ Out << " " << Dependency->FullName << '\n';
+ }
+ Out << " Weak dependencies:\n";
+ for (const CheckerInfo *Dependency : WeakDependencies) {
+ Out << " " << Dependency->FullName << '\n';
+ }
+}
+
+LLVM_DUMP_METHOD void PackageInfo::dump() const { dumpToStream(llvm::errs()); }
+
+LLVM_DUMP_METHOD void PackageInfo::dumpToStream(llvm::raw_ostream &Out) const {
+ Out << FullName << "\n";
+ Out << " Options:\n";
+ for (const CmdLineOption &Option : CmdLineOptions) {
+ Out << " ";
+ Option.dumpToStream(Out);
+ Out << '\n';
+ }
+}
+
+static constexpr char PackageSeparator = '.';
+
+static bool isInPackage(const CheckerInfo &Checker, StringRef PackageName) {
+ // Does the checker's full name have the package as a prefix?
+ if (!Checker.FullName.startswith(PackageName))
+ return false;
+
+ // Is the package actually just the name of a specific checker?
+ if (Checker.FullName.size() == PackageName.size())
+ return true;
+
+ // Is the checker in the package (or a subpackage)?
+ if (Checker.FullName[PackageName.size()] == PackageSeparator)
+ return true;
+
+ return false;
+}
+
+CheckerInfoListRange
+CheckerRegistryData::getMutableCheckersForCmdLineArg(StringRef CmdLineArg) {
+ auto It = checker_registry::binaryFind(Checkers, CmdLineArg);
+
+ if (!isInPackage(*It, CmdLineArg))
+ return {Checkers.end(), Checkers.end()};
+
+ // See how large the package is.
+ // If the package doesn't exist, assume the option refers to a single
+ // checker.
+ size_t Size = 1;
+ llvm::StringMap<size_t>::const_iterator PackageSize =
+ PackageSizes.find(CmdLineArg);
+
+ if (PackageSize != PackageSizes.end())
+ Size = PackageSize->getValue();
+
+ return {It, It + Size};
+}
+//===----------------------------------------------------------------------===//
+// Printing functions.
+//===----------------------------------------------------------------------===//
+
+void CheckerRegistryData::printCheckerWithDescList(
+ const AnalyzerOptions &AnOpts, raw_ostream &Out,
+ size_t MaxNameChars) const {
+ // FIXME: Print available packages.
+
+ Out << "CHECKERS:\n";
+
+ // Find the maximum option length.
+ size_t OptionFieldWidth = 0;
+ for (const auto &Checker : Checkers) {
+ // Limit the amount of padding we are willing to give up for alignment.
+ // Package.Name Description [Hidden]
+ size_t NameLength = Checker.FullName.size();
+ if (NameLength <= MaxNameChars)
+ OptionFieldWidth = std::max(OptionFieldWidth, NameLength);
+ }
+
+ const size_t InitialPad = 2;
+
+ auto Print = [=](llvm::raw_ostream &Out, const CheckerInfo &Checker,
+ StringRef Description) {
+ AnalyzerOptions::printFormattedEntry(Out, {Checker.FullName, Description},
+ InitialPad, OptionFieldWidth);
+ Out << '\n';
+ };
+
+ for (const auto &Checker : Checkers) {
+ // The order of this if branches is significant, we wouldn't like to display
+ // developer checkers even in the alpha output. For example,
+ // alpha.cplusplus.IteratorModeling is a modeling checker, hence it's hidden
+ // by default, and users (even when the user is a developer of an alpha
+ // checker) shouldn't normally tinker with whether they should be enabled.
+
+ if (Checker.IsHidden) {
+ if (AnOpts.ShowCheckerHelpDeveloper)
+ Print(Out, Checker, Checker.Desc);
+ continue;
+ }
+
+ if (Checker.FullName.startswith("alpha")) {
+ if (AnOpts.ShowCheckerHelpAlpha)
+ Print(Out, Checker,
+ ("(Enable only for development!) " + Checker.Desc).str());
+ continue;
+ }
+
+ if (AnOpts.ShowCheckerHelp)
+ Print(Out, Checker, Checker.Desc);
+ }
+}
+
+void CheckerRegistryData::printEnabledCheckerList(raw_ostream &Out) const {
+ for (const auto *i : EnabledCheckers)
+ Out << i->FullName << '\n';
+}
+
+void CheckerRegistryData::printCheckerOptionList(const AnalyzerOptions &AnOpts,
+ raw_ostream &Out) const {
+ Out << "OVERVIEW: Clang Static Analyzer Checker and Package Option List\n\n";
+ Out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
+ Out << " -analyzer-config OPTION1=VALUE, -analyzer-config "
+ "OPTION2=VALUE, ...\n\n";
+ Out << "OPTIONS:\n\n";
+
+ // It's usually ill-advised to use multimap, but clang will terminate after
+ // this function.
+ std::multimap<StringRef, const CmdLineOption &> OptionMap;
+
+ for (const CheckerInfo &Checker : Checkers) {
+ for (const CmdLineOption &Option : Checker.CmdLineOptions) {
+ OptionMap.insert({Checker.FullName, Option});
+ }
+ }
+
+ for (const PackageInfo &Package : Packages) {
+ for (const CmdLineOption &Option : Package.CmdLineOptions) {
+ OptionMap.insert({Package.FullName, Option});
+ }
+ }
+
+ auto Print = [](llvm::raw_ostream &Out, StringRef FullOption,
+ StringRef Desc) {
+ AnalyzerOptions::printFormattedEntry(Out, {FullOption, Desc},
+ /*InitialPad*/ 2,
+ /*EntryWidth*/ 50,
+ /*MinLineWidth*/ 90);
+ Out << "\n\n";
+ };
+ for (const std::pair<const StringRef, const CmdLineOption &> &Entry :
+ OptionMap) {
+ const CmdLineOption &Option = Entry.second;
+ std::string FullOption = (Entry.first + ":" + Option.OptionName).str();
+
+ std::string Desc =
+ ("(" + Option.OptionType + ") " + Option.Description + " (default: " +
+ (Option.DefaultValStr.empty() ? "\"\"" : Option.DefaultValStr) + ")")
+ .str();
+
+ // The list of these if branches is significant, we wouldn't like to
+ // display hidden alpha checker options for
+ // -analyzer-checker-option-help-alpha.
+
+ if (Option.IsHidden) {
+ if (AnOpts.ShowCheckerOptionDeveloperList)
+ Print(Out, FullOption, Desc);
+ continue;
+ }
+
+ if (Option.DevelopmentStatus == "alpha" ||
+ Entry.first.startswith("alpha")) {
+ if (AnOpts.ShowCheckerOptionAlphaList)
+ Print(Out, FullOption,
+ llvm::Twine("(Enable only for development!) " + Desc).str());
+ continue;
+ }
+
+ if (AnOpts.ShowCheckerOptionList)
+ Print(Out, FullOption, Desc);
+ }
+}
diff --git a/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
index bdae3e605eff..a601370775b4 100644
--- a/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -9,13 +9,18 @@
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
// Common strings used for the "category" of many static analyzer issues.
-namespace clang { namespace ento { namespace categories {
+namespace clang {
+namespace ento {
+namespace categories {
-const char * const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
-const char * const LogicError = "Logic error";
-const char * const MemoryRefCount =
- "Memory (Core Foundation/Objective-C/OSObject)";
-const char * const MemoryError = "Memory error";
-const char * const UnixAPI = "Unix API";
-const char * const CXXObjectLifecycle = "C++ object lifecycle";
-}}}
+const char *const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
+const char *const LogicError = "Logic error";
+const char *const MemoryRefCount =
+ "Memory (Core Foundation/Objective-C/OSObject)";
+const char *const MemoryError = "Memory error";
+const char *const UnixAPI = "Unix API";
+const char *const CXXObjectLifecycle = "C++ object lifecycle";
+const char *const SecurityError = "Security error";
+} // namespace categories
+} // namespace ento
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 94cf74de8293..70deb13a8e1a 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -23,8 +23,8 @@
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
@@ -52,8 +52,7 @@ STATISTIC(NumPathsExplored,
// Core analysis engine.
//===----------------------------------------------------------------------===//
-static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts,
- SubEngine &subengine) {
+static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) {
switch (Opts.getExplorationStrategy()) {
case ExplorationStrategyKind::DFS:
return WorkList::makeDFS();
@@ -71,9 +70,9 @@ static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts,
llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind");
}
-CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS,
+CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS,
AnalyzerOptions &Opts)
- : SubEng(subengine), WList(generateWorkList(Opts, subengine)),
+ : ExprEng(exprengine), WList(generateWorkList(Opts)),
BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
@@ -104,7 +103,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
if (!InitState)
- InitState = SubEng.getInitialState(L);
+ InitState = ExprEng.getInitialState(L);
bool IsNew;
ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
@@ -113,7 +112,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
ExplodedNodeSet DstBegin;
- SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
+ ExprEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
enqueue(DstBegin);
}
@@ -147,7 +146,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
dispatchWorkItem(Node, Node->getLocation(), WU);
}
- SubEng.processEndWorklist();
+ ExprEng.processEndWorklist();
return WList->hasWork();
}
@@ -172,7 +171,7 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
case ProgramPoint::CallExitBeginKind:
- SubEng.processCallExit(Pred);
+ ExprEng.processCallExit(Pred);
break;
case ProgramPoint::EpsilonKind: {
@@ -221,7 +220,7 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
if (L.getSrc()->getTerminator().isVirtualBaseBranch() &&
L.getDst() == *L.getSrc()->succ_begin()) {
ProgramPoint P = L.withTag(getNoteTags().makeNoteTag(
- [](BugReporterContext &, BugReport &) -> std::string {
+ [](BugReporterContext &, PathSensitiveBugReport &) -> std::string {
// TODO: Just call out the name of the most derived class
// when we know it.
return "Virtual base initialization skipped because "
@@ -253,17 +252,17 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
}
// Process the final state transition.
- SubEng.processEndOfFunction(BuilderCtx, Pred, RS);
+ ExprEng.processEndOfFunction(BuilderCtx, Pred, RS);
// This path is done. Don't enqueue any more nodes.
return;
}
- // Call into the SubEngine to process entering the CFGBlock.
+ // Call into the ExprEngine to process entering the CFGBlock.
ExplodedNodeSet dstNodes;
BlockEntrance BE(Blk, Pred->getLocationContext());
NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
- SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
+ ExprEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
// Auto-generate a node.
if (!nodeBuilder.hasGeneratedNodes()) {
@@ -287,7 +286,7 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
// Process the entrance of the block.
if (Optional<CFGElement> E = L.getFirstElement()) {
NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
- SubEng.processCFGElement(*E, Pred, 0, &Ctx);
+ ExprEng.processCFGElement(*E, Pred, 0, &Ctx);
}
else
HandleBlockExit(L.getBlock(), Pred);
@@ -367,7 +366,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(),
*(B->succ_begin()), this);
- SubEng.processIndirectGoto(builder);
+ ExprEng.processIndirectGoto(builder);
return;
}
@@ -378,7 +377,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
// 'element' variable to a value.
// (2) in a terminator, which represents the branch.
//
- // For (1), subengines will bind a value (i.e., 0 or 1) indicating
+ // For (1), ExprEngine will bind a value (i.e., 0 or 1) indicating
// whether or not collection contains any more elements. We cannot
// just test to see if the element is nil because a container can
// contain nil elements.
@@ -389,7 +388,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(),
this);
- SubEng.processSwitch(builder);
+ ExprEng.processSwitch(builder);
return;
}
@@ -418,7 +417,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
- SubEng.processCallEnter(BuilderCtx, CE, Pred);
+ ExprEng.processCallEnter(BuilderCtx, CE, Pred);
}
void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
@@ -426,7 +425,7 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
+ ExprEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -438,7 +437,7 @@ void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
+ ExprEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
*(B->succ_begin() + 1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -449,7 +448,7 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
assert(B->succ_size() == 2);
NodeBuilderContext Ctx(*this, B, Pred);
ExplodedNodeSet Dst;
- SubEng.processStaticInitializer(DS, Ctx, Pred, Dst,
+ ExprEng.processStaticInitializer(DS, Ctx, Pred, Dst,
*(B->succ_begin()), *(B->succ_begin()+1));
// Enqueue the new frontier onto the worklist.
enqueue(Dst);
@@ -464,7 +463,7 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
HandleBlockExit(B, Pred);
else {
NodeBuilderContext Ctx(*this, B, Pred);
- SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
+ ExprEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
new file mode 100644
index 000000000000..8b2172db445c
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/DynamicSize.cpp
@@ -0,0 +1,71 @@
+//===- DynamicSize.cpp - Dynamic size related APIs --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs that track and query dynamic size information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+
+namespace clang {
+namespace ento {
+
+DefinedOrUnknownSVal getDynamicSize(ProgramStateRef State, const MemRegion *MR,
+ SValBuilder &SVB) {
+ return MR->getMemRegionManager().getStaticSize(MR, SVB);
+}
+
+DefinedOrUnknownSVal getDynamicElementCount(ProgramStateRef State,
+ const MemRegion *MR,
+ SValBuilder &SVB,
+ QualType ElementTy) {
+ MemRegionManager &MemMgr = MR->getMemRegionManager();
+ ASTContext &Ctx = MemMgr.getContext();
+
+ DefinedOrUnknownSVal Size = getDynamicSize(State, MR, SVB);
+ SVal ElementSizeV = SVB.makeIntVal(
+ Ctx.getTypeSizeInChars(ElementTy).getQuantity(), SVB.getArrayIndexType());
+
+ SVal DivisionV =
+ SVB.evalBinOp(State, BO_Div, Size, ElementSizeV, SVB.getArrayIndexType());
+
+ return DivisionV.castAs<DefinedOrUnknownSVal>();
+}
+
+SVal getDynamicSizeWithOffset(ProgramStateRef State, const SVal &BufV) {
+ SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder();
+ const MemRegion *MRegion = BufV.getAsRegion();
+ if (!MRegion)
+ return UnknownVal();
+ RegionOffset Offset = MRegion->getAsOffset();
+ if (Offset.hasSymbolicOffset())
+ return UnknownVal();
+ const MemRegion *BaseRegion = MRegion->getBaseRegion();
+ if (!BaseRegion)
+ return UnknownVal();
+
+ NonLoc OffsetInBytes = SvalBuilder.makeArrayIndex(
+ Offset.getOffset() /
+ MRegion->getMemRegionManager().getContext().getCharWidth());
+ DefinedOrUnknownSVal ExtentInBytes =
+ getDynamicSize(State, BaseRegion, SvalBuilder);
+
+ return SvalBuilder.evalBinOp(State, BinaryOperator::Opcode::BO_Sub,
+ ExtentInBytes, OffsetInBytes,
+ SvalBuilder.getArrayIndexType());
+}
+
+} // namespace ento
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Core/DynamicType.cpp b/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
index a78e0e05e903..e9b64fd79614 100644
--- a/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ b/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -34,6 +34,10 @@ REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
CastSet)
+// A map from Class object symbols to the most likely pointed-to type.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicClassObjectMap, clang::ento::SymbolRef,
+ clang::ento::DynamicTypeInfo)
+
namespace clang {
namespace ento {
@@ -76,6 +80,12 @@ const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
return nullptr;
}
+DynamicTypeInfo getClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym) {
+ const DynamicTypeInfo *DTI = State->get<DynamicClassObjectMap>(Sym);
+ return DTI ? *DTI : DynamicTypeInfo{};
+}
+
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
DynamicTypeInfo NewTy) {
State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
@@ -118,111 +128,165 @@ ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
return State;
}
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym,
+ DynamicTypeInfo NewTy) {
+ State = State->set<DynamicClassObjectMap>(Sym, NewTy);
+ return State;
+}
+
+ProgramStateRef setClassObjectDynamicTypeInfo(ProgramStateRef State,
+ SymbolRef Sym, QualType NewTy,
+ bool CanBeSubClassed) {
+ return setClassObjectDynamicTypeInfo(State, Sym,
+ DynamicTypeInfo(NewTy, CanBeSubClassed));
+}
+
+static bool isLive(SymbolReaper &SR, const MemRegion *MR) {
+ return SR.isLiveRegion(MR);
+}
+
+static bool isLive(SymbolReaper &SR, SymbolRef Sym) { return SR.isLive(Sym); }
+
template <typename MapTy>
-ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map,
- SymbolReaper &SR) {
+static ProgramStateRef removeDeadImpl(ProgramStateRef State, SymbolReaper &SR) {
+ const auto &Map = State->get<MapTy>();
+
for (const auto &Elem : Map)
- if (!SR.isLiveRegion(Elem.first))
- State = State->remove<DynamicCastMap>(Elem.first);
+ if (!isLive(SR, Elem.first))
+ State = State->remove<MapTy>(Elem.first);
return State;
}
ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
- return removeDead(State, State->get<DynamicTypeMap>(), SR);
+ return removeDeadImpl<DynamicTypeMap>(State, SR);
}
ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
- return removeDead(State, State->get<DynamicCastMap>(), SR);
+ return removeDeadImpl<DynamicCastMap>(State, SR);
}
-static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL, unsigned int Space,
- bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+ProgramStateRef removeDeadClassObjectTypes(ProgramStateRef State,
+ SymbolReaper &SR) {
+ return removeDeadImpl<DynamicClassObjectMap>(State, SR);
+}
- const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>();
- if (Map.isEmpty()) {
- Out << "null," << NL;
- return;
- }
+//===----------------------------------------------------------------------===//
+// Implementation of the 'printer-to-JSON' function
+//===----------------------------------------------------------------------===//
- ++Space;
- Out << '[' << NL;
- for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
- const MemRegion *MR = I->first;
- const DynamicTypeInfo &DTI = I->second;
- Indent(Out, Space, IsDot)
- << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
- if (!DTI.isValid()) {
- Out << "null";
- } else {
- Out << '\"' << DTI.getType()->getPointeeType().getAsString()
- << "\", \"sub_classable\": "
- << (DTI.canBeASubClass() ? "true" : "false");
- }
- Out << " }";
-
- if (std::next(I) != Map.end())
- Out << ',';
- Out << NL;
+static raw_ostream &printJson(const MemRegion *Region, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"region\": \"" << Region << "\"";
+}
+
+static raw_ostream &printJson(const SymExpr *Symbol, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"symbol\": \"" << Symbol << "\"";
+}
+
+static raw_ostream &printJson(const DynamicTypeInfo &DTI, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ Out << "\"dyn_type\": ";
+ if (!DTI.isValid()) {
+ Out << "null";
+ } else {
+ QualType ToPrint = DTI.getType();
+ if (ToPrint->isAnyPointerType())
+ ToPrint = ToPrint->getPointeeType();
+
+ Out << '\"' << ToPrint.getAsString() << "\", \"sub_classable\": "
+ << (DTI.canBeASubClass() ? "true" : "false");
}
+ return Out;
+}
- --Space;
- Indent(Out, Space, IsDot) << "]," << NL;
+static raw_ostream &printJson(const DynamicCastInfo &DCI, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ return Out << "\"from\": \"" << DCI.from().getAsString() << "\", \"to\": \""
+ << DCI.to().getAsString() << "\", \"kind\": \""
+ << (DCI.succeeds() ? "success" : "fail") << "\"";
}
-static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL, unsigned int Space,
- bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+template <class T, class U>
+static raw_ostream &printJson(const std::pair<T, U> &Pair, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ printJson(Pair.first, Out, NL, Space, IsDot) << ", ";
+ return printJson(Pair.second, Out, NL, Space, IsDot);
+}
- const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
- if (Map.isEmpty()) {
- Out << "null," << NL;
- return;
+template <class ContainerTy>
+static raw_ostream &printJsonContainer(const ContainerTy &Container,
+ raw_ostream &Out, const char *NL,
+ unsigned int Space, bool IsDot) {
+ if (Container.isEmpty()) {
+ return Out << "null";
}
++Space;
Out << '[' << NL;
- for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
- const MemRegion *MR = I->first;
- const CastSet &Set = I->second;
-
- Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": ";
- if (Set.isEmpty()) {
- Out << "null ";
- } else {
- ++Space;
- Out << '[' << NL;
- for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
- Indent(Out, Space, IsDot)
- << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
- << SI->to().getAsString() << "\", \"kind\": \""
- << (SI->succeeds() ? "success" : "fail") << "\" }";
-
- if (std::next(SI) != Set.end())
- Out << ',';
- Out << NL;
- }
- --Space;
- Indent(Out, Space, IsDot) << ']';
- }
- Out << '}';
-
- if (std::next(I) != Map.end())
+ for (auto I = Container.begin(); I != Container.end(); ++I) {
+ const auto &Element = *I;
+
+ Indent(Out, Space, IsDot) << "{ ";
+ printJson(Element, Out, NL, Space, IsDot) << " }";
+
+ if (std::next(I) != Container.end())
Out << ',';
Out << NL;
}
--Space;
- Indent(Out, Space, IsDot) << "]," << NL;
+ return Indent(Out, Space, IsDot) << "]";
+}
+
+static raw_ostream &printJson(const CastSet &Set, raw_ostream &Out,
+ const char *NL, unsigned int Space, bool IsDot) {
+ Out << "\"casts\": ";
+ return printJsonContainer(Set, Out, NL, Space, IsDot);
+}
+
+template <class MapTy>
+static void printJsonImpl(raw_ostream &Out, ProgramStateRef State,
+ const char *Name, const char *NL, unsigned int Space,
+ bool IsDot, bool PrintEvenIfEmpty = true) {
+ const auto &Map = State->get<MapTy>();
+ if (Map.isEmpty() && !PrintEvenIfEmpty)
+ return;
+
+ Indent(Out, Space, IsDot) << "\"" << Name << "\": ";
+ printJsonContainer(Map, Out, NL, Space, IsDot) << "," << NL;
+}
+
+static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ printJsonImpl<DynamicTypeMap>(Out, State, "dynamic_types", NL, Space, IsDot);
+}
+
+static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ printJsonImpl<DynamicCastMap>(Out, State, "dynamic_casts", NL, Space, IsDot);
+}
+
+static void printClassObjectDynamicTypesJson(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ // Let's print Class object type information only if we have something
+ // meaningful to print.
+ printJsonImpl<DynamicClassObjectMap>(Out, State, "class_object_types", NL,
+ Space, IsDot,
+ /*PrintEvenIfEmpty=*/false);
}
void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space, bool IsDot) {
printDynamicTypesJson(Out, State, NL, Space, IsDot);
printDynamicCastsJson(Out, State, NL, Space, IsDot);
+ printClassObjectDynamicTypesJson(Out, State, NL, Space, IsDot);
}
} // namespace ento
diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 1ccf4c6104a6..9e6d79bb7dcc 100644
--- a/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -183,12 +183,18 @@ EnvironmentManager::removeDeadBindings(Environment Env,
F.getTreeFactory());
// Iterate over the block-expr bindings.
- for (Environment::iterator I = Env.begin(), E = Env.end();
- I != E; ++I) {
+ for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
const SVal &X = I.getData();
- if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
+ const bool IsBlkExprLive =
+ SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
+
+ assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
+ "Only Exprs can be live, LivenessAnalysis argues about the liveness "
+ "of *values*!");
+
+ if (IsBlkExprLive) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index c4838492271c..635495e9bf60 100644
--- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -50,9 +50,8 @@ ExplodedGraph::~ExplodedGraph() = default;
bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
if (!Ex->isLValue())
return false;
- return isa<DeclRefExpr>(Ex) ||
- isa<MemberExpr>(Ex) ||
- isa<ObjCIvarRefExpr>(Ex);
+ return isa<DeclRefExpr>(Ex) || isa<MemberExpr>(Ex) ||
+ isa<ObjCIvarRefExpr>(Ex) || isa<ArraySubscriptExpr>(Ex);
}
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index f917a4c8637b..265dcd134213 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1210,9 +1210,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
switch (S->getStmtClass()) {
// C++, OpenMP and ARC stuff we don't support yet.
- case Expr::ObjCIndirectCopyRestoreExprClass:
case Stmt::CXXDependentScopeMemberExprClass:
- case Stmt::CXXInheritedCtorInitExprClass:
case Stmt::CXXTryStmtClass:
case Stmt::CXXTypeidExprClass:
case Stmt::CXXUuidofExprClass:
@@ -1226,6 +1224,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::UnresolvedLookupExprClass:
case Stmt::UnresolvedMemberExprClass:
case Stmt::TypoExprClass:
+ case Stmt::RecoveryExprClass:
case Stmt::CXXNoexceptExprClass:
case Stmt::PackExpansionExprClass:
case Stmt::SubstNonTypeTemplateParmPackExprClass:
@@ -1258,6 +1257,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTaskwaitDirectiveClass:
case Stmt::OMPTaskgroupDirectiveClass:
case Stmt::OMPFlushDirectiveClass:
+ case Stmt::OMPDepobjDirectiveClass:
+ case Stmt::OMPScanDirectiveClass:
case Stmt::OMPOrderedDirectiveClass:
case Stmt::OMPAtomicDirectiveClass:
case Stmt::OMPTargetDirectiveClass:
@@ -1386,6 +1387,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::AsTypeExprClass:
case Stmt::ConceptSpecializationExprClass:
case Stmt::CXXRewrittenBinaryOperatorClass:
+ case Stmt::RequiresExprClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
@@ -1410,6 +1412,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::SubstNonTypeTemplateParmExprClass:
case Stmt::CXXNullPtrLiteralExprClass:
case Stmt::OMPArraySectionExprClass:
+ case Stmt::OMPArrayShapingExprClass:
+ case Stmt::OMPIteratorExprClass:
case Stmt::TypeTraitExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet preVisit;
@@ -1510,6 +1514,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::MatrixSubscriptExprClass:
+ llvm_unreachable("Support for MatrixSubscriptExpr is not implemented.");
+ break;
+
case Stmt::GCCAsmStmtClass:
Bldr.takeNodes(Pred);
VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst);
@@ -1617,6 +1625,13 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
+ case Stmt::CXXInheritedCtorInitExprClass:
+ Bldr.takeNodes(Pred);
+ VisitCXXInheritedCtorInitExpr(cast<CXXInheritedCtorInitExpr>(S), Pred,
+ Dst);
+ Bldr.addNodes(Dst);
+ break;
+
case Stmt::CXXNewExprClass: {
Bldr.takeNodes(Pred);
@@ -1637,8 +1652,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet PreVisit;
const auto *CDE = cast<CXXDeleteExpr>(S);
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+ ExplodedNodeSet PostVisit;
+ getCheckerManager().runCheckersForPostStmt(PostVisit, PreVisit, S, *this);
- for (const auto i : PreVisit)
+ for (const auto i : PostVisit)
VisitCXXDeleteExpr(CDE, i, Dst);
Bldr.addNodes(Dst);
@@ -1704,7 +1721,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
case Stmt::BuiltinBitCastExprClass:
- case Stmt::ObjCBridgedCastExprClass: {
+ case Stmt::ObjCBridgedCastExprClass:
+ case Stmt::CXXAddrspaceCastExprClass: {
Bldr.takeNodes(Pred);
const auto *C = cast<CastExpr>(S);
ExplodedNodeSet dstExpr;
@@ -1851,6 +1869,21 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
}
+
+ case Expr::ObjCIndirectCopyRestoreExprClass: {
+ // ObjCIndirectCopyRestoreExpr implies passing a temporary for
+ // correctness of lifetime management. Due to limited analysis
+ // of ARC, this is implemented as direct arg passing.
+ Bldr.takeNodes(Pred);
+ ProgramStateRef state = Pred->getState();
+ const auto *OIE = cast<ObjCIndirectCopyRestoreExpr>(S);
+ const Expr *E = OIE->getSubExpr();
+ SVal V = state->getSVal(E, Pred->getLocationContext());
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(), V));
+ Bldr.addNodes(Dst);
+ break;
+ }
}
}
@@ -3160,11 +3193,13 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
return DumpGraph(Src, Filename);
} else {
return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
- /*Title=*/"Exploded Graph", /*Filename=*/Filename);
+ /*Title=*/"Exploded Graph",
+ /*Filename=*/std::string(Filename));
}
-#endif
+#else
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
return "";
+#endif
}
std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
@@ -3178,7 +3213,7 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
/*ShortNames=*/false,
/*Title=*/"Trimmed Exploded Graph",
- /*Filename=*/Filename);
+ /*Filename=*/std::string(Filename));
}
#endif
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
@@ -3189,3 +3224,5 @@ void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
static int index = 0;
return &index;
}
+
+void ExprEngine::anchor() { }
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index b17f26aa9c53..c5e38cc7423d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -218,7 +218,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
auto CE = BD->capture_end();
for (; I != E; ++I) {
const VarRegion *capturedR = I.getCapturedRegion();
- const VarRegion *originalR = I.getOriginalRegion();
+ const TypedValueRegion *originalR = I.getOriginalRegion();
// If the capture had a copy expression, use the result of evaluating
// that expression, otherwise use the original value.
@@ -573,6 +573,18 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ if (isa<TypedefNameDecl>(*DS->decl_begin())) {
+ // C99 6.7.7 "Any array size expressions associated with variable length
+ // array declarators are evaluated each time the declaration of the typedef
+ // name is reached in the order of execution."
+ // The checkers should know about typedef to be able to handle VLA size
+ // expressions.
+ ExplodedNodeSet DstPre;
+ getCheckerManager().runCheckersForPreStmt(DstPre, Pred, DS, *this);
+ getCheckerManager().runCheckersForPostStmt(Dst, DstPre, DS, *this);
+ return;
+ }
+
// Assumption: The CFG has one DeclStmt per Decl.
const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin());
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index b816aab7c18f..38a680eb04c0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -109,15 +109,14 @@ SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
return LValue;
}
-std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
+SVal ExprEngine::computeObjectUnderConstruction(
const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
const ConstructionContext *CC, EvalCallOptions &CallOpts) {
SValBuilder &SVB = getSValBuilder();
MemRegionManager &MRMgr = SVB.getRegionManager();
ASTContext &ACtx = SVB.getContext();
- // See if we're constructing an existing region by looking at the
- // current construction context.
+ // Compute the target region by exploring the construction context.
if (CC) {
switch (CC->getKind()) {
case ConstructionContext::CXX17ElidedCopyVariableKind:
@@ -125,13 +124,9 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *DSCC = cast<VariableConstructionContext>(CC);
const auto *DS = DSCC->getDeclStmt();
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
- SVal LValue = State->getLValue(Var, LCtx);
QualType Ty = Var->getType();
- LValue =
- makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
- State =
- addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);
- return std::make_pair(State, LValue);
+ return makeZeroElementRegion(State, State->getLValue(Var, LCtx), Ty,
+ CallOpts.IsArrayCtorOrDtor);
}
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
@@ -139,8 +134,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *Init = ICC->getCXXCtorInitializer();
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
- Loc ThisPtr =
- SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
+ Loc ThisPtr = SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
const ValueDecl *Field;
@@ -154,10 +148,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
}
QualType Ty = Field->getType();
- FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
- CallOpts.IsArrayCtorOrDtor);
- State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
- return std::make_pair(State, FieldVal);
+ return makeZeroElementRegion(State, FieldVal, Ty,
+ CallOpts.IsArrayCtorOrDtor);
}
case ConstructionContext::NewAllocatedObjectKind: {
if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
@@ -170,11 +162,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// TODO: In fact, we need to call the constructor for every
// allocated element, not just the first one!
CallOpts.IsArrayCtorOrDtor = true;
- return std::make_pair(
- State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
- MR, NE->getType()->getPointeeType())));
+ return loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType()));
}
- return std::make_pair(State, V);
+ return V;
}
// TODO: Detect when the allocator returns a null pointer.
// Constructor shall not be called in this case.
@@ -202,7 +193,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallerLCtx = CallerLCtx->getParent();
assert(!isa<BlockInvocationContext>(CallerLCtx));
}
- return prepareForObjectConstruction(
+ return computeObjectUnderConstruction(
cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
} else {
@@ -223,64 +214,46 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
assert(RetE && "Void returns should not have a construction context");
QualType ReturnTy = RetE->getType();
QualType RegionTy = ACtx.getPointerType(ReturnTy);
- SVal V = SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC,
- RegionTy, currBldrCtx->blockCount());
- return std::make_pair(State, V);
+ return SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC, RegionTy,
+ currBldrCtx->blockCount());
}
llvm_unreachable("Unhandled return value construction context!");
}
case ConstructionContext::ElidedTemporaryObjectKind: {
assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
- const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
- const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
- const CXXConstructExpr *CE = TCC->getConstructorAfterElision();
// Support pre-C++17 copy elision. We'll have the elidable copy
// constructor in the AST and in the CFG, but we'll skip it
// and construct directly into the final object. This call
// also sets the CallOpts flags for us.
- SVal V;
// If the elided copy/move constructor is not supported, there's still
// benefit in trying to model the non-elided constructor.
// Stash our state before trying to elide, as it'll get overwritten.
ProgramStateRef PreElideState = State;
EvalCallOptions PreElideCallOpts = CallOpts;
- std::tie(State, V) = prepareForObjectConstruction(
- CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
+ SVal V = computeObjectUnderConstruction(
+ TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructionContextAfterElision(), CallOpts);
// FIXME: This definition of "copy elision has not failed" is unreliable.
// It doesn't indicate that the constructor will actually be inlined
- // later; it is still up to evalCall() to decide.
- if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
- // Remember that we've elided the constructor.
- State = addObjectUnderConstruction(State, CE, LCtx, V);
-
- // Remember that we've elided the destructor.
- if (BTE)
- State = elideDestructor(State, BTE, LCtx);
-
- // Instead of materialization, shamelessly return
- // the final object destination.
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
-
- return std::make_pair(State, V);
- } else {
- // Copy elision failed. Revert the changes and proceed as if we have
- // a simple temporary.
- State = PreElideState;
- CallOpts = PreElideCallOpts;
- }
+ // later; this is still up to evalCall() to decide.
+ if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
+ return V;
+
+ // Copy elision failed. Revert the changes and proceed as if we have
+ // a simple temporary.
+ CallOpts = PreElideCallOpts;
+ CallOpts.IsElidableCtorThatHasNotBeenElided = true;
LLVM_FALLTHROUGH;
}
case ConstructionContext::SimpleTemporaryObjectKind: {
const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
- const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
- SVal V = UnknownVal();
+ CallOpts.IsTemporaryCtorOrDtor = true;
if (MTE) {
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
assert(MTE->getStorageDuration() != SD_FullExpression);
@@ -296,20 +269,10 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
if (MTE->getStorageDuration() == SD_Static ||
MTE->getStorageDuration() == SD_Thread)
- V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
+ return loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
}
- if (V.isUnknown())
- V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
-
- if (BTE)
- State = addObjectUnderConstruction(State, BTE, LCtx, V);
-
- if (MTE)
- State = addObjectUnderConstruction(State, MTE, LCtx, V);
-
- CallOpts.IsTemporaryCtorOrDtor = true;
- return std::make_pair(State, V);
+ return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
}
case ConstructionContext::ArgumentKind: {
// Arguments are technically temporaries.
@@ -318,10 +281,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
const auto *ACC = cast<ArgumentConstructionContext>(CC);
const Expr *E = ACC->getCallLikeExpr();
unsigned Idx = ACC->getIndex();
- const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- SVal V = UnknownVal();
auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
const LocationContext *FutureSFC =
Caller->getCalleeStackFrame(currBldrCtx->blockCount());
@@ -342,76 +303,171 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// 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 VarRegion *VR = Caller->getParameterLocation(
+ const TypedValueRegion *TVR = Caller->getParameterLocation(
*Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
- if (!VR)
+ if (!TVR)
return None;
- return loc::MemRegionVal(VR);
+ return loc::MemRegionVal(TVR);
};
if (const auto *CE = dyn_cast<CallExpr>(E)) {
CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V);
} else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
// Don't bother figuring out the target region for the future
// constructor because we won't need it.
CallEventRef<> Caller =
CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {CCE, Idx}, LCtx, V);
} else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
- if (auto OptV = getArgLoc(Caller))
- V = *OptV;
+ if (Optional<SVal> V = getArgLoc(Caller))
+ return *V;
else
break;
- State = addObjectUnderConstruction(State, {ME, Idx}, LCtx, V);
+ }
+ }
+ } // switch (CC->getKind())
+ }
+
+ // If we couldn't find an existing region to construct into, assume we're
+ // constructing a temporary. Notify the caller of our failure.
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ return loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+}
+
+ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
+ SVal V, const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, const EvalCallOptions &CallOpts) {
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+ // Sounds like we failed to find the target region and therefore
+ // copy elision failed. There's nothing we can do about it here.
+ return State;
+ }
+
+ // See if we're constructing an existing region by looking at the
+ // current construction context.
+ assert(CC && "Computed target region without construction context?");
+ switch (CC->getKind()) {
+ case ConstructionContext::CXX17ElidedCopyVariableKind:
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<VariableConstructionContext>(CC);
+ return addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, V);
+ }
+ case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+ case ConstructionContext::SimpleConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(),
+ LCtx, V);
+ }
+ case ConstructionContext::NewAllocatedObjectKind: {
+ return State;
+ }
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+ const StackFrameContext *SFC = LCtx->getStackFrame();
+ const LocationContext *CallerLCtx = SFC->getParent();
+ if (!CallerLCtx) {
+ // No extra work is necessary in top frame.
+ return State;
}
- assert(!V.isUnknown());
+ auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
+ .getAs<CFGCXXRecordTypedCall>();
+ assert(RTC && "Could not have had a target region without it");
+ if (isa<BlockInvocationContext>(CallerLCtx)) {
+ // Unwrap block invocation contexts. They're mostly part of
+ // the current stack frame.
+ CallerLCtx = CallerLCtx->getParent();
+ assert(!isa<BlockInvocationContext>(CallerLCtx));
+ }
- if (BTE)
+ return updateObjectsUnderConstruction(V,
+ cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ RTC->getConstructionContext(), CallOpts);
+ }
+ case ConstructionContext::ElidedTemporaryObjectKind: {
+ assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
+ if (!CallOpts.IsElidableCtorThatHasNotBeenElided) {
+ const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+ State = updateObjectsUnderConstruction(
+ V, TCC->getConstructorAfterElision(), State, LCtx,
+ TCC->getConstructionContextAfterElision(), CallOpts);
+
+ // Remember that we've elided the constructor.
+ State = addObjectUnderConstruction(
+ State, TCC->getConstructorAfterElision(), LCtx, V);
+
+ // Remember that we've elided the destructor.
+ if (const auto *BTE = TCC->getCXXBindTemporaryExpr())
+ State = elideDestructor(State, BTE, LCtx);
+
+ // Instead of materialization, shamelessly return
+ // the final object destination.
+ if (const auto *MTE = TCC->getMaterializedTemporaryExpr())
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ return State;
+ }
+ // If we decided not to elide the constructor, proceed as if
+ // it's a simple temporary.
+ LLVM_FALLTHROUGH;
+ }
+ case ConstructionContext::SimpleTemporaryObjectKind: {
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+ if (const auto *BTE = TCC->getCXXBindTemporaryExpr())
State = addObjectUnderConstruction(State, BTE, LCtx, V);
- return std::make_pair(State, V);
+ if (const auto *MTE = TCC->getMaterializedTemporaryExpr())
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ return State;
}
+ case ConstructionContext::ArgumentKind: {
+ const auto *ACC = cast<ArgumentConstructionContext>(CC);
+ if (const auto *BTE = ACC->getCXXBindTemporaryExpr())
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
+
+ return addObjectUnderConstruction(
+ State, {ACC->getCallLikeExpr(), ACC->getIndex()}, LCtx, V);
}
}
- // If we couldn't find an existing region to construct into, assume we're
- // constructing a temporary. Notify the caller of our failure.
- CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
- return std::make_pair(
- State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
+ llvm_unreachable("Unhandled construction context!");
}
-void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
- ExplodedNode *Pred,
- ExplodedNodeSet &destNodes) {
+void ExprEngine::handleConstructor(const Expr *E,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &destNodes) {
+ const auto *CE = dyn_cast<CXXConstructExpr>(E);
+ const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(E);
+ assert(CE || CIE);
+
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
SVal Target = UnknownVal();
- if (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;
+ if (CE) {
+ if (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;
+ }
}
// FIXME: Handle arrays, which run the same constructor for every element.
@@ -423,10 +479,16 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
assert(C || getCurrentCFGElement().getAs<CFGStmt>());
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
- switch (CE->getConstructionKind()) {
+ const CXXConstructExpr::ConstructionKind CK =
+ CE ? CE->getConstructionKind() : CIE->getConstructionKind();
+ switch (CK) {
case CXXConstructExpr::CK_Complete: {
+ // Inherited constructors are always base class constructors.
+ assert(CE && !CIE && "A complete constructor is inherited?!");
+
+ // The target region is found from construction context.
std::tie(State, Target) =
- prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
+ handleConstructionContext(CE, State, LCtx, CC, CallOpts);
break;
}
case CXXConstructExpr::CK_VirtualBase: {
@@ -455,9 +517,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) passed down from CFG or
// otherwise always available during construction.
- if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
+ if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(E))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
- Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
+ Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
@@ -468,14 +530,13 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
- if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+ if (CK == CXXConstructExpr::CK_Delegating) {
Target = ThisVal;
} else {
// Cast to the base type.
- bool IsVirtual =
- (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
- SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
- IsVirtual);
+ bool IsVirtual = (CK == CXXConstructExpr::CK_VirtualBase);
+ SVal BaseVal =
+ getStoreManager().evalDerivedToBase(ThisVal, E->getType(), IsVirtual);
Target = BaseVal;
}
break;
@@ -487,23 +548,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"Prepare for object construction");
ExplodedNodeSet DstPrepare;
StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
- BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
+ BldrPrepare.generateNode(E, Pred, State, &T, ProgramPoint::PreStmtKind);
assert(DstPrepare.size() <= 1);
if (DstPrepare.size() == 0)
return;
Pred = *BldrPrepare.begin();
}
+ const MemRegion *TargetRegion = Target.getAsRegion();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
- CallEventRef<CXXConstructorCall> Call =
- CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
+ CallEventRef<> Call =
+ CIE ? (CallEventRef<>)CEMgr.getCXXInheritedConstructorCall(
+ CIE, TargetRegion, State, LCtx)
+ : (CallEventRef<>)CEMgr.getCXXConstructorCall(
+ CE, TargetRegion, State, LCtx);
ExplodedNodeSet DstPreVisit;
- getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+ getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, E, *this);
- // FIXME: Is it possible and/or useful to do this before PreStmt?
ExplodedNodeSet PreInitialized;
- {
+ if (CE) {
+ // FIXME: Is it possible and/or useful to do this before PreStmt?
StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
E = DstPreVisit.end();
@@ -528,6 +593,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
}
+ } else {
+ PreInitialized = DstPreVisit;
}
ExplodedNodeSet DstPreCall;
@@ -537,7 +604,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNodeSet DstEvaluated;
StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
- if (CE->getConstructor()->isTrivial() &&
+ if (CE && CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
!CallOpts.IsArrayCtorOrDtor) {
// FIXME: Handle other kinds of trivial constructors as well.
@@ -548,7 +615,8 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
} else {
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call, CallOpts);
+ getCheckerManager().runCheckersForEvalCall(DstEvaluated, *I, *Call, *this,
+ CallOpts);
}
// If the CFG was constructed without elements for temporary destructors
@@ -560,9 +628,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// paths when no-return temporary destructors are used for assertions.
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
- const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
- if (Target && isa<CXXTempObjectRegion>(Target) &&
- Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
+ if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
+ cast<CXXConstructorDecl>(Call->getDecl())
+ ->getParent()
+ ->isAnyDestructorNoReturn()) {
// If we've inlined the constructor, then DstEvaluated would be empty.
// In this case we still want a sink, which could be implemented
@@ -575,7 +644,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
"We should not have inlined this constructor!");
for (ExplodedNode *N : DstEvaluated) {
- Bldr.generateSink(CE, N, N->getState());
+ Bldr.generateSink(E, N, N->getState());
}
// There is no need to run the PostCall and PostStmt checker
@@ -586,7 +655,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
}
ExplodedNodeSet DstPostArgumentCleanup;
- for (auto I : DstEvaluated)
+ for (ExplodedNode *I : DstEvaluated)
finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
// If there were other constructors called for object-type arguments
@@ -595,7 +664,19 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
getCheckerManager().runCheckersForPostCall(DstPostCall,
DstPostArgumentCleanup,
*Call, *this);
- getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
+ getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, E, *this);
+}
+
+void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ handleConstructor(CE, Pred, Dst);
+}
+
+void ExprEngine::VisitCXXInheritedCtorInitExpr(
+ const CXXInheritedCtorInitExpr *CE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ handleConstructor(CE, Pred, Dst);
}
void ExprEngine::VisitCXXDestructor(QualType ObjectType,
@@ -683,7 +764,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNodeSet DstPostCall;
StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx);
- for (auto I : DstPreCall) {
+ for (ExplodedNode *I : DstPreCall) {
// FIXME: Provide evalCall for checkers?
defaultEvalCall(CallBldr, I, *Call);
}
@@ -693,7 +774,7 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
// CXXNewExpr gets processed.
ExplodedNodeSet DstPostValue;
StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx);
- for (auto I : DstPostCall) {
+ for (ExplodedNode *I : DstPostCall) {
// FIXME: Because CNE serves as the "call site" for the allocator (due to
// lack of a better expression in the AST), the conjured return value symbol
// is going to be of the same type (C++ object pointer type). Technically
@@ -727,10 +808,8 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
ExplodedNodeSet DstPostPostCallCallback;
getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
DstPostValue, *Call, *this);
- for (auto I : DstPostPostCallCallback) {
- getCheckerManager().runCheckersForNewAllocator(
- CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I,
- *this);
+ for (ExplodedNode *I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(*Call, Dst, I, *this);
}
}
@@ -846,13 +925,18 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
- StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
- ProgramStateRef state = Pred->getState();
- Bldr.generateNode(CDE, Pred, state);
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDeallocatorCall> Call = CEMgr.getCXXDeallocatorCall(
+ CDE, Pred->getState(), Pred->getLocationContext());
+
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, *Call, *this);
+
+ getCheckerManager().runCheckersForPostCall(Dst, DstPreCall, *Call, *this);
}
-void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS,
- ExplodedNode *Pred,
+void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
const VarDecl *VD = CS->getExceptionDecl();
if (!VD) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 01a371e664b2..52ba17d59ae0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -10,17 +10,19 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/Decl.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "PrettyStackTraceLocationContext.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/ConstructionContext.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace clang;
@@ -324,17 +326,14 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
ExplodedNodeSet DstPostCall;
- if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+ if (llvm::isa_and_nonnull<CXXNewExpr>(CE)) {
ExplodedNodeSet DstPostPostCallCallback;
getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
CEENode, *UpdatedCall, *this,
/*wasInlined=*/true);
- for (auto I : DstPostPostCallCallback) {
+ for (ExplodedNode *I : DstPostPostCallCallback) {
getCheckerManager().runCheckersForNewAllocator(
- CNE,
- *getObjectUnderConstruction(I->getState(), CNE,
- calleeCtx->getParent()),
- DstPostCall, I, *this,
+ cast<CXXAllocatorCall>(*UpdatedCall), DstPostCall, I, *this,
/*wasInlined=*/true);
}
} else {
@@ -585,12 +584,12 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
// defaultEvalCall if all of them fail.
ExplodedNodeSet dstCallEvaluated;
getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
- Call, *this);
+ Call, *this, EvalCallOptions());
// If there were other constructors called for object-type arguments
// of this call, clean them up.
ExplodedNodeSet dstArgumentCleanup;
- for (auto I : dstCallEvaluated)
+ for (ExplodedNode *I : dstCallEvaluated)
finishArgumentConstruction(dstArgumentCleanup, I, Call);
ExplodedNodeSet dstPostCall;
@@ -604,7 +603,7 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
// Run pointerEscape callback with the newly conjured symbols.
SmallVector<std::pair<SVal, SVal>, 8> Escaped;
- for (auto I : dstPostCall) {
+ for (ExplodedNode *I : dstPostCall) {
NodeBuilder B(I, Dst, *currBldrCtx);
ProgramStateRef State = I->getState();
Escaped.clear();
@@ -668,8 +667,8 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
assert(RTC->getStmt() == Call.getOriginExpr());
EvalCallOptions CallOpts; // FIXME: We won't really need those.
std::tie(State, Target) =
- prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
- RTC->getConstructionContext(), CallOpts);
+ handleConstructionContext(Call.getOriginExpr(), State, LCtx,
+ RTC->getConstructionContext(), CallOpts);
const MemRegion *TargetR = Target.getAsRegion();
assert(TargetR);
// Invalidate the region so that it didn't look uninitialized. If this is
@@ -718,7 +717,7 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
ExprEngine::CallInlinePolicy
ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
AnalyzerOptions &Opts,
- const ExprEngine::EvalCallOptions &CallOpts) {
+ const EvalCallOptions &CallOpts) {
const LocationContext *CurLC = Pred->getLocationContext();
const StackFrameContext *CallerSFC = CurLC->getStackFrame();
switch (Call.getKind()) {
@@ -742,7 +741,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
: nullptr;
- if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
+ if (llvm::isa_and_nonnull<NewAllocatedObjectConstructionContext>(CC) &&
!Opts.MayInlineCXXAllocator)
return CIP_DisallowedOnce;
@@ -789,6 +788,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
break;
}
+ case CE_CXXInheritedConstructor: {
+ // This doesn't really increase the cost of inlining ever, because
+ // the stack frame of the inherited constructor is trivial.
+ return CIP_Allowed;
+ }
case CE_CXXDestructor: {
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;
@@ -814,6 +818,8 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
return CIP_DisallowedOnce;
break;
}
+ case CE_CXXDeallocator:
+ LLVM_FALLTHROUGH;
case CE_CXXAllocator:
if (Opts.MayInlineCXXAllocator)
break;
diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index a4918d7179ff..bc7c41d039c4 100644
--- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -66,11 +66,9 @@ class HTMLDiagnostics : public PathDiagnosticConsumer {
const bool SupportsCrossFileDiagnostics;
public:
- HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
- const std::string& prefix,
- const Preprocessor &pp,
- bool supportsMultipleFiles)
- : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &OutputDir,
+ const Preprocessor &pp, bool supportsMultipleFiles)
+ : Directory(OutputDir), PP(pp), AnalyzerOpts(AnalyzerOpts),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
@@ -136,16 +134,45 @@ private:
void ento::createHTMLDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &prefix, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true));
+ const std::string &OutputDir, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // FIXME: HTML is currently our default output type, but if the output
+ // directory isn't specified, it acts like if it was in the minimal text
+ // output mode. This doesn't make much sense, we should have the minimal text
+ // as our default. In the case of backward compatibility concerns, this could
+ // be preserved with -analyzer-config-compatibility-mode=true.
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+
+ // TODO: Emit an error here.
+ if (OutputDir.empty())
+ return;
+
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, true));
}
void ento::createHTMLSingleFileDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputDir, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputDir.empty())
+ return;
+
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, false));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+}
+
+void ento::createPlistHTMLDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
const std::string &prefix, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false));
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ createHTMLDiagnosticConsumer(
+ AnalyzerOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
+ CTU);
+ createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
}
//===----------------------------------------------------------------------===//
@@ -607,10 +634,17 @@ window.addEventListener("keydown", function (event) {
)<<<";
}
+static bool shouldDisplayPopUpRange(const SourceRange &Range) {
+ return !(Range.getBegin().isMacroID() || Range.getEnd().isMacroID());
+}
+
static void
HandlePopUpPieceStartTag(Rewriter &R,
const std::vector<SourceRange> &PopUpRanges) {
for (const auto &Range : PopUpRanges) {
+ if (!shouldDisplayPopUpRange(Range))
+ continue;
+
html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "",
"<table class='variable_popup'><tbody>",
/*IsTokenRange=*/true);
@@ -626,6 +660,8 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
llvm::raw_svector_ostream Out(Buf);
SourceRange Range(Piece.getLocation().asRange());
+ if (!shouldDisplayPopUpRange(Range))
+ return;
// Write out the path indices with a right arrow and the message as a row.
Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
@@ -870,7 +906,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
<< (num - 1)
<< "\" title=\"Previous event ("
<< (num - 1)
- << ")\">&#x2190;</a></div></td>";
+ << ")\">&#x2190;</a></div>";
}
os << "</td><td>";
@@ -1034,8 +1070,13 @@ StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
<script type='text/javascript'>
var digitMatcher = new RegExp("[0-9]+");
+var querySelectorAllArray = function(selector) {
+ return Array.prototype.slice.call(
+ document.querySelectorAll(selector));
+}
+
document.addEventListener("DOMContentLoaded", function() {
- document.querySelectorAll(".PathNav > a").forEach(
+ querySelectorAllArray(".PathNav > a").forEach(
function(currentValue, currentIndex) {
var hrefValue = currentValue.getAttribute("href");
currentValue.onclick = function() {
@@ -1055,7 +1096,7 @@ var findNum = function() {
};
var scrollTo = function(el) {
- document.querySelectorAll(".selected").forEach(function(s) {
+ querySelectorAllArray(".selected").forEach(function(s) {
s.classList.remove("selected");
});
el.classList.add("selected");
diff --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index 1a09a521f116..dc268e562237 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -130,10 +130,10 @@ static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
// Escaping and not known mutation of the loop counter is handled
// by exclusion of assigning and address-of operators and
// pass-by-ref function calls on the loop counter from the body.
- changeIntBoundNode(equalsBoundNode(NodeName)),
- callByRef(equalsBoundNode(NodeName)),
- getAddrTo(equalsBoundNode(NodeName)),
- assignedToRef(equalsBoundNode(NodeName)))));
+ changeIntBoundNode(equalsBoundNode(std::string(NodeName))),
+ callByRef(equalsBoundNode(std::string(NodeName))),
+ getAddrTo(equalsBoundNode(std::string(NodeName))),
+ assignedToRef(equalsBoundNode(std::string(NodeName))))));
}
static internal::Matcher<Stmt> forLoopMatcher() {
@@ -164,6 +164,11 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
if (VD->hasGlobalStorage())
return true;
+ const bool isParm = isa<ParmVarDecl>(VD);
+ // Reference parameters are assumed as escaped variables.
+ if (isParm && VD->getType()->isReferenceType())
+ return true;
+
while (!N->pred_empty()) {
// FIXME: getStmtForDiagnostics() does nasty things in order to provide
// a valid statement for body farms, do we need this behavior here?
@@ -193,6 +198,11 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
N = N->getFirstPred();
}
+
+ // Parameter declaration will not be found.
+ if (isParm)
+ return false;
+
llvm_unreachable("Reached root without finding the declaration of VD");
}
diff --git a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 9a7b1a24b819..47e34dd84b9a 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -67,8 +67,10 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
}
// References should not be invalidated.
- auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))),
- *LCtx->getDecl()->getBody(), ASTCtx);
+ auto Matches = match(
+ findAll(stmt(hasDescendant(
+ varDecl(hasType(hasCanonicalType(referenceType()))).bind(MatchRef)))),
+ *LCtx->getDecl()->getBody(), ASTCtx);
for (BoundNodes Match : Matches) {
const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef);
assert(VD);
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index a10d7e69ad7e..455adf53ac99 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -142,7 +142,7 @@ bool SubRegion::isSubRegionOf(const MemRegion* R) const {
return false;
}
-MemRegionManager* SubRegion::getMemRegionManager() const {
+MemRegionManager &SubRegion::getMemRegionManager() const {
const SubRegion* r = this;
do {
const MemRegion *superRegion = r->getSuperRegion();
@@ -159,62 +159,10 @@ const StackFrameContext *VarRegion::getStackFrame() const {
return SSR ? SSR->getStackFrame() : nullptr;
}
-//===----------------------------------------------------------------------===//
-// Region extents.
-//===----------------------------------------------------------------------===//
-
-DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
- ASTContext &Ctx = svalBuilder.getContext();
- QualType T = getDesugaredValueType(Ctx);
-
- if (isa<VariableArrayType>(T))
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
- if (T->isIncompleteType())
- return UnknownVal();
-
- CharUnits size = Ctx.getTypeSizeInChars(T);
- QualType sizeTy = svalBuilder.getArrayIndexType();
- return svalBuilder.makeIntVal(size.getQuantity(), sizeTy);
-}
-
-DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const {
- // Force callers to deal with bitfields explicitly.
- if (getDecl()->isBitField())
- return UnknownVal();
-
- DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder);
-
- // A zero-length array at the end of a struct often stands for dynamically-
- // allocated extra memory.
- if (Extent.isZeroConstant()) {
- QualType T = getDesugaredValueType(svalBuilder.getContext());
-
- if (isa<ConstantArrayType>(T))
- return UnknownVal();
- }
-
- return Extent;
-}
-
-DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const {
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
-}
-
-DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const {
- return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
-}
-
-DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const {
- return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1,
- svalBuilder.getArrayIndexType());
-}
-
ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
- : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+ : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {}
-const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
- return cast<ObjCIvarDecl>(D);
-}
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; }
QualType ObjCIvarRegion::getValueType() const {
return getDecl()->getType();
@@ -228,6 +176,33 @@ QualType CXXDerivedObjectRegion::getValueType() const {
return QualType(getDecl()->getTypeForDecl(), 0);
}
+QualType ParamVarRegion::getValueType() const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ return getDecl()->getType();
+}
+
+const ParmVarDecl *ParamVarRegion::getDecl() const {
+ const Decl *D = getStackFrame()->getDecl();
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ assert(Index < FD->param_size());
+ return FD->parameters()[Index];
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ assert(Index < BD->param_size());
+ return BD->parameters()[Index];
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ assert(Index < MD->param_size());
+ return MD->parameters()[Index];
+ } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+ assert(Index < CD->param_size());
+ return CD->parameters()[Index];
+ } else {
+ llvm_unreachable("Unexpected Decl kind!");
+ }
+}
+
//===----------------------------------------------------------------------===//
// FoldingSet profiling.
//===----------------------------------------------------------------------===//
@@ -299,25 +274,44 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
}
+void FieldRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
const ObjCIvarDecl *ivd,
const MemRegion* superRegion) {
- DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+ ID.AddInteger(static_cast<unsigned>(ObjCIvarRegionKind));
+ ID.AddPointer(ivd);
+ ID.AddPointer(superRegion);
}
-void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
- const MemRegion* superRegion, Kind k) {
- ID.AddInteger(static_cast<unsigned>(k));
- ID.AddPointer(D);
+void ObjCIvarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
+void NonParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const VarDecl *VD,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(NonParamVarRegionKind));
+ ID.AddPointer(VD);
ID.AddPointer(superRegion);
}
-void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const {
- DeclRegion::ProfileRegion(ID, D, superRegion, getKind());
+void NonParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
}
-void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
- VarRegion::ProfileRegion(ID, getDecl(), superRegion);
+void ParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+ unsigned Idx, const MemRegion *SReg) {
+ ID.AddInteger(static_cast<unsigned>(ParamVarRegionKind));
+ ID.AddPointer(OE);
+ ID.AddInteger(Idx);
+ ID.AddPointer(SReg);
+}
+
+void ParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getOriginExpr(), getIndex(), superRegion);
}
void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym,
@@ -529,12 +523,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const {
os << "SymRegion{" << sym << '}';
}
-void VarRegion::dumpToStream(raw_ostream &os) const {
- const auto *VD = cast<VarDecl>(D);
+void NonParamVarRegion::dumpToStream(raw_ostream &os) const {
if (const IdentifierInfo *ID = VD->getIdentifier())
os << ID->getName();
else
- os << "VarRegion{D" << VD->getID() << '}';
+ os << "NonParamVarRegion{D" << VD->getID() << '}';
}
LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
@@ -581,6 +574,18 @@ void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
os << "StackLocalsSpaceRegion";
}
+void ParamVarRegion::dumpToStream(raw_ostream &os) const {
+ const ParmVarDecl *PVD = getDecl();
+ assert(PVD &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ if (const IdentifierInfo *ID = PVD->getIdentifier()) {
+ os << ID->getName();
+ } else {
+ os << "ParamVarRegion{P" << PVD->getID() << '}';
+ }
+}
+
bool MemRegion::canPrintPretty() const {
return canPrintPrettyAsExpr();
}
@@ -600,11 +605,18 @@ void MemRegion::printPrettyAsExpr(raw_ostream &) const {
llvm_unreachable("This region cannot be printed pretty.");
}
-bool VarRegion::canPrintPrettyAsExpr() const {
- return true;
+bool NonParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void NonParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ os << getDecl()->getName();
}
-void VarRegion::printPrettyAsExpr(raw_ostream &os) const {
+bool ParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void ParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
os << getDecl()->getName();
}
@@ -717,11 +729,79 @@ SourceRange MemRegion::sourceRange() const {
// MemRegionManager methods.
//===----------------------------------------------------------------------===//
+static DefinedOrUnknownSVal getTypeSize(QualType Ty, ASTContext &Ctx,
+ SValBuilder &SVB) {
+ CharUnits Size = Ctx.getTypeSizeInChars(Ty);
+ QualType SizeTy = SVB.getArrayIndexType();
+ return SVB.makeIntVal(Size.getQuantity(), SizeTy);
+}
+
+DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
+ SValBuilder &SVB) const {
+ const auto *SR = cast<SubRegion>(MR);
+ SymbolManager &SymMgr = SVB.getSymbolManager();
+
+ switch (SR->getKind()) {
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::SymbolicRegionKind:
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+ case MemRegion::StringRegionKind:
+ return SVB.makeIntVal(
+ cast<StringRegion>(SR)->getStringLiteral()->getByteLength() + 1,
+ SVB.getArrayIndexType());
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::CXXBaseObjectRegionKind:
+ case MemRegion::CXXDerivedObjectRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
+ case MemRegion::ElementRegionKind:
+ case MemRegion::ObjCStringRegionKind: {
+ QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
+ if (isa<VariableArrayType>(Ty))
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+
+ if (Ty->isIncompleteType())
+ return UnknownVal();
+
+ return getTypeSize(Ty, Ctx, SVB);
+ }
+ case MemRegion::FieldRegionKind: {
+ // Force callers to deal with bitfields explicitly.
+ if (cast<FieldRegion>(SR)->getDecl()->isBitField())
+ return UnknownVal();
+
+ QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
+ DefinedOrUnknownSVal Size = getTypeSize(Ty, Ctx, SVB);
+
+ // A zero-length array at the end of a struct often stands for dynamically
+ // allocated extra memory.
+ if (Size.isZeroConstant()) {
+ if (isa<ConstantArrayType>(Ty))
+ return UnknownVal();
+ }
+
+ return Size;
+ }
+ // FIXME: The following are being used in 'SimpleSValBuilder' and in
+ // 'ArrayBoundChecker::checkLocation' because there is no symbol to
+ // represent the regions more appropriately.
+ case MemRegion::BlockDataRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::FunctionCodeRegionKind:
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(SR));
+ default:
+ llvm_unreachable("Unhandled region");
+ }
+}
+
template <typename REG>
const REG *MemRegionManager::LazyAllocate(REG*& region) {
if (!region) {
region = A.Allocate<REG>();
- new (region) REG(this);
+ new (region) REG(*this);
}
return region;
@@ -746,7 +826,7 @@ MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) {
return R;
R = A.Allocate<StackLocalsSpaceRegion>();
- new (R) StackLocalsSpaceRegion(this, STC);
+ new (R) StackLocalsSpaceRegion(*this, STC);
return R;
}
@@ -759,7 +839,7 @@ MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) {
return R;
R = A.Allocate<StackArgumentsSpaceRegion>();
- new (R) StackArgumentsSpaceRegion(this, STC);
+ new (R) StackArgumentsSpaceRegion(*this, STC);
return R;
}
@@ -781,7 +861,7 @@ const GlobalsSpaceRegion
return R;
R = A.Allocate<StaticGlobalSpaceRegion>();
- new (R) StaticGlobalSpaceRegion(this, CR);
+ new (R) StaticGlobalSpaceRegion(*this, CR);
return R;
}
@@ -825,15 +905,16 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
return SFC;
}
if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) {
- const auto *BR =
- static_cast<const BlockDataRegion *>(BC->getContextData());
+ const auto *BR = static_cast<const BlockDataRegion *>(BC->getData());
// FIXME: This can be made more efficient.
for (BlockDataRegion::referenced_vars_iterator
I = BR->referenced_vars_begin(),
E = BR->referenced_vars_end(); I != E; ++I) {
- const VarRegion *VR = I.getOriginalRegion();
- if (VR->getDecl() == VD)
- return cast<VarRegion>(I.getCapturedRegion());
+ const TypedValueRegion *OrigR = I.getOriginalRegion();
+ if (const auto *VR = dyn_cast<VarRegion>(OrigR)) {
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
}
}
@@ -842,15 +923,37 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
return (const StackFrameContext *)nullptr;
}
-const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
+const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
+ const auto *PVD = dyn_cast<ParmVarDecl>(D);
+ if (PVD) {
+ unsigned Index = PVD->getFunctionScopeIndex();
+ const StackFrameContext *SFC = LC->getStackFrame();
+ const Stmt *CallSite = SFC->getCallSite();
+ if (CallSite) {
+ const Decl *D = SFC->getDecl();
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else {
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ }
+ }
+ }
+
D = D->getCanonicalDecl();
const MemRegion *sReg = nullptr;
if (D->hasGlobalStorage() && !D->isStaticLocal()) {
// First handle the globals defined in system headers.
- if (C.getSourceManager().isInSystemHeader(D->getLocation())) {
+ if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) {
// Whitelist the system globals which often DO GET modified, assume the
// rest are immutable.
if (D->getName().find("errno") != StringRef::npos)
@@ -914,7 +1017,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
T = getContext().getBlockPointerType(T);
const BlockCodeRegion *BTR =
- getBlockCodeRegion(BD, C.getCanonicalType(T),
+ getBlockCodeRegion(BD, Ctx.getCanonicalType(T),
STC->getAnalysisDeclContext());
sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
BTR);
@@ -926,13 +1029,23 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
}
}
- return getSubRegion<VarRegion>(D, sReg);
+ return getSubRegion<NonParamVarRegion>(D, sReg);
}
-const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
- const MemRegion *superR) {
+const NonParamVarRegion *
+MemRegionManager::getNonParamVarRegion(const VarDecl *D,
+ const MemRegion *superR) {
D = D->getCanonicalDecl();
- return getSubRegion<VarRegion>(D, superR);
+ return getSubRegion<NonParamVarRegion>(D, superR);
+}
+
+const ParamVarRegion *
+MemRegionManager::getParamVarRegion(const Expr *OriginExpr, unsigned Index,
+ const LocationContext *LC) {
+ const StackFrameContext *SFC = LC->getStackFrame();
+ assert(SFC);
+ return getSubRegion<ParamVarRegion>(OriginExpr, Index,
+ getStackArgumentsRegion(SFC));
}
const BlockDataRegion *
@@ -1325,7 +1438,8 @@ static RegionOffset calculateOffset(const MemRegion *R) {
case MemRegion::CXXThisRegionKind:
case MemRegion::StringRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
// Usual base regions.
goto Finish;
@@ -1476,12 +1590,12 @@ RegionOffset MemRegion::getAsOffset() const {
std::pair<const VarRegion *, const VarRegion *>
BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
- MemRegionManager &MemMgr = *getMemRegionManager();
+ MemRegionManager &MemMgr = getMemRegionManager();
const VarRegion *VR = nullptr;
const VarRegion *OriginalVR = nullptr;
if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) {
- VR = MemMgr.getVarRegion(VD, this);
+ VR = MemMgr.getNonParamVarRegion(VD, this);
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
else {
@@ -1490,7 +1604,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
OriginalVR = VR;
}
else {
- VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ VR = MemMgr.getNonParamVarRegion(VD, MemMgr.getUnknownRegion());
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
@@ -1511,7 +1625,7 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
return;
}
- MemRegionManager &MemMgr = *getMemRegionManager();
+ MemRegionManager &MemMgr = getMemRegionManager();
llvm::BumpPtrAllocator &A = MemMgr.getAllocator();
BumpVectorContext BC(A);
diff --git a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 3a3942a8301b..ed62778623a8 100644
--- a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -45,8 +45,8 @@ namespace {
AnalyzerOptions &AnOpts;
const bool SupportsCrossFileDiagnostics;
public:
- PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix,
- const Preprocessor &PP,
+ PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU,
bool supportsMultipleFiles);
@@ -582,19 +582,32 @@ PlistDiagnostics::PlistDiagnostics(
void ento::createPlistDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &s, const Preprocessor &PP,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
/*supportsMultipleFiles*/ false));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
}
void ento::createPlistMultiFileDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &s, const Preprocessor &PP,
+ const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
/*supportsMultipleFiles*/ true));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
}
+
void PlistDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) {
@@ -939,7 +952,7 @@ getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
std::string MacroName = getMacroNameAndPrintExpansion(
Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens);
- return { MacroName, OS.str() };
+ return {MacroName, std::string(OS.str())};
}
static std::string getMacroNameAndPrintExpansion(
@@ -960,9 +973,8 @@ static std::string getMacroNameAndPrintExpansion(
// in this case we don't get the full expansion text in the Plist file. See
// the test file where "value" is expanded to "garbage_" instead of
// "garbage_value".
- if (AlreadyProcessedTokens.find(IDInfo) != AlreadyProcessedTokens.end())
+ if (!AlreadyProcessedTokens.insert(IDInfo).second)
return Info.Name;
- AlreadyProcessedTokens.insert(IDInfo);
if (!Info.MI)
return Info.Name;
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 14006f79fd0f..006a4006b7fc 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -16,8 +16,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -76,12 +76,12 @@ ProgramStateManager::ProgramStateManager(ASTContext &Ctx,
StoreManagerCreator CreateSMgr,
ConstraintManagerCreator CreateCMgr,
llvm::BumpPtrAllocator &alloc,
- SubEngine *SubEng)
- : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc),
+ ExprEngine *ExprEng)
+ : Eng(ExprEng), EnvMgr(alloc), GDMFactory(alloc),
svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
StoreMgr = (*CreateSMgr)(*this);
- ConstraintMgr = (*CreateCMgr)(*this, SubEng);
+ ConstraintMgr = (*CreateCMgr)(*this, ExprEng);
}
@@ -189,7 +189,7 @@ ProgramState::invalidateRegionsImpl(ValueList Values,
RegionAndSymbolInvalidationTraits *ITraits,
const CallEvent *Call) const {
ProgramStateManager &Mgr = getStateManager();
- SubEngine &Eng = Mgr.getOwningEngine();
+ ExprEngine &Eng = Mgr.getOwningEngine();
InvalidatedSymbols InvalidatedSyms;
if (!IS)
@@ -240,6 +240,13 @@ ProgramState::enterStackFrame(const CallEvent &Call,
return makeWithStore(NewStore);
}
+SVal ProgramState::getSelfSVal(const LocationContext *LCtx) const {
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ if (!SelfDecl)
+ return SVal();
+ return getSVal(getRegion(SelfDecl, LCtx));
+}
+
SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
// We only want to do fetches from regions that we can actually bind
// values. For example, SymbolicRegions of type 'id<...>' cannot
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 9752a0e22832..cb6f61e86ae3 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -16,6 +16,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/Support/raw_ostream.h"
@@ -23,10 +24,89 @@
using namespace clang;
using namespace ento;
+// This class can be extended with other tables which will help to reason
+// about ranges more precisely.
+class OperatorRelationsTable {
+ static_assert(BO_LT < BO_GT && BO_GT < BO_LE && BO_LE < BO_GE &&
+ BO_GE < BO_EQ && BO_EQ < BO_NE,
+ "This class relies on operators order. Rework it otherwise.");
+
+public:
+ enum TriStateKind {
+ False = 0,
+ True,
+ Unknown,
+ };
+
+private:
+ // CmpOpTable holds states which represent the corresponding range for
+ // branching an exploded graph. We can reason about the branch if there is
+ // a previously known fact of the existence of a comparison expression with
+ // operands used in the current expression.
+ // E.g. assuming (x < y) is true that means (x != y) is surely true.
+ // if (x previous_operation y) // < | != | >
+ // if (x operation y) // != | > | <
+ // tristate // True | Unknown | False
+ //
+ // CmpOpTable represents next:
+ // __|< |> |<=|>=|==|!=|UnknownX2|
+ // < |1 |0 |* |0 |0 |* |1 |
+ // > |0 |1 |0 |* |0 |* |1 |
+ // <=|1 |0 |1 |* |1 |* |0 |
+ // >=|0 |1 |* |1 |1 |* |0 |
+ // ==|0 |0 |* |* |1 |0 |1 |
+ // !=|1 |1 |* |* |0 |1 |0 |
+ //
+ // Columns stands for a previous operator.
+ // Rows stands for a current operator.
+ // Each row has exactly two `Unknown` cases.
+ // UnknownX2 means that both `Unknown` previous operators are met in code,
+ // and there is a special column for that, for example:
+ // if (x >= y)
+ // if (x != y)
+ // if (x <= y)
+ // False only
+ static constexpr size_t CmpOpCount = BO_NE - BO_LT + 1;
+ const TriStateKind CmpOpTable[CmpOpCount][CmpOpCount + 1] = {
+ // < > <= >= == != UnknownX2
+ {True, False, Unknown, False, False, Unknown, True}, // <
+ {False, True, False, Unknown, False, Unknown, True}, // >
+ {True, False, True, Unknown, True, Unknown, False}, // <=
+ {False, True, Unknown, True, True, Unknown, False}, // >=
+ {False, False, Unknown, Unknown, True, False, True}, // ==
+ {True, True, Unknown, Unknown, False, True, False}, // !=
+ };
+
+ static size_t getIndexFromOp(BinaryOperatorKind OP) {
+ return static_cast<size_t>(OP - BO_LT);
+ }
+
+public:
+ constexpr size_t getCmpOpCount() const { return CmpOpCount; }
+
+ static BinaryOperatorKind getOpFromIndex(size_t Index) {
+ return static_cast<BinaryOperatorKind>(Index + BO_LT);
+ }
+
+ TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
+ BinaryOperatorKind QueriedOP) const {
+ return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
+ }
+
+ TriStateKind getCmpOpStateForUnknownX2(BinaryOperatorKind CurrentOP) const {
+ return CmpOpTable[getIndexFromOp(CurrentOP)][CmpOpCount];
+ }
+};
+//===----------------------------------------------------------------------===//
+// RangeSet implementation
+//===----------------------------------------------------------------------===//
+
void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
- const llvm::APSInt &Lower, const llvm::APSInt &Upper,
- PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
- PrimRangeSet::iterator &e) const {
+ const llvm::APSInt &Lower,
+ const llvm::APSInt &Upper,
+ PrimRangeSet &newRanges,
+ PrimRangeSet::iterator &i,
+ PrimRangeSet::iterator &e) const {
// There are six cases for each range R in the set:
// 1. R is entirely before the intersection range.
// 2. R is entirely after the intersection range.
@@ -62,10 +142,27 @@ void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
const llvm::APSInt &RangeSet::getMinValue() const {
assert(!isEmpty());
- return ranges.begin()->From();
+ return begin()->From();
+}
+
+const llvm::APSInt &RangeSet::getMaxValue() const {
+ assert(!isEmpty());
+ // NOTE: It's a shame that we can't implement 'getMaxValue' without scanning
+ // the whole tree to get to the last element.
+ // llvm::ImmutableSet should support decrement for 'end' iterators
+ // or reverse order iteration.
+ auto It = begin();
+ for (auto End = end(); std::next(It) != End; ++It) {
+ }
+ return It->To();
}
bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ if (isEmpty()) {
+ // This range is already infeasible.
+ return false;
+ }
+
// This function has nine cases, the cartesian product of range-testing
// both the upper and lower bounds against the symbol's type.
// Each case requires a different pinning operation.
@@ -155,11 +252,11 @@ bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
// or, alternatively, /removing/ all integers between Upper and Lower.
RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F,
llvm::APSInt Lower, llvm::APSInt Upper) const {
- if (!pin(Lower, Upper))
- return F.getEmptySet();
-
PrimRangeSet newRanges = F.getEmptySet();
+ if (isEmpty() || !pin(Lower, Upper))
+ return newRanges;
+
PrimRangeSet::iterator i = begin(), e = end();
if (Lower <= Upper)
IntersectInRange(BV, F, Lower, Upper, newRanges, i, e);
@@ -190,33 +287,78 @@ RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F,
return newRanges;
}
-// Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set
-// [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal
-// signed values of the type.
+// Turn all [A, B] ranges to [-B, -A], when "-" is a C-like unary minus
+// operation under the values of the type.
+//
+// We also handle MIN because applying unary minus to MIN does not change it.
+// Example 1:
+// char x = -128; // -128 is a MIN value in a range of 'char'
+// char y = -x; // y: -128
+// Example 2:
+// unsigned char x = 0; // 0 is a MIN value in a range of 'unsigned char'
+// unsigned char y = -x; // y: 0
+//
+// And it makes us to separate the range
+// like [MIN, N] to [MIN, MIN] U [-N,MAX].
+// For instance, whole range is {-128..127} and subrange is [-128,-126],
+// thus [-128,-127,-126,.....] negates to [-128,.....,126,127].
+//
+// Negate restores disrupted ranges on bounds,
+// e.g. [MIN, B] => [MIN, MIN] U [-B, MAX] => [MIN, B].
RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const {
PrimRangeSet newRanges = F.getEmptySet();
- for (iterator i = begin(), e = end(); i != e; ++i) {
- const llvm::APSInt &from = i->From(), &to = i->To();
- const llvm::APSInt &newTo = (from.isMinSignedValue() ?
- BV.getMaxValue(from) :
- BV.getValue(- from));
- if (to.isMaxSignedValue() && !newRanges.isEmpty() &&
- newRanges.begin()->From().isMinSignedValue()) {
- assert(newRanges.begin()->To().isMinSignedValue() &&
- "Ranges should not overlap");
- assert(!from.isMinSignedValue() && "Ranges should not overlap");
- const llvm::APSInt &newFrom = newRanges.begin()->From();
- newRanges =
- F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo));
- } else if (!to.isMinSignedValue()) {
- const llvm::APSInt &newFrom = BV.getValue(- to);
- newRanges = F.add(newRanges, Range(newFrom, newTo));
- }
- if (from.isMinSignedValue()) {
- newRanges = F.add(newRanges, Range(BV.getMinValue(from),
- BV.getMinValue(from)));
+ if (isEmpty())
+ return newRanges;
+
+ const llvm::APSInt sampleValue = getMinValue();
+ const llvm::APSInt &MIN = BV.getMinValue(sampleValue);
+ const llvm::APSInt &MAX = BV.getMaxValue(sampleValue);
+
+ // Handle a special case for MIN value.
+ iterator i = begin();
+ const llvm::APSInt &from = i->From();
+ const llvm::APSInt &to = i->To();
+ if (from == MIN) {
+ // If [from, to] are [MIN, MAX], then just return the same [MIN, MAX].
+ if (to == MAX) {
+ newRanges = ranges;
+ } else {
+ // Add separate range for the lowest value.
+ newRanges = F.add(newRanges, Range(MIN, MIN));
+ // Skip adding the second range in case when [from, to] are [MIN, MIN].
+ if (to != MIN) {
+ newRanges = F.add(newRanges, Range(BV.getValue(-to), MAX));
+ }
}
+ // Skip the first range in the loop.
+ ++i;
+ }
+
+ // Negate all other ranges.
+ for (iterator e = end(); i != e; ++i) {
+ // Negate int values.
+ const llvm::APSInt &newFrom = BV.getValue(-i->To());
+ const llvm::APSInt &newTo = BV.getValue(-i->From());
+ // Add a negated range.
+ newRanges = F.add(newRanges, Range(newFrom, newTo));
+ }
+
+ if (newRanges.isSingleton())
+ return newRanges;
+
+ // Try to find and unite next ranges:
+ // [MIN, MIN] & [MIN + 1, N] => [MIN, N].
+ iterator iter1 = newRanges.begin();
+ iterator iter2 = std::next(iter1);
+
+ if (iter1->To() == MIN && (iter2->From() - 1) == MIN) {
+ const llvm::APSInt &to = iter2->To();
+ // remove adjacent ranges
+ newRanges = F.remove(newRanges, *iter1);
+ newRanges = F.remove(newRanges, *newRanges.begin());
+ // add united range
+ newRanges = F.add(newRanges, Range(MIN, to));
}
return newRanges;
@@ -238,10 +380,534 @@ void RangeSet::print(raw_ostream &os) const {
}
namespace {
+
+/// A little component aggregating all of the reasoning we have about
+/// the ranges of symbolic expressions.
+///
+/// Even when we don't know the exact values of the operands, we still
+/// can get a pretty good estimate of the result's range.
+class SymbolicRangeInferrer
+ : public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
+public:
+ static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef Sym) {
+ SymbolicRangeInferrer Inferrer(BV, F, State);
+ return Inferrer.infer(Sym);
+ }
+
+ RangeSet VisitSymExpr(SymbolRef Sym) {
+ // If we got to this function, the actual type of the symbolic
+ // expression is not supported for advanced inference.
+ // In this case, we simply backoff to the default "let's simply
+ // infer the range from the expression's type".
+ return infer(Sym->getType());
+ }
+
+ RangeSet VisitSymIntExpr(const SymIntExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+ RangeSet VisitIntSymExpr(const IntSymExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+ RangeSet VisitSymSymExpr(const SymSymExpr *Sym) {
+ return VisitBinaryOperator(Sym);
+ }
+
+private:
+ SymbolicRangeInferrer(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef S)
+ : ValueFactory(BV), RangeFactory(F), State(S) {}
+
+ /// Infer range information from the given integer constant.
+ ///
+ /// It's not a real "inference", but is here for operating with
+ /// sub-expressions in a more polymorphic manner.
+ RangeSet inferAs(const llvm::APSInt &Val, QualType) {
+ return {RangeFactory, Val};
+ }
+
+ /// Infer range information from symbol in the context of the given type.
+ RangeSet inferAs(SymbolRef Sym, QualType DestType) {
+ QualType ActualType = Sym->getType();
+ // Check that we can reason about the symbol at all.
+ if (ActualType->isIntegralOrEnumerationType() ||
+ Loc::isLocType(ActualType)) {
+ return infer(Sym);
+ }
+ // Otherwise, let's simply infer from the destination type.
+ // We couldn't figure out nothing else about that expression.
+ return infer(DestType);
+ }
+
+ RangeSet infer(SymbolRef Sym) {
+ const RangeSet *AssociatedRange = State->get<ConstraintRange>(Sym);
+
+ // If Sym is a difference of symbols A - B, then maybe we have range set
+ // stored for B - A.
+ const RangeSet *RangeAssociatedWithNegatedSym =
+ getRangeForMinusSymbol(State, Sym);
+
+ // If we have range set stored for both A - B and B - A then calculate the
+ // effective range set by intersecting the range set for A - B and the
+ // negated range set of B - A.
+ if (AssociatedRange && RangeAssociatedWithNegatedSym)
+ return AssociatedRange->Intersect(
+ ValueFactory, RangeFactory,
+ RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory));
+
+ if (AssociatedRange)
+ return *AssociatedRange;
+
+ if (RangeAssociatedWithNegatedSym)
+ return RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory);
+
+ // If Sym is a comparison expression (except <=>),
+ // find any other comparisons with the same operands.
+ // See function description.
+ const RangeSet CmpRangeSet = getRangeForComparisonSymbol(State, Sym);
+ if (!CmpRangeSet.isEmpty())
+ return CmpRangeSet;
+
+ return Visit(Sym);
+ }
+
+ /// Infer range information solely from the type.
+ RangeSet infer(QualType T) {
+ // Lazily generate a new RangeSet representing all possible values for the
+ // given symbol type.
+ RangeSet Result(RangeFactory, ValueFactory.getMinValue(T),
+ ValueFactory.getMaxValue(T));
+
+ // References are known to be non-zero.
+ if (T->isReferenceType())
+ return assumeNonZero(Result, T);
+
+ return Result;
+ }
+
+ template <class BinarySymExprTy>
+ RangeSet VisitBinaryOperator(const BinarySymExprTy *Sym) {
+ // TODO #1: VisitBinaryOperator implementation might not make a good
+ // use of the inferred ranges. In this case, we might be calculating
+ // everything for nothing. This being said, we should introduce some
+ // sort of laziness mechanism here.
+ //
+ // TODO #2: We didn't go into the nested expressions before, so it
+ // might cause us spending much more time doing the inference.
+ // This can be a problem for deeply nested expressions that are
+ // involved in conditions and get tested continuously. We definitely
+ // need to address this issue and introduce some sort of caching
+ // in here.
+ QualType ResultType = Sym->getType();
+ return VisitBinaryOperator(inferAs(Sym->getLHS(), ResultType),
+ Sym->getOpcode(),
+ inferAs(Sym->getRHS(), ResultType), ResultType);
+ }
+
+ 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);
+ }
+ }
+
+ //===----------------------------------------------------------------------===//
+ // Ranges and operators
+ //===----------------------------------------------------------------------===//
+
+ /// Return a rough approximation of the given range set.
+ ///
+ /// For the range set:
+ /// { [x_0, y_0], [x_1, y_1], ... , [x_N, y_N] }
+ /// it will return the range [x_0, y_N].
+ static Range fillGaps(RangeSet Origin) {
+ assert(!Origin.isEmpty());
+ return {Origin.getMinValue(), Origin.getMaxValue()};
+ }
+
+ /// 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) {
+ if (To.testInRange(Origin.From(), false) != APSIntType::RTR_Within ||
+ To.testInRange(Origin.To(), false) != APSIntType::RTR_Within) {
+ return llvm::None;
+ }
+ return Range(ValueFactory.Convert(To, Origin.From()),
+ ValueFactory.Convert(To, Origin.To()));
+ }
+
+ 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();
+ }
+
+ Range CoarseLHS = fillGaps(LHS);
+ Range CoarseRHS = fillGaps(RHS);
+
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+
+ // We need to convert ranges to the resulting type, so we can compare values
+ // and combine them in a meaningful (in terms of the given operation) way.
+ auto ConvertedCoarseLHS = convert(CoarseLHS, ResultType);
+ auto ConvertedCoarseRHS = convert(CoarseRHS, ResultType);
+
+ // It is hard to reason about ranges when conversion changes
+ // borders of the ranges.
+ if (!ConvertedCoarseLHS || !ConvertedCoarseRHS) {
+ return infer(T);
+ }
+
+ return VisitBinaryOperator<Op>(*ConvertedCoarseLHS, *ConvertedCoarseRHS, T);
+ }
+
+ template <BinaryOperator::Opcode Op>
+ RangeSet VisitBinaryOperator(Range LHS, Range RHS, QualType T) {
+ return infer(T);
+ }
+
+ /// Return a symmetrical range for the given range and type.
+ ///
+ /// If T is signed, return the smallest range [-x..x] that covers the original
+ /// range, or [-min(T), max(T)] if the aforementioned symmetric range doesn't
+ /// exist due to original range covering min(T)).
+ ///
+ /// If T is unsigned, return the smallest range [0..x] that covers the
+ /// original range.
+ Range getSymmetricalRange(Range Origin, QualType T) {
+ APSIntType RangeType = ValueFactory.getAPSIntType(T);
+
+ if (RangeType.isUnsigned()) {
+ return Range(ValueFactory.getMinValue(RangeType), Origin.To());
+ }
+
+ if (Origin.From().isMinSignedValue()) {
+ // If mini is a minimal signed value, absolute value of it is greater
+ // than the maximal signed value. In order to avoid these
+ // complications, we simply return the whole range.
+ return {ValueFactory.getMinValue(RangeType),
+ ValueFactory.getMaxValue(RangeType)};
+ }
+
+ // At this point, we are sure that the type is signed and we can safely
+ // use unary - operator.
+ //
+ // While calculating absolute maximum, we can use the following formula
+ // because of these reasons:
+ // * If From >= 0 then To >= From and To >= -From.
+ // AbsMax == To == max(To, -From)
+ // * If To <= 0 then -From >= -To and -From >= From.
+ // AbsMax == -From == max(-From, To)
+ // * Otherwise, From <= 0, To >= 0, and
+ // AbsMax == max(abs(From), abs(To))
+ llvm::APSInt AbsMax = std::max(-Origin.From(), Origin.To());
+
+ // Intersection is guaranteed to be non-empty.
+ return {ValueFactory.getValue(-AbsMax), ValueFactory.getValue(AbsMax)};
+ }
+
+ /// Return a range set subtracting zero from \p Domain.
+ RangeSet assumeNonZero(RangeSet Domain, QualType T) {
+ APSIntType IntType = ValueFactory.getAPSIntType(T);
+ return Domain.Intersect(ValueFactory, RangeFactory,
+ ++IntType.getZeroValue(), --IntType.getZeroValue());
+ }
+
+ // FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
+ // obtain the negated symbolic expression instead of constructing the
+ // symbol manually. This will allow us to support finding ranges of not
+ // only negated SymSymExpr-type expressions, but also of other, simpler
+ // expressions which we currently do not know how to negate.
+ const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym) {
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ if (SSE->getOpcode() == BO_Sub) {
+ QualType T = Sym->getType();
+ SymbolManager &SymMgr = State->getSymbolManager();
+ SymbolRef negSym =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T);
+
+ if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
+ // Unsigned range set cannot be negated, unless it is [0, 0].
+ if (T->isUnsignedIntegerOrEnumerationType() ||
+ T->isSignedIntegerOrEnumerationType())
+ return negV;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ // Returns ranges only for binary comparison operators (except <=>)
+ // when left and right operands are symbolic values.
+ // Finds any other comparisons with the same operands.
+ // Then do logical calculations and refuse impossible branches.
+ // E.g. (x < y) and (x > y) at the same time are impossible.
+ // E.g. (x >= y) and (x != y) at the same time makes (x > y) true only.
+ // E.g. (x == y) and (y == x) are just reversed but the same.
+ // It covers all possible combinations (see CmpOpTable description).
+ // Note that `x` and `y` can also stand for subexpressions,
+ // not only for actual symbols.
+ RangeSet getRangeForComparisonSymbol(ProgramStateRef State, SymbolRef Sym) {
+ const RangeSet EmptyRangeSet = RangeFactory.getEmptySet();
+
+ auto SSE = dyn_cast<SymSymExpr>(Sym);
+ if (!SSE)
+ return EmptyRangeSet;
+
+ BinaryOperatorKind CurrentOP = SSE->getOpcode();
+
+ // We currently do not support <=> (C++20).
+ if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
+ return EmptyRangeSet;
+
+ static const OperatorRelationsTable CmpOpTable{};
+
+ const SymExpr *LHS = SSE->getLHS();
+ const SymExpr *RHS = SSE->getRHS();
+ QualType T = SSE->getType();
+
+ SymbolManager &SymMgr = State->getSymbolManager();
+ const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
+ const llvm::APSInt &One = ValueFactory.getValue(1, T);
+ const RangeSet TrueRangeSet(RangeFactory, One, One);
+ const RangeSet FalseRangeSet(RangeFactory, Zero, Zero);
+
+ int UnknownStates = 0;
+
+ // Loop goes through all of the columns exept the last one ('UnknownX2').
+ // We treat `UnknownX2` column separately at the end of the loop body.
+ for (size_t i = 0; i < CmpOpTable.getCmpOpCount(); ++i) {
+
+ // Let's find an expression e.g. (x < y).
+ BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i);
+ const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
+ const RangeSet *QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+
+ // If ranges were not previously found,
+ // try to find a reversed expression (y > x).
+ if (!QueriedRangeSet) {
+ const BinaryOperatorKind ROP =
+ BinaryOperator::reverseComparisonOp(QueriedOP);
+ SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
+ QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ }
+
+ if (!QueriedRangeSet || QueriedRangeSet->isEmpty())
+ continue;
+
+ const llvm::APSInt *ConcreteValue = QueriedRangeSet->getConcreteValue();
+ const bool isInFalseBranch =
+ ConcreteValue ? (*ConcreteValue == 0) : false;
+
+ // If it is a false branch, we shall be guided by opposite operator,
+ // because the table is made assuming we are in the true branch.
+ // E.g. when (x <= y) is false, then (x > y) is true.
+ if (isInFalseBranch)
+ QueriedOP = BinaryOperator::negateComparisonOp(QueriedOP);
+
+ OperatorRelationsTable::TriStateKind BranchState =
+ CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
+
+ if (BranchState == OperatorRelationsTable::Unknown) {
+ if (++UnknownStates == 2)
+ // If we met both Unknown states.
+ // if (x <= y) // assume true
+ // if (x != y) // assume true
+ // if (x < y) // would be also true
+ // Get a state from `UnknownX2` column.
+ BranchState = CmpOpTable.getCmpOpStateForUnknownX2(CurrentOP);
+ else
+ continue;
+ }
+
+ return (BranchState == OperatorRelationsTable::True) ? TrueRangeSet
+ : FalseRangeSet;
+ }
+
+ return EmptyRangeSet;
+ }
+
+ BasicValueFactory &ValueFactory;
+ RangeSet::Factory &RangeFactory;
+ ProgramStateRef State;
+};
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
+ QualType T) {
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+ llvm::APSInt Zero = ResultType.getZeroValue();
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ bool IsLHSNegative = LHS.To() < Zero;
+ bool IsRHSNegative = RHS.To() < Zero;
+
+ // Check if both ranges have the same sign.
+ if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
+ (IsLHSNegative && IsRHSNegative)) {
+ // The result is definitely greater or equal than any of the operands.
+ const llvm::APSInt &Min = std::max(LHS.From(), RHS.From());
+
+ // We estimate maximal value for positives as the maximal value for the
+ // given type. For negatives, we estimate it with -1 (e.g. 0x11111111).
+ //
+ // TODO: We basically, limit the resulting range from below, but don't do
+ // anything with the upper bound.
+ //
+ // For positive operands, it can be done as follows: for the upper
+ // bound of LHS and RHS we calculate the most significant bit set.
+ // Let's call it the N-th bit. Then we can estimate the maximal
+ // number to be 2^(N+1)-1, i.e. the number with all the bits up to
+ // the N-th bit set.
+ const llvm::APSInt &Max = IsLHSNegative
+ ? ValueFactory.getValue(--Zero)
+ : ValueFactory.getMaxValue(ResultType);
+
+ return {RangeFactory, ValueFactory.getValue(Min), Max};
+ }
+
+ // Otherwise, let's check if at least one of the operands is negative.
+ if (IsLHSNegative || IsRHSNegative) {
+ // This means that the result is definitely negative as well.
+ return {RangeFactory, ValueFactory.getMinValue(ResultType),
+ ValueFactory.getValue(--Zero)};
+ }
+
+ RangeSet DefaultRange = infer(T);
+
+ // It is pretty hard to reason about operands with different signs
+ // (and especially with possibly different signs). We simply check if it
+ // can be zero. In order to conclude that the result could not be zero,
+ // at least one of the operands should be definitely not zero itself.
+ if (!LHS.Includes(Zero) || !RHS.Includes(Zero)) {
+ return assumeNonZero(DefaultRange, T);
+ }
+
+ // Nothing much else to do here.
+ return DefaultRange;
+}
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_And>(Range LHS,
+ Range RHS,
+ QualType T) {
+ APSIntType ResultType = ValueFactory.getAPSIntType(T);
+ llvm::APSInt Zero = ResultType.getZeroValue();
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ bool IsLHSNegative = LHS.To() < Zero;
+ bool IsRHSNegative = RHS.To() < Zero;
+
+ // Check if both ranges have the same sign.
+ if ((IsLHSPositiveOrZero && IsRHSPositiveOrZero) ||
+ (IsLHSNegative && IsRHSNegative)) {
+ // The result is definitely less or equal than any of the operands.
+ const llvm::APSInt &Max = std::min(LHS.To(), RHS.To());
+
+ // We conservatively estimate lower bound to be the smallest positive
+ // or negative value corresponding to the sign of the operands.
+ const llvm::APSInt &Min = IsLHSNegative
+ ? ValueFactory.getMinValue(ResultType)
+ : ValueFactory.getValue(Zero);
+
+ return {RangeFactory, Min, Max};
+ }
+
+ // Otherwise, let's check if at least one of the operands is positive.
+ if (IsLHSPositiveOrZero || IsRHSPositiveOrZero) {
+ // This makes result definitely positive.
+ //
+ // We can also reason about a maximal value by finding the maximal
+ // value of the positive operand.
+ const llvm::APSInt &Max = IsLHSPositiveOrZero ? LHS.To() : RHS.To();
+
+ // The minimal value on the other hand is much harder to reason about.
+ // The only thing we know for sure is that the result is positive.
+ return {RangeFactory, ValueFactory.getValue(Zero),
+ ValueFactory.getValue(Max)};
+ }
+
+ // Nothing much else to do here.
+ return infer(T);
+}
+
+template <>
+RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
+ Range RHS,
+ QualType T) {
+ llvm::APSInt Zero = ValueFactory.getAPSIntType(T).getZeroValue();
+
+ Range ConservativeRange = getSymmetricalRange(RHS, T);
+
+ llvm::APSInt Max = ConservativeRange.To();
+ llvm::APSInt Min = ConservativeRange.From();
+
+ if (Max == Zero) {
+ // It's an undefined behaviour to divide by 0 and it seems like we know
+ // for sure that RHS is 0. Let's say that the resulting range is
+ // simply infeasible for that matter.
+ return RangeFactory.getEmptySet();
+ }
+
+ // At this point, our conservative range is closed. The result, however,
+ // couldn't be greater than the RHS' maximal absolute value. Because of
+ // this reason, we turn the range into open (or half-open in case of
+ // unsigned integers).
+ //
+ // While we operate on integer values, an open interval (a, b) can be easily
+ // represented by the closed interval [a + 1, b - 1]. And this is exactly
+ // what we do next.
+ //
+ // If we are dealing with unsigned case, we shouldn't move the lower bound.
+ if (Min.isSigned()) {
+ ++Min;
+ }
+ --Max;
+
+ bool IsLHSPositiveOrZero = LHS.From() >= Zero;
+ bool IsRHSPositiveOrZero = RHS.From() >= Zero;
+
+ // Remainder operator results with negative operands is implementation
+ // defined. Positive cases are much easier to reason about though.
+ if (IsLHSPositiveOrZero && IsRHSPositiveOrZero) {
+ // If maximal value of LHS is less than maximal value of RHS,
+ // the result won't get greater than LHS.To().
+ Max = std::min(LHS.To(), Max);
+ // We want to check if it is a situation similar to the following:
+ //
+ // <------------|---[ LHS ]--------[ RHS ]----->
+ // -INF 0 +INF
+ //
+ // In this situation, we can conclude that (LHS / RHS) == 0 and
+ // (LHS % RHS) == LHS.
+ Min = LHS.To() < RHS.From() ? LHS.From() : Zero;
+ }
+
+ // Nevertheless, the symmetrical range for RHS is a conservative estimate
+ // for any sign of either LHS, or RHS.
+ return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
+}
+
class RangeConstraintManager : public RangedConstraintManager {
public:
- RangeConstraintManager(SubEngine *SE, SValBuilder &SVB)
- : RangedConstraintManager(SE, SVB) {}
+ RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
+ : RangedConstraintManager(EE, SVB) {}
//===------------------------------------------------------------------===//
// Implementation for interface from ConstraintManager.
@@ -305,8 +971,6 @@ private:
RangeSet::Factory F;
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
- const RangeSet* getRangeForMinusSymbol(ProgramStateRef State,
- SymbolRef Sym);
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
@@ -323,13 +987,13 @@ private:
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
-
};
} // end anonymous namespace
std::unique_ptr<ConstraintManager>
-ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+ento::CreateRangeConstraintManager(ProgramStateManager &StMgr,
+ ExprEngine *Eng) {
return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
@@ -429,113 +1093,9 @@ RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
return Changed ? State->set<ConstraintRange>(CR) : State;
}
-/// Return a range set subtracting zero from \p Domain.
-static RangeSet assumeNonZero(
- BasicValueFactory &BV,
- RangeSet::Factory &F,
- SymbolRef Sym,
- RangeSet Domain) {
- APSIntType IntType = BV.getAPSIntType(Sym->getType());
- return Domain.Intersect(BV, F, ++IntType.getZeroValue(),
- --IntType.getZeroValue());
-}
-
-/// Apply implicit constraints for bitwise OR- and AND-.
-/// For unsigned types, bitwise OR with a constant always returns
-/// a value greater-or-equal than the constant, and bitwise AND
-/// returns a value less-or-equal then the constant.
-///
-/// Pattern matches the expression \p Sym against those rule,
-/// and applies the required constraints.
-/// \p Input Previously established expression range set
-static RangeSet applyBitwiseConstraints(
- BasicValueFactory &BV,
- RangeSet::Factory &F,
- RangeSet Input,
- const SymIntExpr* SIE) {
- QualType T = SIE->getType();
- bool IsUnsigned = T->isUnsignedIntegerType();
- const llvm::APSInt &RHS = SIE->getRHS();
- const llvm::APSInt &Zero = BV.getAPSIntType(T).getZeroValue();
- BinaryOperator::Opcode Operator = SIE->getOpcode();
-
- // For unsigned types, the output of bitwise-or is bigger-or-equal than RHS.
- if (Operator == BO_Or && IsUnsigned)
- return Input.Intersect(BV, F, RHS, BV.getMaxValue(T));
-
- // Bitwise-or with a non-zero constant is always non-zero.
- if (Operator == BO_Or && RHS != Zero)
- return assumeNonZero(BV, F, SIE, Input);
-
- // For unsigned types, or positive RHS,
- // bitwise-and output is always smaller-or-equal than RHS (assuming two's
- // complement representation of signed types).
- if (Operator == BO_And && (IsUnsigned || RHS >= Zero))
- return Input.Intersect(BV, F, BV.getMinValue(T), RHS);
-
- return Input;
-}
-
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
SymbolRef Sym) {
- ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym);
-
- // If Sym is a difference of symbols A - B, then maybe we have range set
- // stored for B - A.
- BasicValueFactory &BV = getBasicVals();
- const RangeSet *R = getRangeForMinusSymbol(State, Sym);
-
- // If we have range set stored for both A - B and B - A then calculate the
- // effective range set by intersecting the range set for A - B and the
- // negated range set of B - A.
- if (V && R)
- return V->Intersect(BV, F, R->Negate(BV, F));
- if (V)
- return *V;
- if (R)
- return R->Negate(BV, F);
-
- // Lazily generate a new RangeSet representing all possible values for the
- // given symbol type.
- QualType T = Sym->getType();
-
- RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T));
-
- // References are known to be non-zero.
- if (T->isReferenceType())
- return assumeNonZero(BV, F, Sym, Result);
-
- // Known constraints on ranges of bitwise expressions.
- if (const SymIntExpr* SIE = dyn_cast<SymIntExpr>(Sym))
- return applyBitwiseConstraints(BV, F, Result, SIE);
-
- return Result;
-}
-
-// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
-// obtain the negated symbolic expression instead of constructing the
-// symbol manually. This will allow us to support finding ranges of not
-// only negated SymSymExpr-type expressions, but also of other, simpler
-// expressions which we currently do not know how to negate.
-const RangeSet*
-RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State,
- SymbolRef Sym) {
- if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- if (SSE->getOpcode() == BO_Sub) {
- QualType T = Sym->getType();
- SymbolManager &SymMgr = State->getSymbolManager();
- SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub,
- SSE->getLHS(), T);
- if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
- // Unsigned range set cannot be negated, unless it is [0, 0].
- if ((negV->getConcreteValue() &&
- (*negV->getConcreteValue() == 0)) ||
- T->isSignedIntegerOrEnumerationType())
- return negV;
- }
- }
- }
- return nullptr;
+ return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
}
//===------------------------------------------------------------------------===
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 4797f564a837..57fde32bc01d 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -23,10 +23,11 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/raw_ostream.h"
@@ -381,7 +382,7 @@ public:
: StoreManager(mgr), Features(f),
RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()),
SmallStructLimit(0) {
- SubEngine &Eng = StateMgr.getOwningEngine();
+ ExprEngine &Eng = StateMgr.getOwningEngine();
AnalyzerOptions &Options = Eng.getAnalysisManager().options;
SmallStructLimit = Options.RegionStoreSmallStructLimit;
}
@@ -622,15 +623,6 @@ public: // Part of public interface to class.
SymbolReaper& SymReaper) override;
//===------------------------------------------------------------------===//
- // Region "extents".
- //===------------------------------------------------------------------===//
-
- // FIXME: This method will soon be eliminated; see the note in Store.h.
- DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state,
- const MemRegion* R,
- QualType EleTy) override;
-
- //===------------------------------------------------------------------===//
// Utility methods.
//===------------------------------------------------------------------===//
@@ -876,7 +868,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
// Find the length (in bits) of the region being invalidated.
uint64_t Length = UINT64_MAX;
- SVal Extent = Top->getExtent(SVB);
+ SVal Extent = Top->getMemRegionManager().getStaticSize(Top, SVB);
if (Optional<nonloc::ConcreteInt> ExtentCI =
Extent.getAs<nonloc::ConcreteInt>()) {
const llvm::APSInt &ExtentInt = ExtentCI->getValue();
@@ -1387,37 +1379,6 @@ RegionStoreManager::invalidateRegions(Store store,
}
//===----------------------------------------------------------------------===//
-// Extents for regions.
-//===----------------------------------------------------------------------===//
-
-DefinedOrUnknownSVal
-RegionStoreManager::getSizeInElements(ProgramStateRef state,
- const MemRegion *R,
- QualType EleTy) {
- SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder);
- const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
- if (!SizeInt)
- return UnknownVal();
-
- CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue());
-
- if (Ctx.getAsVariableArrayType(EleTy)) {
- // FIXME: We need to track extra state to properly record the size
- // of VLAs. Returning UnknownVal here, however, is a stop-gap so that
- // we don't have a divide-by-zero below.
- return UnknownVal();
- }
-
- CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy);
-
- // If a variable is reinterpreted as a type that doesn't fit into a larger
- // type evenly, round it down.
- // This is a signed value, since it's used in arithmetic with signed indices.
- return svalBuilder.makeIntVal(RegionSize / EleSize,
- svalBuilder.getArrayIndexType());
-}
-
-//===----------------------------------------------------------------------===//
// Location and region casting.
//===----------------------------------------------------------------------===//
@@ -1667,10 +1628,6 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
- // We do not currently model bindings of the CompoundLiteralregion.
- if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
- return UnknownVal();
-
// Check if the region has a binding.
if (const Optional<SVal> &V = B.getDirectBinding(R))
return *V;
diff --git a/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
index 6ad12ca0a688..7395622a659c 100644
--- a/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -13,6 +13,6 @@ using namespace clang;
using namespace ento;
std::unique_ptr<ConstraintManager>
-ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, ExprEngine *Eng) {
return std::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder());
}
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 3a5841137e1a..c00a2c8ba8a2 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -24,12 +24,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/APSInt.h"
diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index 12332aaf936f..8c2e85601576 100644
--- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/Basic/FileManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
@@ -49,8 +50,14 @@ public:
void ento::createSarifDiagnosticConsumer(
AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
const std::string &Output, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &) {
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (Output.empty())
+ return;
+
C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts()));
+ createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, Output, PP, CTU);
}
static StringRef getFileName(const FileEntry &FE) {
@@ -106,7 +113,7 @@ static std::string fileNameToURI(StringRef Filename) {
}
});
- return Ret.str().str();
+ return std::string(Ret);
}
static json::Object createArtifactLocation(const FileEntry &FE) {
@@ -322,7 +329,7 @@ static json::Object createRule(const PathDiagnostic &Diag) {
{"name", CheckName},
{"id", CheckName}};
- std::string RuleURI = getRuleHelpURIStr(CheckName);
+ std::string RuleURI = std::string(getRuleHelpURIStr(CheckName));
if (!RuleURI.empty())
Ret["helpUri"] = RuleURI;
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 85f60231a276..3709106ad44c 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -44,8 +44,8 @@ ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
NonLoc Cond, bool Assumption) {
State = assumeAux(State, Cond, Assumption);
- if (NotifyAssumeClients && SU)
- return SU->processAssume(State, Cond, Assumption);
+ if (NotifyAssumeClients && EE)
+ return EE->processAssume(State, Cond, Assumption);
return State;
}
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 84c52f53ca5e..2e269f6a596e 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -13,8 +13,8 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
using namespace clang;
@@ -652,6 +652,11 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
if (LHSValue == 0)
return evalCastFromNonLoc(lhs, resultTy);
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ case BO_Rem:
+ // 0 % x == 0
+ if (LHSValue == 0)
+ return makeZeroVal(resultTy);
+ LLVM_FALLTHROUGH;
default:
return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
}
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index b33129c88cea..ea617bbeeba1 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -134,7 +134,8 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
case MemRegion::CXXDerivedObjectRegionKind:
diff --git a/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
deleted file mode 100644
index d7ddd9cf4610..000000000000
--- a/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
-
-using namespace clang::ento;
-
-void SubEngine::anchor() { }
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 675209f6fd7e..6ca7aec9caec 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -34,45 +34,27 @@ using namespace ento;
void SymExpr::anchor() {}
-LLVM_DUMP_METHOD void SymExpr::dump() const {
- dumpToStream(llvm::errs());
-}
+LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
-void SymIntExpr::dumpToStream(raw_ostream &os) const {
- os << '(';
- getLHS()->dumpToStream(os);
- os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode()) << ' ';
- if (getRHS().isUnsigned())
- os << getRHS().getZExtValue();
- else
- os << getRHS().getSExtValue();
- if (getRHS().isUnsigned())
- os << 'U';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
+ OS << '(';
+ Sym->dumpToStream(OS);
+ OS << ')';
}
-void IntSymExpr::dumpToStream(raw_ostream &os) const {
- if (getLHS().isUnsigned())
- os << getLHS().getZExtValue();
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ const llvm::APSInt &Value) {
+ if (Value.isUnsigned())
+ OS << Value.getZExtValue();
else
- os << getLHS().getSExtValue();
- if (getLHS().isUnsigned())
- os << 'U';
- os << ' '
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
- getRHS()->dumpToStream(os);
- os << ')';
+ OS << Value.getSExtValue();
+ if (Value.isUnsigned())
+ OS << 'U';
}
-void SymSymExpr::dumpToStream(raw_ostream &os) const {
- os << '(';
- getLHS()->dumpToStream(os);
- os << ") "
- << BinaryOperator::getOpcodeStr(getOpcode())
- << " (";
- getRHS()->dumpToStream(os);
- os << ')';
+void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS,
+ BinaryOperator::Opcode Op) {
+ OS << ' ' << BinaryOperator::getOpcodeStr(Op) << ' ';
}
void SymbolCast::dumpToStream(raw_ostream &os) const {
@@ -329,7 +311,7 @@ QualType SymbolDerived::getType() const {
}
QualType SymbolExtent::getType() const {
- ASTContext &Ctx = R->getMemRegionManager()->getContext();
+ ASTContext &Ctx = R->getMemRegionManager().getContext();
return Ctx.getSizeType();
}
@@ -341,10 +323,6 @@ QualType SymbolRegionValue::getType() const {
return R->getValueType();
}
-SymbolManager::~SymbolManager() {
- llvm::DeleteContainerSeconds(SymbolDependencies);
-}
-
bool SymbolManager::canSymbolicate(QualType T) {
T = T.getCanonicalType();
@@ -362,13 +340,9 @@ bool SymbolManager::canSymbolicate(QualType T) {
void SymbolManager::addSymbolDependency(const SymbolRef Primary,
const SymbolRef Dependent) {
- SymbolDependTy::iterator I = SymbolDependencies.find(Primary);
- SymbolRefSmallVectorTy *dependencies = nullptr;
- if (I == SymbolDependencies.end()) {
- dependencies = new SymbolRefSmallVectorTy();
- SymbolDependencies[Primary] = dependencies;
- } else {
- dependencies = I->second;
+ auto &dependencies = SymbolDependencies[Primary];
+ if (!dependencies) {
+ dependencies = std::make_unique<SymbolRefSmallVectorTy>();
}
dependencies->push_back(Dependent);
}
@@ -378,7 +352,7 @@ const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols(
SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary);
if (I == SymbolDependencies.end())
return nullptr;
- return I->second;
+ return I->second.get();
}
void SymbolReaper::markDependentsLive(SymbolRef sym) {
@@ -542,6 +516,11 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
if (!Loc)
return true;
+ // Anonymous parameters of an inheriting constructor are live for the entire
+ // duration of the constructor.
+ if (isa<CXXInheritedCtorInitExpr>(Loc))
+ return true;
+
if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
return true;
diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
new file mode 100644
index 000000000000..f4c7e5978e19
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -0,0 +1,156 @@
+//===--- TextDiagnostics.cpp - Text Diagnostics for Paths -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the TextDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/PathDiagnostic.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+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
+/// diagnostics in textual format for the 'text' output type.
+class TextDiagnostics : public PathDiagnosticConsumer {
+ DiagnosticsEngine &DiagEng;
+ const LangOptions &LO;
+ const bool IncludePath = false;
+ const bool ShouldEmitAsError = false;
+ const bool ApplyFixIts = false;
+ const bool ShouldDisplayCheckerName = false;
+
+public:
+ TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO,
+ bool ShouldIncludePath, const AnalyzerOptions &AnOpts)
+ : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath),
+ ShouldEmitAsError(AnOpts.AnalyzerWerror),
+ ApplyFixIts(AnOpts.ShouldApplyFixIts),
+ ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {}
+ ~TextDiagnostics() override {}
+
+ StringRef getName() const override { return "TextDiagnostics"; }
+
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override { return true; }
+
+ PathGenerationScheme getGenerationScheme() const override {
+ return IncludePath ? Minimal : None;
+ }
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override {
+ unsigned WarnID =
+ ShouldEmitAsError
+ ? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
+ : DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
+ unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
+ SourceManager &SM = DiagEng.getSourceManager();
+
+ Replacements Repls;
+ auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
+ if (!ApplyFixIts) {
+ DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
+ return;
+ }
+
+ DiagEng.Report(Loc, ID) << String << Ranges;
+ for (const FixItHint &Hint : Fixits) {
+ Replacement Repl(SM, Hint.RemoveRange, Hint.CodeToInsert);
+
+ if (llvm::Error Err = Repls.add(Repl)) {
+ llvm::errs() << "Error applying replacement " << Repl.toString()
+ << ": " << Err << "\n";
+ }
+ }
+ };
+
+ for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
+ E = Diags.end();
+ I != E; ++I) {
+ const PathDiagnostic *PD = *I;
+ std::string WarningMsg =
+ (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "")
+ .str();
+
+ reportPiece(WarnID, PD->getLocation().asLocation(),
+ (PD->getShortDescription() + WarningMsg).str(),
+ PD->path.back()->getRanges(), PD->path.back()->getFixits());
+
+ // First, add extra notes, even if paths should not be included.
+ for (const auto &Piece : PD->path) {
+ if (!isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(),
+ Piece->getFixits());
+ }
+
+ if (!IncludePath)
+ continue;
+
+ // Then, add the path notes if necessary.
+ PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
+ for (const auto &Piece : FlatPath) {
+ if (isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(),
+ Piece->getFixits());
+ }
+ }
+
+ if (!ApplyFixIts || Repls.empty())
+ return;
+
+ Rewriter Rewrite(SM, LO);
+ if (!applyAllReplacements(Repls, Rewrite)) {
+ llvm::errs() << "An error occured during applying fix-it.\n";
+ }
+
+ Rewrite.overwriteChangedFiles();
+ }
+};
+} // end anonymous namespace
+
+void ento::createTextPathDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Prefix, const clang::Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
+ /*ShouldIncludePath*/ true, AnalyzerOpts));
+}
+
+void ento::createTextMinimalPathDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Prefix, const clang::Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
+ /*ShouldIncludePath*/ false,
+ AnalyzerOpts));
+}