aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h2
-rw-r--r--lib/StaticAnalyzer/Checkers/AllocationState.h34
-rw-r--r--lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp62
-rw-r--r--lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/CMakeLists.txt7
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp470
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp79
-rw-r--r--lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp152
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp61
-rw-r--r--lib/StaticAnalyzer/Checkers/ChrootChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp229
-rw-r--r--lib/StaticAnalyzer/Checkers/GTestChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp60
-rw-r--r--lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp252
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorChecker.cpp512
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp12
-rw-r--r--lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h2
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp328
-rw-r--r--lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp88
-rw-r--r--lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp140
-rw-r--r--lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp44
-rw-r--r--lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp209
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp16
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/PaddingChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp22
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp77
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp217
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp36
-rw-r--r--lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/TraversalChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp90
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp33
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp34
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp688
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/ValistChecker.cpp28
-rw-r--r--lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp113
-rw-r--r--lib/StaticAnalyzer/Core/BasicValueFactory.cpp62
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp2538
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp1245
-rw-r--r--lib/StaticAnalyzer/Core/CMakeLists.txt5
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp240
-rw-r--r--lib/StaticAnalyzer/Core/CheckerContext.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/CheckerHelpers.cpp29
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp310
-rw-r--r--lib/StaticAnalyzer/Core/CheckerRegistry.cpp72
-rw-r--r--lib/StaticAnalyzer/Core/ConstraintManager.cpp11
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp257
-rw-r--r--lib/StaticAnalyzer/Core/DynamicTypeMap.cpp38
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp95
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp47
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp1088
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp49
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp530
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp224
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp94
-rw-r--r--lib/StaticAnalyzer/Core/FunctionSummary.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp266
-rw-r--r--lib/StaticAnalyzer/Core/LoopUnrolling.cpp20
-rw-r--r--lib/StaticAnalyzer/Core/LoopWidening.cpp33
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp303
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp409
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp142
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp65
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp455
-rw-r--r--lib/StaticAnalyzer/Core/RangedConstraintManager.cpp25
-rw-r--r--lib/StaticAnalyzer/Core/RangedConstraintManager.h102
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp140
-rw-r--r--lib/StaticAnalyzer/Core/SMTConstraintManager.cpp181
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp85
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp65
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp260
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp96
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp46
-rw-r--r--lib/StaticAnalyzer/Core/WorkList.cpp254
-rw-r--r--lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp2053
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp174
-rw-r--r--lib/StaticAnalyzer/Frontend/CMakeLists.txt3
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp13
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelConsumer.cpp2
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.cpp6
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.h4
116 files changed, 10343 insertions, 6201 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h
index 048418ef62db..62b7fab0739a 100644
--- a/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h
+++ b/lib/StaticAnalyzer/Checkers/AllocationDiagnostics.h
@@ -18,7 +18,7 @@
namespace clang { namespace ento {
-/// \brief Returns true if leak diagnostics should directly reference
+/// Returns true if leak diagnostics should directly reference
/// the allocatin site (where possible).
///
/// The default is false.
diff --git a/lib/StaticAnalyzer/Checkers/AllocationState.h b/lib/StaticAnalyzer/Checkers/AllocationState.h
new file mode 100644
index 000000000000..a6908bd7a651
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/AllocationState.h
@@ -0,0 +1,34 @@
+//===--- AllocationState.h ------------------------------------- *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+
+namespace allocation_state {
+
+ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
+ const Expr *Origin);
+
+/// This function provides an additional visitor that augments the bug report
+/// with information relevant to memory errors caused by the misuse of
+/// AF_InnerBuffer symbols.
+std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym);
+
+} // end namespace allocation_state
+
+} // end namespace ento
+} // end namespace clang
+
+#endif
diff --git a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
index 90d5c0e36a47..e4cdc500de6a 100644
--- a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -15,8 +15,10 @@
//===----------------------------------------------------------------------===//
#include "ClangSACheckers.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
using namespace clang;
@@ -29,8 +31,17 @@ class AnalysisOrderChecker
check::PostStmt<CastExpr>,
check::PreStmt<ArraySubscriptExpr>,
check::PostStmt<ArraySubscriptExpr>,
+ check::PreStmt<CXXNewExpr>,
+ check::PostStmt<CXXNewExpr>,
+ check::PreStmt<OffsetOfExpr>,
+ check::PostStmt<OffsetOfExpr>,
+ check::PreCall,
+ check::PostCall,
+ check::NewAllocator,
check::Bind,
- check::RegionChanges> {
+ check::RegionChanges,
+ check::LiveSymbols> {
+
bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
return Opts.getBooleanOption("*", false, this) ||
Opts.getBooleanOption(CallbackName, false, this);
@@ -72,11 +83,60 @@ public:
llvm::errs() << "PostStmt<ArraySubscriptExpr>\n";
}
+ void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtCXXNewExpr"))
+ llvm::errs() << "PreStmt<CXXNewExpr>\n";
+ }
+
+ void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtCXXNewExpr"))
+ llvm::errs() << "PostStmt<CXXNewExpr>\n";
+ }
+
+ void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtOffsetOfExpr"))
+ llvm::errs() << "PreStmt<OffsetOfExpr>\n";
+ }
+
+ void checkPostStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtOffsetOfExpr"))
+ llvm::errs() << "PostStmt<OffsetOfExpr>\n";
+ }
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreCall")) {
+ llvm::errs() << "PreCall";
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
+ llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << '\n';
+ }
+ }
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostCall")) {
+ llvm::errs() << "PostCall";
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
+ llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << '\n';
+ }
+ }
+
+ void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
+ CheckerContext &C) const {
+ if (isCallbackEnabled(C, "NewAllocator"))
+ llvm::errs() << "NewAllocator\n";
+ }
+
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
if (isCallbackEnabled(C, "Bind"))
llvm::errs() << "Bind\n";
}
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const {
+ if (isCallbackEnabled(State, "LiveSymbols"))
+ llvm::errs() << "LiveSymbols\n";
+ }
+
ProgramStateRef
checkRegionChanges(ProgramStateRef State,
const InvalidatedSymbols *Invalidated,
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 64c30e7a82c1..aadc6bac8d00 100644
--- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -122,6 +122,8 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
E = CE.blocks_exhausted_end(); I != E; ++I) {
const BlockEdge &BE = I->first;
const CFGBlock *Exit = BE.getDst();
+ if (Exit->empty())
+ continue;
const CFGElement &CE = Exit->front();
if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
SmallString<128> bufI;
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index b944f90539d4..933380d494a4 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -33,8 +33,8 @@ class ArrayBoundCheckerV2 :
enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
- void reportOOB(CheckerContext &C, ProgramStateRef errorState,
- OOB_Kind kind) const;
+ void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
public:
void checkLocation(SVal l, bool isLoad, const Stmt*S,
@@ -125,7 +125,6 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
// have some flexibility in defining the base region, we can achieve
// various levels of conservatism in our buffer overflow checking.
ProgramStateRef state = checkerContext.getState();
- ProgramStateRef originalState = state;
SValBuilder &svalBuilder = checkerContext.getSValBuilder();
const RegionRawOffsetV2 &rawOffset =
@@ -205,8 +204,10 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
// If we are under constrained and the index variables are tainted, report.
if (state_exceedsUpperBound && state_withinUpperBound) {
- if (state->isTainted(rawOffset.getByteOffset())) {
- reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted);
+ SVal ByteOffset = rawOffset.getByteOffset();
+ if (state->isTainted(ByteOffset)) {
+ reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
+ llvm::make_unique<TaintBugVisitor>(ByteOffset));
return;
}
} else if (state_exceedsUpperBound) {
@@ -222,13 +223,12 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
}
while (false);
- if (state != originalState)
- checkerContext.addTransition(state);
+ checkerContext.addTransition(state);
}
-void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
- ProgramStateRef errorState,
- OOB_Kind kind) const {
+void ArrayBoundCheckerV2::reportOOB(
+ CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind,
+ std::unique_ptr<BugReporterVisitor> Visitor) const {
ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
if (!errorNode)
@@ -255,8 +255,9 @@ void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext,
break;
}
- checkerContext.emitReport(
- llvm::make_unique<BugReport>(*BT, os.str(), errorNode));
+ auto BR = llvm::make_unique<BugReport>(*BT, os.str(), errorNode);
+ BR->addVisitor(std::move(Visitor));
+ checkerContext.emitReport(std::move(BR));
}
#ifndef NDEBUG
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 371187747f03..7d6358acbbac 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -436,8 +436,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
return;
// Get the value of the "theType" argument.
- const LocationContext *LCtx = C.getLocationContext();
- SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
+ SVal TheTypeVal = C.getSVal(CE->getArg(1));
// FIXME: We really should allow ranges of valid theType values, and
// bifurcate the state appropriately.
@@ -457,7 +456,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// Look at the value of the integer being passed by reference. Essentially
// we want to catch cases where the value passed in is not equal to the
// size of the type being created.
- SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
+ SVal TheValueExpr = C.getSVal(CE->getArg(2));
// FIXME: Eventually we should handle arbitrary locations. We can do this
// by having an enhanced memory model that does low-level typing.
@@ -571,7 +570,7 @@ void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
// Get the argument's value.
const Expr *Arg = CE->getArg(0);
- SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
+ SVal ArgVal = C.getSVal(Arg);
Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
if (!DefArgVal)
return;
@@ -977,8 +976,7 @@ assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
if (!State)
return nullptr;
- SymbolRef CollectionS =
- State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol();
+ SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
}
@@ -1166,7 +1164,7 @@ void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
namespace {
/// \class ObjCNonNilReturnValueChecker
-/// \brief The checker restricts the return values of APIs known to
+/// The checker restricts the return values of APIs known to
/// never (or almost never) return 'nil'.
class ObjCNonNilReturnValueChecker
: public Checker<check::PostObjCMessage,
@@ -1206,7 +1204,7 @@ ProgramStateRef
ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
ProgramStateRef State,
CheckerContext &C) const {
- SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
+ SVal Val = C.getSVal(NonNullExpr);
if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
return State->assume(*DV, true);
return State;
diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index 097d4198800d..0e781d08e24c 100644
--- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -43,7 +43,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
case Builtin::BI__builtin_assume: {
assert (CE->arg_begin() != CE->arg_end());
- SVal ArgSVal = state->getSVal(CE->getArg(0), LCtx);
+ SVal ArgSVal = C.getSVal(CE->getArg(0));
if (ArgSVal.isUndef())
return true; // Return true to model purity.
@@ -68,7 +68,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
// __builtin_addressof is going from a reference to a pointer, but those
// are represented the same way in the analyzer.
assert (CE->arg_begin() != CE->arg_end());
- SVal X = state->getSVal(*(CE->arg_begin()), LCtx);
+ SVal X = C.getSVal(*(CE->arg_begin()));
C.addTransition(state->BindExpr(CE, LCtx, X));
return true;
}
@@ -83,8 +83,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
// Set the extent of the region in bytes. This enables us to use the
// SVal of the argument directly. If we save the extent in bits, we
// cannot represent values like symbol*8.
- DefinedOrUnknownSVal Size =
- state->getSVal(*(CE->arg_begin()), LCtx).castAs<DefinedOrUnknownSVal>();
+ auto Size = C.getSVal(*(CE->arg_begin())).castAs<DefinedOrUnknownSVal>();
SValBuilder& svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
@@ -97,7 +96,8 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
return true;
}
- case Builtin::BI__builtin_object_size: {
+ case Builtin::BI__builtin_object_size:
+ case Builtin::BI__builtin_constant_p: {
// This must be resolvable at compile time, so we defer to the constant
// evaluator for a value.
SVal V = UnknownVal();
diff --git a/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 7ab9c6114eae..5bb4770b5675 100644
--- a/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -37,9 +37,11 @@ add_clang_library(clangStaticAnalyzerCheckers
DynamicTypeChecker.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
+ GCDAntipatternChecker.cpp
GenericTaintChecker.cpp
GTestChecker.cpp
IdenticalExprChecker.cpp
+ InnerPointerChecker.cpp
IteratorChecker.cpp
IvarInvalidationChecker.cpp
LLVMConventionsChecker.cpp
@@ -49,6 +51,7 @@ add_clang_library(clangStaticAnalyzerCheckers
MallocChecker.cpp
MallocOverflowSecurityChecker.cpp
MallocSizeofChecker.cpp
+ MmapWriteExecChecker.cpp
MisusedMovedObjectChecker.cpp
MPI-Checker/MPIBugReporter.cpp
MPI-Checker/MPIChecker.cpp
@@ -61,6 +64,7 @@ add_clang_library(clangStaticAnalyzerCheckers
NullabilityChecker.cpp
NumberObjectConversionChecker.cpp
ObjCAtSyncChecker.cpp
+ ObjCAutoreleaseWriteChecker.cpp
ObjCContainersASTChecker.cpp
ObjCContainersChecker.cpp
ObjCMissingSuperCallChecker.cpp
@@ -75,6 +79,7 @@ add_clang_library(clangStaticAnalyzerCheckers
RetainCountChecker.cpp
ReturnPointerRangeChecker.cpp
ReturnUndefChecker.cpp
+ RunLoopAutoreleaseLeakChecker.cpp
SimpleStreamChecker.cpp
StackAddrEscapeChecker.cpp
StdLibraryFunctionsChecker.cpp
@@ -82,11 +87,13 @@ add_clang_library(clangStaticAnalyzerCheckers
TaintTesterChecker.cpp
TestAfterDivZeroChecker.cpp
TraversalChecker.cpp
+ TrustNonnullChecker.cpp
UndefBranchChecker.cpp
UndefCapturedBlockVarChecker.cpp
UndefResultChecker.cpp
UndefinedArraySubscriptChecker.cpp
UndefinedAssignmentChecker.cpp
+ UninitializedObjectChecker.cpp
UnixAPIChecker.cpp
UnreachableCodeChecker.cpp
VforkChecker.cpp
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 28ad7e9e5071..278452ec994a 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -97,14 +97,17 @@ public:
void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const;
void evalStrcpyCommon(CheckerContext &C,
const CallExpr *CE,
bool returnEnd,
bool isBounded,
- bool isAppending) const;
+ bool isAppending,
+ bool returnPtr = true) const;
void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrlcat(CheckerContext &C, const CallExpr *CE) const;
void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
void evalStrncmp(CheckerContext &C, const CallExpr *CE) const;
@@ -155,6 +158,10 @@ public:
static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
const MemRegion *MR);
+ static bool memsetAux(const Expr *DstBuffer, const Expr *CharE,
+ const Expr *Size, CheckerContext &C,
+ ProgramStateRef &State);
+
// Re-usable checks
ProgramStateRef checkNonNull(CheckerContext &C,
ProgramStateRef state,
@@ -194,6 +201,14 @@ public:
const Stmt *First,
const Stmt *Second) const;
+ void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S,
+ StringRef WarningMsg) const;
+ void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const;
+ void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const;
+ void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const;
+
ProgramStateRef checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state,
NonLoc left,
@@ -239,30 +254,14 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
if (stateNull && !stateNonNull) {
- if (!Filter.CheckCStringNullArg)
- return nullptr;
-
- ExplodedNode *N = C.generateErrorNode(stateNull);
- if (!N)
- return nullptr;
-
- if (!BT_Null)
- BT_Null.reset(new BuiltinBug(
- Filter.CheckNameCStringNullArg, categories::UnixAPI,
- "Null pointer argument in call to byte string function"));
-
- SmallString<80> buf;
- llvm::raw_svector_ostream os(buf);
- assert(CurrentFunctionDescription);
- os << "Null pointer argument in call to " << CurrentFunctionDescription;
-
- // Generate a report for this bug.
- BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get());
- auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ if (Filter.CheckCStringNullArg) {
+ SmallString<80> buf;
+ llvm::raw_svector_ostream os(buf);
+ assert(CurrentFunctionDescription);
+ os << "Null pointer argument in call to " << CurrentFunctionDescription;
- report->addRange(S->getSourceRange());
- bugreporter::trackNullOrUndefValue(N, S, *report);
- C.emitReport(std::move(report));
+ emitNullArgBug(C, stateNull, S, os.str());
+ }
return nullptr;
}
@@ -305,21 +304,14 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true);
ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false);
if (StOutBound && !StInBound) {
- ExplodedNode *N = C.generateErrorNode(StOutBound);
- if (!N)
+ // These checks are either enabled by the CString out-of-bounds checker
+ // explicitly or implicitly by the Malloc checker.
+ // In the latter case we only do modeling but do not emit warning.
+ if (!Filter.CheckCStringOutOfBounds)
return nullptr;
-
- if (!BT_Bounds) {
- BT_Bounds.reset(new BuiltinBug(
- Filter.CheckNameCStringOutOfBounds, "Out-of-bound array access",
- "Byte string function accesses out-of-bound array element"));
- }
- BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get());
-
- // Generate a report for this bug.
- std::unique_ptr<BugReport> report;
+ // Emit a bug report.
if (warningMsg) {
- report = llvm::make_unique<BugReport>(*BT, warningMsg, N);
+ emitOutOfBoundsBug(C, StOutBound, S, warningMsg);
} else {
assert(CurrentFunctionDescription);
assert(CurrentFunctionDescription[0] != '\0');
@@ -329,15 +321,8 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
os << toUppercase(CurrentFunctionDescription[0])
<< &CurrentFunctionDescription[1]
<< " accesses out-of-bound array element";
- report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ emitOutOfBoundsBug(C, StOutBound, S, os.str());
}
-
- // FIXME: It would be nice to eventually make this diagnostic more clear,
- // e.g., by referencing the original declaration or by saying *why* this
- // reference is outside the range.
-
- report->addRange(S->getSourceRange());
- C.emitReport(std::move(report));
return nullptr;
}
@@ -366,7 +351,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
// Check that the first buffer is non-null.
- SVal BufVal = state->getSVal(FirstBuf, LCtx);
+ SVal BufVal = C.getSVal(FirstBuf);
state = checkNonNull(C, state, FirstBuf, BufVal);
if (!state)
return nullptr;
@@ -378,15 +363,17 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
// Get the access length and make sure it is known.
// FIXME: This assumes the caller has already checked that the access length
// is positive. And that it's unsigned.
- SVal LengthVal = state->getSVal(Size, LCtx);
+ SVal LengthVal = C.getSVal(Size);
Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
if (!Length)
return state;
// Compute the offset of the last element to be accessed: size-1.
NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
- NonLoc LastOffset = svalBuilder
- .evalBinOpNN(state, BO_Sub, *Length, One, sizeTy).castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ if (Offset.isUnknown())
+ return nullptr;
+ NonLoc LastOffset = Offset.castAs<NonLoc>();
// Check that the first buffer is sufficiently long.
SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
@@ -555,6 +542,79 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
C.emitReport(std::move(report));
}
+void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_Null)
+ BT_Null.reset(new BuiltinBug(
+ Filter.CheckNameCStringNullArg, categories::UnixAPI,
+ "Null pointer argument in call to byte string function"));
+
+ BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get());
+ auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
+ bugreporter::trackNullOrUndefValue(N, S, *Report);
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
+ ProgramStateRef State, const Stmt *S,
+ StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_Bounds)
+ BT_Bounds.reset(new BuiltinBug(
+ Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds
+ : Filter.CheckNameCStringNullArg,
+ "Out-of-bound array access",
+ "Byte string function accesses out-of-bound array element"));
+
+ BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get());
+
+ // FIXME: It would be nice to eventually make this diagnostic more clear,
+ // e.g., by referencing the original declaration or by saying *why* this
+ // reference is outside the range.
+ auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
+ Report->addRange(S->getSourceRange());
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S,
+ StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ if (!BT_NotCString)
+ BT_NotCString.reset(new BuiltinBug(
+ Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
+ "Argument is not a null-terminated string."));
+
+ auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
+
+ Report->addRange(S->getSourceRange());
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
+ ProgramStateRef State) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_NotCString)
+ BT_NotCString.reset(
+ new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
+ "Sum of expressions causes overflow."));
+
+ // This isn't a great error message, but this should never occur in real
+ // code anyway -- you'd have to create a buffer longer than a size_t can
+ // represent, which is sort of a contradiction.
+ const char *WarningMsg =
+ "This expression will create a string whose length is too big to "
+ "be represented as a size_t";
+
+ auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
+ C.emitReport(std::move(Report));
+ }
+}
+
ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
ProgramStateRef state,
NonLoc left,
@@ -598,26 +658,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
if (stateOverflow && !stateOkay) {
// We have an overflow. Emit a bug report.
- ExplodedNode *N = C.generateErrorNode(stateOverflow);
- if (!N)
- return nullptr;
-
- if (!BT_AdditionOverflow)
- BT_AdditionOverflow.reset(
- new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
- "Sum of expressions causes overflow"));
-
- // This isn't a great error message, but this should never occur in real
- // code anyway -- you'd have to create a buffer longer than a size_t can
- // represent, which is sort of a contradiction.
- const char *warning =
- "This expression will create a string whose length is too big to "
- "be represented as a size_t";
-
- // Generate a report for this bug.
- C.emitReport(
- llvm::make_unique<BugReport>(*BT_AdditionOverflow, warning, N));
-
+ emitAdditionOverflowBug(C, stateOverflow);
return nullptr;
}
@@ -717,15 +758,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// C string. In the context of locations, the only time we can issue such
// a warning is for labels.
if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
- if (!Filter.CheckCStringNotNullTerm)
- return UndefinedVal();
-
- if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
- if (!BT_NotCString)
- BT_NotCString.reset(new BuiltinBug(
- Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
- "Argument is not a null-terminated string."));
-
+ if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
assert(CurrentFunctionDescription);
@@ -733,14 +766,9 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
<< " is the address of the label '" << Label->getLabel()->getName()
<< "', which is not a null-terminated string";
- // Generate a report for this bug.
- auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
-
- report->addRange(Ex->getSourceRange());
- C.emitReport(std::move(report));
+ emitNotCStringBug(C, state, Ex, os.str());
}
return UndefinedVal();
-
}
// If it's not a region and not a label, give up.
@@ -777,15 +805,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// Other regions (mostly non-data) can't have a reliable C string length.
// In this case, an error is emitted and UndefinedVal is returned.
// The caller should always be prepared to handle this case.
- if (!Filter.CheckCStringNotNullTerm)
- return UndefinedVal();
-
- if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
- if (!BT_NotCString)
- BT_NotCString.reset(new BuiltinBug(
- Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
- "Argument is not a null-terminated string."));
-
+ if (Filter.CheckCStringNotNullTerm) {
SmallString<120> buf;
llvm::raw_svector_ostream os(buf);
@@ -797,13 +817,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
else
os << "not a null-terminated string";
- // Generate a report for this bug.
- auto report = llvm::make_unique<BugReport>(*BT_NotCString, os.str(), N);
-
- report->addRange(Ex->getSourceRange());
- C.emitReport(std::move(report));
+ emitNotCStringBug(C, state, Ex, os.str());
}
-
return UndefinedVal();
}
}
@@ -852,9 +867,10 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
// Compute the offset of the last element to be accessed: size-1.
NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
- NonLoc LastOffset =
- svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy)
- .castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ if (Offset.isUnknown())
+ return true; // cf top comment
+ NonLoc LastOffset = Offset.castAs<NonLoc>();
// Check that the first buffer is sufficiently long.
SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
@@ -987,6 +1003,95 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
}
}
+bool CStringChecker::memsetAux(const Expr *DstBuffer, const Expr *CharE,
+ const Expr *Size, CheckerContext &C,
+ ProgramStateRef &State) {
+ SVal MemVal = C.getSVal(DstBuffer);
+ SVal CharVal = C.getSVal(CharE);
+ SVal SizeVal = C.getSVal(Size);
+ const MemRegion *MR = MemVal.getAsRegion();
+ if (!MR)
+ return false;
+
+ // We're about to model memset by producing a "default binding" in the Store.
+ // Our current implementation - RegionStore - doesn't support default bindings
+ // that don't cover the whole base region. So we should first get the offset
+ // and the base region to figure out whether the offset of buffer is 0.
+ RegionOffset Offset = MR->getAsOffset();
+ const MemRegion *BR = Offset.getRegion();
+
+ Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
+ if (!SizeNL)
+ return false;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = C.getASTContext();
+
+ // void *memset(void *dest, int ch, size_t count);
+ // For now we can only handle the case of offset is 0 and concrete char value.
+ if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() == 0) {
+ // Get the base region's extent.
+ auto *SubReg = cast<SubRegion>(BR);
+ DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder);
+
+ ProgramStateRef StateWholeReg, StateNotWholeReg;
+ std::tie(StateWholeReg, StateNotWholeReg) =
+ State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL));
+
+ // With the semantic of 'memset()', we should convert the CharVal to
+ // unsigned char.
+ CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);
+
+ ProgramStateRef StateNullChar, StateNonNullChar;
+ std::tie(StateNullChar, StateNonNullChar) =
+ assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
+
+ if (StateWholeReg && !StateNotWholeReg && StateNullChar &&
+ !StateNonNullChar) {
+ // If the 'memset()' acts on the whole region of destination buffer and
+ // the value of the second argument of 'memset()' is zero, bind the second
+ // argument's value to the destination buffer with 'default binding'.
+ // FIXME: Since there is no perfect way to bind the non-zero character, we
+ // can only deal with zero value here. In the future, we need to deal with
+ // the binding of non-zero value in the case of whole region.
+ State = State->bindDefaultZero(svalBuilder.makeLoc(BR),
+ C.getLocationContext());
+ } else {
+ // If the destination buffer's extent is not equal to the value of
+ // third argument, just invalidate buffer.
+ State = InvalidateBuffer(C, State, DstBuffer, MemVal,
+ /*IsSourceBuffer*/ false, Size);
+ }
+
+ if (StateNullChar && !StateNonNullChar) {
+ // If the value of the second argument of 'memset()' is zero, set the
+ // string length of destination buffer to 0 directly.
+ State = setCStringLength(State, MR,
+ svalBuilder.makeZeroVal(Ctx.getSizeType()));
+ } else if (!StateNullChar && StateNonNullChar) {
+ SVal NewStrLen = svalBuilder.getMetadataSymbolVal(
+ CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(),
+ C.getLocationContext(), C.blockCount());
+
+ // If the value of second argument is not zero, then the string length
+ // is at least the size argument.
+ SVal NewStrLenGESize = svalBuilder.evalBinOp(
+ State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType());
+
+ State = setCStringLength(
+ State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true),
+ MR, NewStrLen);
+ }
+ } else {
+ // If the offset is not zero and char value is not concrete, we can do
+ // nothing but invalidate the buffer.
+ State = InvalidateBuffer(C, State, DstBuffer, MemVal,
+ /*IsSourceBuffer*/ false, Size);
+ }
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// evaluation of individual function calls.
//===----------------------------------------------------------------------===//
@@ -1384,6 +1489,18 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
/* isAppending = */ false);
}
+void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // char *strlcpy(char *dst, const char *src, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ true,
+ /* isBounded = */ true,
+ /* isAppending = */ false,
+ /* returnPtr = */ false);
+}
+
void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
if (CE->getNumArgs() < 2)
return;
@@ -1406,9 +1523,21 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
/* isAppending = */ true);
}
+void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ //char *strlcat(char *s1, const char *s2, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ true,
+ /* isAppending = */ true,
+ /* returnPtr = */ false);
+}
+
void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
bool returnEnd, bool isBounded,
- bool isAppending) const {
+ bool isAppending, bool returnPtr) const {
CurrentFunctionDescription = "string copy function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
@@ -1446,6 +1575,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
SVal maxLastElementIndex = UnknownVal();
const char *boundWarning = nullptr;
+ state = CheckOverlap(C, state, isBounded ? CE->getArg(2) : CE->getArg(1), Dst, srcExpr);
+
+ if (!state)
+ return;
+
// If the function is strncpy, strncat, etc... it is bounded.
if (isBounded) {
// Get the max number of characters to copy.
@@ -1518,7 +1652,11 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// If the size is known to be zero, we're done.
if (StateZeroSize && !StateNonZeroSize) {
- StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+ if (returnPtr) {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+ } else {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, *lenValNL);
+ }
C.addTransition(StateZeroSize);
return;
}
@@ -1649,16 +1787,22 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
finalStrLength = amountCopied;
}
- // The final result of the function will either be a pointer past the last
- // copied element, or a pointer to the start of the destination buffer.
- SVal Result = (returnEnd ? UnknownVal() : DstVal);
+ SVal Result;
+
+ if (returnPtr) {
+ // The final result of the function will either be a pointer past the last
+ // copied element, or a pointer to the start of the destination buffer.
+ Result = (returnEnd ? UnknownVal() : DstVal);
+ } else {
+ Result = finalStrLength;
+ }
assert(state);
// If the destination is a MemRegion, try to check for a buffer overflow and
// record the new string length.
if (Optional<loc::MemRegionVal> dstRegVal =
- DstVal.getAs<loc::MemRegionVal>()) {
+ DstVal.getAs<loc::MemRegionVal>()) {
QualType ptrTy = Dst->getType();
// If we have an exact value on a bounded copy, use that to check for
@@ -1666,9 +1810,9 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
if (boundWarning) {
if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
- *maxLastNL, ptrTy);
+ *maxLastNL, ptrTy);
state = CheckLocation(C, state, CE->getArg(2), maxLastElement,
- boundWarning);
+ boundWarning);
if (!state)
return;
}
@@ -1677,7 +1821,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// Then, if the final length is known...
if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
- *knownStrLength, ptrTy);
+ *knownStrLength, ptrTy);
// ...and we haven't checked the bound, we'll check the actual copy.
if (!boundWarning) {
@@ -1689,7 +1833,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
}
// If this is a stpcpy-style copy, the last element is the return value.
- if (returnEnd)
+ if (returnPtr && returnEnd)
Result = lastElement;
}
@@ -1701,12 +1845,12 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
// This would probably remove any existing bindings past the end of the
// string, but that's still an improvement over blank invalidation.
state = InvalidateBuffer(C, state, Dst, *dstRegVal,
- /*IsSourceBuffer*/false, nullptr);
+ /*IsSourceBuffer*/false, nullptr);
// Invalidate the source (const-invalidation without const-pointer-escaping
// the address of the top-level region).
state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true,
- nullptr);
+ nullptr);
// Set the C string length of the destination, if we know it.
if (isBounded && !isAppending) {
@@ -1722,12 +1866,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
assert(state);
- // If this is a stpcpy-style copy, but we were unable to check for a buffer
- // overflow, we still need a result. Conjure a return value.
- if (returnEnd && Result.isUnknown()) {
- Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ if (returnPtr) {
+ // If this is a stpcpy-style copy, but we were unable to check for a buffer
+ // overflow, we still need a result. Conjure a return value.
+ if (returnEnd && Result.isUnknown()) {
+ Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ }
}
-
// Set the return value.
state = state->BindExpr(CE, LCtx, Result);
C.addTransition(state);
@@ -1750,7 +1895,7 @@ void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrcasecmp(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallExpr *CE) const {
if (CE->getNumArgs() < 2)
return;
@@ -1759,7 +1904,7 @@ void CStringChecker::evalStrcasecmp(CheckerContext &C,
}
void CStringChecker::evalStrncasecmp(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallExpr *CE) const {
if (CE->getNumArgs() < 3)
return;
@@ -1768,7 +1913,7 @@ void CStringChecker::evalStrncasecmp(CheckerContext &C,
}
void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
- bool isBounded, bool ignoreCase) const {
+ bool isBounded, bool ignoreCase) const {
CurrentFunctionDescription = "string comparison function";
ProgramStateRef state = C.getState();
const LocationContext *LCtx = C.getLocationContext();
@@ -1813,7 +1958,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// and we only need to check one size.
if (StSameBuf) {
StSameBuf = StSameBuf->BindExpr(CE, LCtx,
- svalBuilder.makeZeroVal(CE->getType()));
+ svalBuilder.makeZeroVal(CE->getType()));
C.addTransition(StSameBuf);
// If the two arguments are GUARANTEED to be the same, we're done!
@@ -1832,7 +1977,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val);
bool canComputeResult = false;
SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
- C.blockCount());
+ C.blockCount());
if (s1StrLiteral && s2StrLiteral) {
StringRef s1StrRef = s1StrLiteral->getString();
@@ -1867,7 +2012,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// Use StringRef's comparison methods to compute the actual result.
int compareRes = ignoreCase ? s1StrRef.compare_lower(s2StrRef)
- : s1StrRef.compare(s2StrRef);
+ : s1StrRef.compare(s2StrRef);
// The strcmp function returns an integer greater than, equal to, or less
// than zero, [c11, p7.24.4.2].
@@ -1881,7 +2026,7 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT;
SVal compareWithZero =
svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
- svalBuilder.getConditionType());
+ svalBuilder.getConditionType());
DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>();
state = state->assume(compareWithZeroVal, true);
}
@@ -1933,17 +2078,17 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
// Invalidate the search string, representing the change of one delimiter
// character to NUL.
State = InvalidateBuffer(C, State, SearchStrPtr, Result,
- /*IsSourceBuffer*/false, nullptr);
+ /*IsSourceBuffer*/false, nullptr);
// Overwrite the search string pointer. The new value is either an address
// further along in the same string, or NULL if there are no more tokens.
State = State->bindLoc(*SearchStrLoc,
- SVB.conjureSymbolVal(getTag(),
- CE,
- LCtx,
- CharPtrTy,
- C.blockCount()),
- LCtx);
+ SVB.conjureSymbolVal(getTag(),
+ CE,
+ LCtx,
+ CharPtrTy,
+ C.blockCount()),
+ LCtx);
} else {
assert(SearchStrVal.isUnknown());
// Conjure a symbolic value. It's the best we can do.
@@ -1961,12 +2106,12 @@ void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStdCopyBackward(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallExpr *CE) const {
evalStdCopyCommon(C, CE);
}
void CStringChecker::evalStdCopyCommon(CheckerContext &C,
- const CallExpr *CE) const {
+ const CallExpr *CE) const {
if (CE->getNumArgs() < 3)
return;
@@ -1983,7 +2128,7 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
const Expr *Dst = CE->getArg(2);
SVal DstVal = State->getSVal(Dst, LCtx);
State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false,
- /*Size=*/nullptr);
+ /*Size=*/nullptr);
SValBuilder &SVB = C.getSValBuilder();
@@ -2000,6 +2145,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
CurrentFunctionDescription = "memory set function";
const Expr *Mem = CE->getArg(0);
+ const Expr *CharE = CE->getArg(1);
const Expr *Size = CE->getArg(2);
ProgramStateRef State = C.getState();
@@ -2032,9 +2178,11 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
State = CheckBufferAccess(C, State, Size, Mem);
if (!State)
return;
- State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem),
- /*IsSourceBuffer*/false, Size);
- if (!State)
+
+ // According to the values of the arguments, bind the value of the second
+ // argument to the destination buffer and set string length, or just
+ // invalidate the destination buffer.
+ if (!memsetAux(Mem, CharE, Size, C, State))
return;
State = State->BindExpr(CE, LCtx, MemVal);
@@ -2082,10 +2230,14 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
evalFunction = &CStringChecker::evalStrncpy;
else if (C.isCLibraryFunction(FDecl, "stpcpy"))
evalFunction = &CStringChecker::evalStpcpy;
+ else if (C.isCLibraryFunction(FDecl, "strlcpy"))
+ evalFunction = &CStringChecker::evalStrlcpy;
else if (C.isCLibraryFunction(FDecl, "strcat"))
evalFunction = &CStringChecker::evalStrcat;
else if (C.isCLibraryFunction(FDecl, "strncat"))
evalFunction = &CStringChecker::evalStrncat;
+ else if (C.isCLibraryFunction(FDecl, "strlcat"))
+ evalFunction = &CStringChecker::evalStrlcat;
else if (C.isCLibraryFunction(FDecl, "strlen"))
evalFunction = &CStringChecker::evalstrLength;
else if (C.isCLibraryFunction(FDecl, "strnlen"))
@@ -2149,10 +2301,10 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
if (!MR)
continue;
- SVal StrVal = state->getSVal(Init, C.getLocationContext());
+ SVal StrVal = C.getSVal(Init);
assert(StrVal.isValid() && "Initializer string is unknown or undefined");
DefinedOrUnknownSVal strLength =
- getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();
+ getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();
state = state->set<CStringLength>(MR, strLength);
}
@@ -2162,11 +2314,11 @@ void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
ProgramStateRef
CStringChecker::checkRegionChanges(ProgramStateRef state,
- const InvalidatedSymbols *,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const LocationContext *LCtx,
- const CallEvent *Call) const {
+ const InvalidatedSymbols *,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const {
CStringLengthTy Entries = state->get<CStringLength>();
if (Entries.isEmpty())
return state;
@@ -2176,7 +2328,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
// First build sets for the changed regions and their super-regions.
for (ArrayRef<const MemRegion *>::iterator
- I = Regions.begin(), E = Regions.end(); I != E; ++I) {
+ I = Regions.begin(), E = Regions.end(); I != E; ++I) {
const MemRegion *MR = *I;
Invalidated.insert(MR);
@@ -2191,7 +2343,7 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
// Then loop over the entries in the current state.
for (CStringLengthTy::iterator I = Entries.begin(),
- E = Entries.end(); I != E; ++I) {
+ E = Entries.end(); I != E; ++I) {
const MemRegion *MR = I.getKey();
// Is this entry for a super-region of a changed region?
@@ -2215,22 +2367,22 @@ CStringChecker::checkRegionChanges(ProgramStateRef state,
}
void CStringChecker::checkLiveSymbols(ProgramStateRef state,
- SymbolReaper &SR) const {
+ SymbolReaper &SR) const {
// Mark all symbols in our string length map as valid.
CStringLengthTy Entries = state->get<CStringLength>();
for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
- I != E; ++I) {
+ I != E; ++I) {
SVal Len = I.getData();
for (SymExpr::symbol_iterator si = Len.symbol_begin(),
- se = Len.symbol_end(); si != se; ++si)
+ se = Len.symbol_end(); si != se; ++si)
SR.markInUse(*si);
}
}
void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
- CheckerContext &C) const {
+ CheckerContext &C) const {
if (!SR.hasDeadSymbols())
return;
@@ -2241,7 +2393,7 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
CStringLengthTy::Factory &F = state->get_context<CStringLength>();
for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
- I != E; ++I) {
+ I != E; ++I) {
SVal Len = I.getData();
if (SymbolRef Sym = Len.getAsSymbol()) {
if (SR.isDead(Sym))
@@ -2260,11 +2412,11 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
}
-REGISTER_CHECKER(CStringNullArg)
-REGISTER_CHECKER(CStringOutOfBounds)
-REGISTER_CHECKER(CStringBufferOverlap)
+ REGISTER_CHECKER(CStringNullArg)
+ REGISTER_CHECKER(CStringOutOfBounds)
+ REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
-void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
- registerCStringNullArg(Mgr);
-}
+ void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
+ Mgr.registerChecker<CStringChecker>();
+ }
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index 4b5e97b69295..8b4aa857e775 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -80,6 +80,18 @@ class WalkAST: public StmtVisitor<WalkAST> {
/// of bytes to copy.
bool containsBadStrncatPattern(const CallExpr *CE);
+ /// Identify erroneous patterns in the last argument to strlcpy - the number
+ /// of bytes to copy.
+ /// The bad pattern checked is when the size is known
+ /// to be larger than the destination can handle.
+ /// char dst[2];
+ /// size_t cpy = 4;
+ /// strlcpy(dst, "abcd", sizeof("abcd") - 1);
+ /// strlcpy(dst, "abcd", 4);
+ /// strlcpy(dst + 3, "abcd", 2);
+ /// strlcpy(dst, "abcd", cpy);
+ bool containsBadStrlcpyPattern(const CallExpr *CE);
+
public:
WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
: Checker(Checker), BR(BR), AC(AC) {}
@@ -130,6 +142,54 @@ bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
return false;
}
+bool WalkAST::containsBadStrlcpyPattern(const CallExpr *CE) {
+ if (CE->getNumArgs() != 3)
+ return false;
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *LenArg = CE->getArg(2);
+
+ const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts());
+ const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
+ uint64_t DstOff = 0;
+ // - size_t dstlen = sizeof(dst)
+ if (LenArgDecl) {
+ const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
+ if (LenArgVal->getInit())
+ LenArg = LenArgVal->getInit();
+ }
+
+ // - integral value
+ // We try to figure out if the last argument is possibly longer
+ // than the destination can possibly handle if its size can be defined.
+ if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) {
+ uint64_t ILRawVal = IL->getValue().getZExtValue();
+
+ // Case when there is pointer arithmetic on the destination buffer
+ // especially when we offset from the base decreasing the
+ // buffer length accordingly.
+ if (!DstArgDecl) {
+ if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) {
+ DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts());
+ if (BE->getOpcode() == BO_Add) {
+ if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) {
+ DstOff = IL->getValue().getZExtValue();
+ }
+ }
+ }
+ }
+ if (DstArgDecl) {
+ if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
+ ASTContext &C = BR.getContext();
+ uint64_t BufferLen = C.getTypeSize(Buffer) / 8;
+ if ((BufferLen - DstOff) < ILRawVal)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
void WalkAST::VisitCallExpr(CallExpr *CE) {
const FunctionDecl *FD = CE->getDirectCallee();
if (!FD)
@@ -159,6 +219,25 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
"C String API", os.str(), Loc,
LenArg->getSourceRange());
}
+ } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy")) {
+ if (containsBadStrlcpyPattern(CE)) {
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *LenArg = CE->getArg(2);
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
+
+ StringRef DstName = getPrintableName(DstArg);
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ os << "The third argument is larger than the size of the input buffer. ";
+ if (!DstName.empty())
+ os << "Replace with the value 'sizeof(" << DstName << ")` or lower";
+
+ BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
+ "C String API", os.str(), Loc,
+ LenArg->getSourceRange());
+ }
}
// Recurse and check children.
diff --git a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
index 668e772fe1b3..d1d37c75dfcc 100644
--- a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
@@ -48,7 +48,7 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const {
auto &State = C.getState();
auto &SVB = C.getSValBuilder();
auto ThisVal =
- State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx));
auto ParamVal = State->getSVal(Param);
ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx);
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 3e178152d925..059553b21995 100644
--- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -101,7 +101,7 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
return;
ProgramStateRef state = C.getState();
- const MemRegion *R = state->getSVal(E, C.getLocationContext()).getAsRegion();
+ const MemRegion *R = C.getSVal(E).getAsRegion();
if (!R)
return;
diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
index 65e81315f095..00e903355720 100644
--- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -78,7 +78,7 @@ bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) {
// Don't warn for references
const ValueDecl *VD = nullptr;
if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr()))
- VD = dyn_cast<ValueDecl>(SE->getDecl());
+ VD = SE->getDecl();
else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr()))
VD = SE->getMemberDecl();
if (!VD || VD->getType()->isReferenceType())
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 2818c9d9fd4a..f4d2e32cef11 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -126,7 +126,7 @@ public:
const CallEvent *Call,
PointerEscapeKind Kind) const;
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
- void checkEndFunction(CheckerContext &Ctx) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
private:
void diagnoseMissingReleases(CheckerContext &C) const;
@@ -398,7 +398,7 @@ void ObjCDeallocChecker::checkPostObjCMessage(
/// Check for missing releases even when -dealloc does not call
/// '[super dealloc]'.
void ObjCDeallocChecker::checkEndFunction(
- CheckerContext &C) const {
+ const ReturnStmt *RS, CheckerContext &C) const {
diagnoseMissingReleases(C);
}
@@ -535,7 +535,7 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
continue;
// Prevents diagnosing multiple times for the same instance variable
- // at, for example, both a return and at the end of of the function.
+ // at, for example, both a return and at the end of the function.
NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
if (State->getStateManager()
@@ -645,7 +645,7 @@ ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
const ObjCMethodCall &M,
CheckerContext &C) const {
- // Try to get the region from which the the released value was loaded.
+ // Try to get the region from which the released value was loaded.
// Note that, unlike diagnosing for missing releases, here we don't track
// values that must not be released in the state. This is because even if
// these values escape, it is still an error under the rules of MRR to
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 6dbacad7f2ea..202233acffab 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -37,6 +37,9 @@ static bool isArc4RandomAvailable(const ASTContext &Ctx) {
namespace {
struct ChecksFilter {
+ DefaultBool check_bcmp;
+ DefaultBool check_bcopy;
+ DefaultBool check_bzero;
DefaultBool check_gets;
DefaultBool check_getpw;
DefaultBool check_mktemp;
@@ -47,6 +50,9 @@ struct ChecksFilter {
DefaultBool check_FloatLoopCounter;
DefaultBool check_UncheckedReturn;
+ CheckName checkName_bcmp;
+ CheckName checkName_bcopy;
+ CheckName checkName_bzero;
CheckName checkName_gets;
CheckName checkName_getpw;
CheckName checkName_mktemp;
@@ -89,6 +95,9 @@ public:
// Checker-specific methods.
void checkLoopConditionForFloat(const ForStmt *FS);
+ void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
@@ -129,6 +138,9 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
// Set the evaluation function by switching on the callee name.
FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
+ .Case("bcmp", &WalkAST::checkCall_bcmp)
+ .Case("bcopy", &WalkAST::checkCall_bcopy)
+ .Case("bzero", &WalkAST::checkCall_bzero)
.Case("gets", &WalkAST::checkCall_gets)
.Case("getpw", &WalkAST::checkCall_getpw)
.Case("mktemp", &WalkAST::checkCall_mktemp)
@@ -296,6 +308,132 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
}
//===----------------------------------------------------------------------===//
+// Check: Any use of bcmp.
+// CWE-477: Use of Obsolete Functions
+// bcmp was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bcmp)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes three arguments.
+ if (FPT->getNumParams() != 3)
+ return;
+
+ for (int i = 0; i < 2; i++) {
+ // Verify the first and second argument type is void*.
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+ }
+
+ // Verify the third argument type is integer.
+ if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
+ "Use of deprecated function in call to 'bcmp()'",
+ "Security",
+ "The bcmp() function is obsoleted by memcmp().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of bcopy.
+// CWE-477: Use of Obsolete Functions
+// bcopy was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bcopy)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes three arguments.
+ if (FPT->getNumParams() != 3)
+ return;
+
+ for (int i = 0; i < 2; i++) {
+ // Verify the first and second argument type is void*.
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+ }
+
+ // Verify the third argument type is integer.
+ if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
+ "Use of deprecated function in call to 'bcopy()'",
+ "Security",
+ "The bcopy() function is obsoleted by memcpy() "
+ "or memmove().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of bzero.
+// CWE-477: Use of Obsolete Functions
+// bzero was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bzero)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes two arguments.
+ if (FPT->getNumParams() != 2)
+ return;
+
+ // Verify the first argument type is void*.
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+
+ // Verify the second argument type is integer.
+ if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
+ "Use of deprecated function in call to 'bzero()'",
+ "Security",
+ "The bzero() function is obsoleted by memset().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+
+//===----------------------------------------------------------------------===//
// Check: Any use of 'gets' is insecure.
// Originally: <rdar://problem/6335715>
// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
@@ -510,6 +648,17 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
if (!checkCall_strCommon(CE, FD))
return;
+ const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
+ *Source = CE->getArg(1)->IgnoreImpCasts();
+ if (const auto *DeclRef = dyn_cast<DeclRefExpr>(Target))
+ if (const auto *Array = dyn_cast<ConstantArrayType>(DeclRef->getType())) {
+ uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
+ if (const auto *String = dyn_cast<StringLiteral>(Source)) {
+ if (ArraySize >= String->getLength() + 1)
+ return;
+ }
+ }
+
// Issue a warning.
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
@@ -764,6 +913,9 @@ public:
checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
}
+REGISTER_CHECKER(bcmp)
+REGISTER_CHECKER(bcopy)
+REGISTER_CHECKER(bzero)
REGISTER_CHECKER(gets)
REGISTER_CHECKER(getpw)
REGISTER_CHECKER(mkstemp)
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 95b6c4d3775d..7862a4c25681 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -42,6 +42,7 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
check::PreCall,
check::PostCall,
check::BranchCondition,
+ check::NewAllocator,
check::Location,
check::Bind,
check::DeadSymbols,
@@ -58,7 +59,7 @@ class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
check::Event<ImplicitNullDerefEvent>,
check::ASTDecl<FunctionDecl> > {
public:
- /// \brief Pre-visit the Statement.
+ /// Pre-visit the Statement.
///
/// The method will be called before the analyzer core processes the
/// statement. The notification is performed for every explored CFGElement,
@@ -71,7 +72,7 @@ public:
/// check::PreStmt<ReturnStmt>
void checkPreStmt(const ReturnStmt *DS, CheckerContext &C) const {}
- /// \brief Post-visit the Statement.
+ /// Post-visit the Statement.
///
/// The method will be called after the analyzer core processes the
/// statement. The notification is performed for every explored CFGElement,
@@ -81,7 +82,7 @@ public:
/// check::PostStmt<DeclStmt>
void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
- /// \brief Pre-visit the Objective C message.
+ /// Pre-visit the Objective C message.
///
/// This will be called before the analyzer core processes the method call.
/// This is called for any action which produces an Objective-C message send,
@@ -90,13 +91,13 @@ public:
/// check::PreObjCMessage
void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
- /// \brief Post-visit the Objective C message.
+ /// Post-visit the Objective C message.
/// \sa checkPreObjCMessage()
///
/// check::PostObjCMessage
void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
- /// \brief Visit an Objective-C message whose receiver is nil.
+ /// Visit an Objective-C message whose receiver is nil.
///
/// This will be called when the analyzer core processes a method call whose
/// receiver is definitely nil. In this case, check{Pre/Post}ObjCMessage and
@@ -105,7 +106,7 @@ public:
/// check::ObjCMessageNil
void checkObjCMessageNil(const ObjCMethodCall &M, CheckerContext &C) const {}
- /// \brief Pre-visit an abstract "call" event.
+ /// Pre-visit an abstract "call" event.
///
/// This is used for checkers that want to check arguments or attributed
/// behavior for functions and methods no matter how they are being invoked.
@@ -117,16 +118,32 @@ public:
/// check::PreCall
void checkPreCall(const CallEvent &Call, CheckerContext &C) const {}
- /// \brief Post-visit an abstract "call" event.
+ /// Post-visit an abstract "call" event.
/// \sa checkPreObjCMessage()
///
/// check::PostCall
void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
- /// \brief Pre-visit of the condition statement of a branch (such as IfStmt).
+ /// Pre-visit of the condition statement of a branch (such as IfStmt).
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
- /// \brief Called on a load from and a store to a location.
+ /// Post-visit the C++ operator new's allocation call.
+ ///
+ /// Execution of C++ operator new consists of the following phases: (1) call
+ /// default or overridden operator new() to allocate memory (2) cast the
+ /// return value of operator new() from void pointer type to class pointer
+ /// type, (3) assuming that the value is non-null, call the object's
+ /// constructor over this pointer, (4) declare that the value of the
+ /// new-expression is this pointer. This callback is called between steps
+ /// (2) and (3). Post-call for the allocator is called after step (1).
+ /// Pre-statement for the new-expression is called on step (4) when the value
+ /// of the expression is evaluated.
+ /// \param NE The C++ new-expression that triggered the allocation.
+ /// \param Target The allocated region, casted to the class type.
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &) const {}
+
+ /// Called on a load from and a store to a location.
///
/// The method will be called each time a location (pointer) value is
/// accessed.
@@ -138,7 +155,7 @@ public:
void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
CheckerContext &) const {}
- /// \brief Called on binding of a value to a location.
+ /// Called on binding of a value to a location.
///
/// \param Loc The value of the location (pointer).
/// \param Val The value which will be stored at the location Loc.
@@ -147,7 +164,7 @@ public:
/// check::Bind
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {}
- /// \brief Called whenever a symbol becomes dead.
+ /// Called whenever a symbol becomes dead.
///
/// This callback should be used by the checkers to aggressively clean
/// up/reduce the checker state, which is important for reducing the overall
@@ -164,20 +181,20 @@ public:
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {}
- /// \brief Called when the analyzer core starts analyzing a function,
+ /// Called when the analyzer core starts analyzing a function,
/// regardless of whether it is analyzed at the top level or is inlined.
///
/// check::BeginFunction
void checkBeginFunction(CheckerContext &Ctx) const {}
- /// \brief Called when the analyzer core reaches the end of a
+ /// Called when the analyzer core reaches the end of a
/// function being analyzed regardless of whether it is analyzed at the top
/// level or is inlined.
///
/// check::EndFunction
- void checkEndFunction(CheckerContext &Ctx) const {}
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const {}
- /// \brief Called after all the paths in the ExplodedGraph reach end of path
+ /// Called after all the paths in the ExplodedGraph reach end of path
/// - the symbolic execution graph is fully explored.
///
/// This callback should be used in cases when a checker needs to have a
@@ -190,14 +207,14 @@ public:
BugReporter &BR,
ExprEngine &Eng) const {}
- /// \brief Called after analysis of a TranslationUnit is complete.
+ /// Called after analysis of a TranslationUnit is complete.
///
/// check::EndOfTranslationUnit
void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
AnalysisManager &Mgr,
BugReporter &BR) const {}
- /// \brief Evaluates function call.
+ /// Evaluates function call.
///
/// The analysis core threats all function calls in the same way. However, some
/// functions have special meaning, which should be reflected in the program
@@ -212,7 +229,7 @@ public:
/// eval::Call
bool evalCall(const CallExpr *CE, CheckerContext &C) const { return true; }
- /// \brief Handles assumptions on symbolic values.
+ /// Handles assumptions on symbolic values.
///
/// This method is called when a symbolic expression is assumed to be true or
/// false. For example, the assumptions are performed when evaluating a
@@ -231,7 +248,7 @@ public:
/// check::LiveSymbols
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {}
- /// \brief Called when the contents of one or more regions change.
+ /// Called when the contents of one or more regions change.
///
/// This can occur in many different ways: an explicit bind, a blanket
/// invalidation of the region contents, or by passing a region to a function
@@ -263,7 +280,7 @@ public:
return State;
}
- /// \brief Called when pointers escape.
+ /// Called when pointers escape.
///
/// This notifies the checkers about pointer escape, which occurs whenever
/// the analyzer cannot track the symbol any more. For example, as a
@@ -283,7 +300,7 @@ public:
return State;
}
- /// \brief Called when const pointers escape.
+ /// Called when const pointers escape.
///
/// Note: in most cases checkPointerEscape callback is sufficient.
/// \sa checkPointerEscape
@@ -297,7 +314,7 @@ public:
/// check::Event<ImplicitNullDerefEvent>
void checkEvent(ImplicitNullDerefEvent Event) const {}
- /// \brief Check every declaration in the AST.
+ /// Check every declaration in the AST.
///
/// An AST traversal callback, which should only be used when the checker is
/// not path sensitive. It will be called for every Declaration in the AST and
diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 9e9939ae25c0..b38992b0e030 100644
--- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -105,7 +105,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
// After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
const Expr *ArgExpr = CE->getArg(0);
- SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
+ SVal ArgVal = C.getSVal(ArgExpr);
if (const MemRegion *R = ArgVal.getAsRegion()) {
R = R->StripCasts();
@@ -132,7 +132,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
if (!II_chdir)
II_chdir = &Ctx.Idents.get("chdir");
- // Ingnore chroot and chdir.
+ // Ignore chroot and chdir.
if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
return;
diff --git a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
index e04e2ab2c320..d3489282ab62 100644
--- a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
@@ -39,7 +39,7 @@ class DeleteWithNonVirtualDtorChecker
: public Checker<check::PreStmt<CXXDeleteExpr>> {
mutable std::unique_ptr<BugType> BT;
- class DeleteBugVisitor : public BugReporterVisitorImpl<DeleteBugVisitor> {
+ class DeleteBugVisitor : public BugReporterVisitor {
public:
DeleteBugVisitor() : Satisfied(false) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
@@ -110,8 +110,6 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
if (Satisfied)
return nullptr;
- ProgramStateRef State = N->getState();
- const LocationContext *LC = N->getLocationContext();
const Stmt *S = PathDiagnosticLocation::getStmt(N);
if (!S)
return nullptr;
@@ -128,7 +126,7 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
}
// Region associated with the current cast expression.
- const MemRegion *M = State->getSVal(CastE, LC).getAsRegion();
+ const MemRegion *M = N->getSVal(CastE).getAsRegion();
if (!M)
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 598502305633..bc39c92ea970 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -24,22 +24,23 @@ using namespace ento;
namespace {
class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
mutable std::unique_ptr<BuiltinBug> BT;
- void reportBug(const char *Msg,
- ProgramStateRef StateZero,
- CheckerContext &C) const ;
+ void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
};
} // end anonymous namespace
-void DivZeroChecker::reportBug(const char *Msg,
- ProgramStateRef StateZero,
- CheckerContext &C) const {
+void DivZeroChecker::reportBug(
+ const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor) const {
if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
if (!BT)
BT.reset(new BuiltinBug(this, "Division by zero"));
auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ R->addVisitor(std::move(Visitor));
bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R);
C.emitReport(std::move(R));
}
@@ -57,7 +58,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
if (!B->getRHS()->getType()->isScalarType())
return;
- SVal Denom = C.getState()->getSVal(B->getRHS(), C.getLocationContext());
+ SVal Denom = C.getSVal(B->getRHS());
Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
// Divide-by-undefined handled in the generic checking for uses of
@@ -78,7 +79,8 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
bool TaintedD = C.getState()->isTainted(*DV);
if ((stateNotZero && stateZero && TaintedD)) {
- reportBug("Division by a tainted value, possibly zero", stateZero, C);
+ reportBug("Division by a tainted value, possibly zero", stateZero, C,
+ llvm::make_unique<TaintBugVisitor>(*DV));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
index 109897be2931..4e4d81cd6714 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -38,8 +38,7 @@ class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
new BugType(this, "Dynamic and static type mismatch", "Type Error"));
}
- class DynamicTypeBugVisitor
- : public BugReporterVisitorImpl<DynamicTypeBugVisitor> {
+ class DynamicTypeBugVisitor : public BugReporterVisitor {
public:
DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index db9179e018a1..126e57645a43 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -59,7 +59,7 @@ class DynamicTypePropagation:
const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
CheckerContext &C) const;
- /// \brief Return a better dynamic type if one can be derived from the cast.
+ /// Return a better dynamic type if one can be derived from the cast.
const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
CheckerContext &C) const;
@@ -74,7 +74,7 @@ class DynamicTypePropagation:
new BugType(this, "Generics", categories::CoreFoundationObjectiveC));
}
- class GenericsBugVisitor : public BugReporterVisitorImpl<GenericsBugVisitor> {
+ class GenericsBugVisitor : public BugReporterVisitor {
public:
GenericsBugVisitor(SymbolRef S) : Sym(S) {}
@@ -562,7 +562,7 @@ void DynamicTypePropagation::checkPostStmt(const CastExpr *CE,
DestObjectPtrType->isUnspecialized())
return;
- SymbolRef Sym = State->getSVal(CE, C.getLocationContext()).getAsSymbol();
+ SymbolRef Sym = C.getSVal(CE).getAsSymbol();
if (!Sym)
return;
@@ -631,7 +631,7 @@ static const Expr *stripCastsAndSugar(const Expr *E) {
}
static bool isObjCTypeParamDependent(QualType Type) {
- // It is illegal to typedef parameterized types inside an interface. Therfore
+ // It is illegal to typedef parameterized types inside an interface. Therefore
// an Objective-C type can only be dependent on a type parameter when the type
// parameter structurally present in the type itself.
class IsObjCTypeParamDependentTypeVisitor
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 0005ec470d20..8de653c10f7e 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -149,7 +149,7 @@ void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
// A specific instantiation of an inlined function may have more constrained
// values than can generally be assumed. Skip the check.
- if (LC->getCurrentStackFrame()->getParent() != nullptr)
+ if (LC->getStackFrame()->getParent() != nullptr)
return;
reportBug(getArgumentValueString(CE, C), C);
@@ -178,7 +178,7 @@ void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
// when we are analyzing it as an inlined function. This means that
// clang_analyzer_checkInlined(true) should always print TRUE, but
// clang_analyzer_checkInlined(false) should never actually print anything.
- if (LC->getCurrentStackFrame()->getParent() == nullptr)
+ if (LC->getStackFrame()->getParent() == nullptr)
return;
reportBug(getArgumentValueString(CE, C), C);
diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index 3fe89f96a43b..059203fca730 100644
--- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -44,8 +44,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
if (!T->isPointerType())
return;
- ProgramStateRef state = C.getState();
- SVal RV = state->getSVal(B->getRHS(), C.getLocationContext());
+ SVal RV = C.getSVal(B->getRHS());
if (!RV.isConstant() || RV.isZeroConstant())
return;
diff --git a/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
new file mode 100644
index 000000000000..5cb51b01f044
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
@@ -0,0 +1,229 @@
+//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines GCDAntipatternChecker which checks against a common
+// antipattern when synchronous API is emulated from asynchronous callbacks
+// using a semaphore:
+//
+// dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+//
+// AnyCFunctionCall(^{
+// // code…
+// dispatch_semaphore_signal(sema);
+// })
+// dispatch_semaphore_wait(sema, *)
+//
+// Such code is a common performance problem, due to inability of GCD to
+// properly handle QoS when a combination of queues and semaphores is used.
+// Good code would either use asynchronous API (when available), or perform
+// the necessary action in asynchronous callback.
+//
+// Currently, the check is performed using a simple heuristical AST pattern
+// matching.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/Support/Debug.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+// ID of a node at which the diagnostic would be emitted.
+const char *WarnAtNode = "waitcall";
+
+class GCDAntipatternChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+auto callsName(const char *FunctionName)
+ -> decltype(callee(functionDecl())) {
+ return callee(functionDecl(hasName(FunctionName)));
+}
+
+auto equalsBoundArgDecl(int ArgIdx, const char *DeclName)
+ -> decltype(hasArgument(0, expr())) {
+ return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr(
+ to(varDecl(equalsBoundNode(DeclName))))));
+}
+
+auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) {
+ return hasLHS(ignoringParenImpCasts(
+ declRefExpr(to(varDecl().bind(DeclName)))));
+}
+
+/// The pattern is very common in tests, and it is OK to use it there.
+/// We have to heuristics for detecting tests: method name starts with "test"
+/// (used in XCTest), and a class name contains "mock" or "test" (used in
+/// helpers which are not tests themselves, but used exclusively in tests).
+static bool isTest(const Decl *D) {
+ if (const auto* ND = dyn_cast<NamedDecl>(D)) {
+ std::string DeclName = ND->getNameAsString();
+ if (StringRef(DeclName).startswith("test"))
+ return true;
+ }
+ if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (const auto *CD = dyn_cast<ObjCContainerDecl>(OD->getParent())) {
+ std::string ContainerName = CD->getNameAsString();
+ StringRef CN(ContainerName);
+ if (CN.contains_lower("test") || CN.contains_lower("mock"))
+ return true;
+ }
+ }
+ return false;
+}
+
+static auto findGCDAntiPatternWithSemaphore() -> decltype(compoundStmt()) {
+
+ const char *SemaphoreBinding = "semaphore_name";
+ auto SemaphoreCreateM = callExpr(allOf(
+ callsName("dispatch_semaphore_create"),
+ hasArgument(0, ignoringParenCasts(integerLiteral(equals(0))))));
+
+ auto SemaphoreBindingM = anyOf(
+ forEachDescendant(
+ varDecl(hasDescendant(SemaphoreCreateM)).bind(SemaphoreBinding)),
+ forEachDescendant(binaryOperator(bindAssignmentToDecl(SemaphoreBinding),
+ hasRHS(SemaphoreCreateM))));
+
+ auto HasBlockArgumentM = hasAnyArgument(hasType(
+ hasCanonicalType(blockPointerType())
+ ));
+
+ auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr(
+ allOf(
+ callsName("dispatch_semaphore_signal"),
+ equalsBoundArgDecl(0, SemaphoreBinding)
+ )))));
+
+ auto HasBlockAndCallsSignalM = allOf(HasBlockArgumentM, ArgCallsSignalM);
+
+ auto HasBlockCallingSignalM =
+ forEachDescendant(
+ stmt(anyOf(
+ callExpr(HasBlockAndCallsSignalM),
+ objcMessageExpr(HasBlockAndCallsSignalM)
+ )));
+
+ auto SemaphoreWaitM = forEachDescendant(
+ callExpr(
+ allOf(
+ callsName("dispatch_semaphore_wait"),
+ equalsBoundArgDecl(0, SemaphoreBinding)
+ )
+ ).bind(WarnAtNode));
+
+ return compoundStmt(
+ SemaphoreBindingM, HasBlockCallingSignalM, SemaphoreWaitM);
+}
+
+static auto findGCDAntiPatternWithGroup() -> decltype(compoundStmt()) {
+
+ const char *GroupBinding = "group_name";
+ auto DispatchGroupCreateM = callExpr(callsName("dispatch_group_create"));
+
+ auto GroupBindingM = anyOf(
+ forEachDescendant(
+ varDecl(hasDescendant(DispatchGroupCreateM)).bind(GroupBinding)),
+ forEachDescendant(binaryOperator(bindAssignmentToDecl(GroupBinding),
+ hasRHS(DispatchGroupCreateM))));
+
+ auto GroupEnterM = forEachDescendant(
+ stmt(callExpr(allOf(callsName("dispatch_group_enter"),
+ equalsBoundArgDecl(0, GroupBinding)))));
+
+ auto HasBlockArgumentM = hasAnyArgument(hasType(
+ hasCanonicalType(blockPointerType())
+ ));
+
+ auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr(
+ allOf(
+ callsName("dispatch_group_leave"),
+ equalsBoundArgDecl(0, GroupBinding)
+ )))));
+
+ auto HasBlockAndCallsLeaveM = allOf(HasBlockArgumentM, ArgCallsSignalM);
+
+ auto AcceptsBlockM =
+ forEachDescendant(
+ stmt(anyOf(
+ callExpr(HasBlockAndCallsLeaveM),
+ objcMessageExpr(HasBlockAndCallsLeaveM)
+ )));
+
+ auto GroupWaitM = forEachDescendant(
+ callExpr(
+ allOf(
+ callsName("dispatch_group_wait"),
+ equalsBoundArgDecl(0, GroupBinding)
+ )
+ ).bind(WarnAtNode));
+
+ return compoundStmt(GroupBindingM, GroupEnterM, AcceptsBlockM, GroupWaitM);
+}
+
+static void emitDiagnostics(const BoundNodes &Nodes,
+ const char* Type,
+ BugReporter &BR,
+ AnalysisDeclContext *ADC,
+ const GCDAntipatternChecker *Checker) {
+ const auto *SW = Nodes.getNodeAs<CallExpr>(WarnAtNode);
+ assert(SW);
+
+ std::string Diagnostics;
+ llvm::raw_string_ostream OS(Diagnostics);
+ OS << "Waiting on a callback using a " << Type << " creates useless threads "
+ << "and is subject to priority inversion; consider "
+ << "using a synchronous API or changing the caller to be asynchronous";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(),
+ Checker,
+ /*Name=*/"GCD performance anti-pattern",
+ /*Category=*/"Performance",
+ OS.str(),
+ PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC),
+ SW->getSourceRange());
+}
+
+void GCDAntipatternChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ if (isTest(D))
+ return;
+
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ auto SemaphoreMatcherM = findGCDAntiPatternWithSemaphore();
+ auto Matches = match(SemaphoreMatcherM, *D->getBody(), AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, "semaphore", BR, ADC, this);
+
+ auto GroupMatcherM = findGCDAntiPatternWithGroup();
+ Matches = match(GroupMatcherM, *D->getBody(), AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, "group", BR, ADC, this);
+}
+
+}
+
+void ento::registerGCDAntipattern(CheckerManager &Mgr) {
+ Mgr.registerChecker<GCDAntipatternChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
index f0be41b293e4..3ef95e673b87 100644
--- a/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -161,7 +161,7 @@ void GTestChecker::modelAssertionResultCopyConstructor(
const CXXConstructorCall *Call, CheckerContext &C) const {
assert(Call->getNumArgs() == 1);
- // The first parameter of the the copy constructor must be the other
+ // The first parameter of the copy constructor must be the other
// instance to initialize this instances fields from.
SVal OtherVal = Call->getArgSVal(0);
SVal ThisVal = Call->getCXXThisVal();
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 43966656cd8d..899586745a0b 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -48,24 +48,24 @@ private:
BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
}
- /// \brief Catch taint related bugs. Check if tainted data is passed to a
+ /// Catch taint related bugs. Check if tainted data is passed to a
/// system call etc.
bool checkPre(const CallExpr *CE, CheckerContext &C) const;
- /// \brief Add taint sources on a pre-visit.
+ /// Add taint sources on a pre-visit.
void addSourcesPre(const CallExpr *CE, CheckerContext &C) const;
- /// \brief Propagate taint generated at pre-visit.
+ /// Propagate taint generated at pre-visit.
bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const;
- /// \brief Add taint sources on a post visit.
+ /// Add taint sources on a post visit.
void addSourcesPost(const CallExpr *CE, CheckerContext &C) const;
/// Check if the region the expression evaluates to is the standard input,
/// and thus, is tainted.
static bool isStdin(const Expr *E, CheckerContext &C);
- /// \brief Given a pointer argument, return the value it points to.
+ /// Given a pointer argument, return the value it points to.
static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg);
/// Functions defining the attack surface.
@@ -100,26 +100,9 @@ private:
bool generateReportIfTainted(const Expr *E, const char Msg[],
CheckerContext &C) const;
- /// The bug visitor prints a diagnostic message at the location where a given
- /// variable was tainted.
- class TaintBugVisitor
- : public BugReporterVisitorImpl<TaintBugVisitor> {
- private:
- const SVal V;
-
- public:
- TaintBugVisitor(const SVal V) : V(V) {}
- void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); }
-
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN,
- BugReporterContext &BRC,
- BugReport &BR) override;
- };
-
typedef SmallVector<unsigned, 2> ArgVector;
- /// \brief A struct used to specify taint propagation rules for a function.
+ /// A struct used to specify taint propagation rules for a function.
///
/// If any of the possible taint source arguments is tainted, all of the
/// destination arguments should also be tainted. Use InvalidArgIndex in the
@@ -183,7 +166,7 @@ private:
return (V && State->isTainted(*V));
}
- /// \brief Pre-process a function which propagates taint according to the
+ /// Pre-process a function which propagates taint according to the
/// taint rule.
ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const;
@@ -214,28 +197,6 @@ const char GenericTaintChecker::MsgTaintedBufferSize[] =
/// points to data, which should be tainted on return.
REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
-std::shared_ptr<PathDiagnosticPiece>
-GenericTaintChecker::TaintBugVisitor::VisitNode(const ExplodedNode *N,
- const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) {
-
- // Find the ExplodedNode where the taint was first introduced
- if (!N->getState()->isTainted(V) || PrevN->getState()->isTainted(V))
- return nullptr;
-
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
- if (!S)
- return nullptr;
-
- const LocationContext *NCtx = N->getLocationContext();
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx);
- if (!L.isValid() || !L.asLocation().isValid())
- return nullptr;
-
- return std::make_shared<PathDiagnosticEventPiece>(
- L, "Taint originated here");
-}
-
GenericTaintChecker::TaintPropagationRule
GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
const FunctionDecl *FDecl,
@@ -468,7 +429,7 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, CheckerContext &C) const{
Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
const Expr *Arg) {
ProgramStateRef State = C.getState();
- SVal AddrVal = State->getSVal(Arg->IgnoreParens(), C.getLocationContext());
+ SVal AddrVal = C.getSVal(Arg->IgnoreParens());
if (AddrVal.isUnknownOrUndef())
return None;
@@ -621,7 +582,7 @@ ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE,
bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
ProgramStateRef State = C.getState();
- SVal Val = State->getSVal(E, C.getLocationContext());
+ SVal Val = C.getSVal(E);
// stdin is a pointer, so it would be a region.
const MemRegion *MemReg = Val.getAsRegion();
@@ -646,7 +607,8 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC())
if (const PointerType * PtrTy =
dyn_cast<PointerType>(D->getType().getTypePtr()))
- if (PtrTy->getPointeeType() == C.getASTContext().getFILEType())
+ if (PtrTy->getPointeeType().getCanonicalType() ==
+ C.getASTContext().getFILEType().getCanonicalType())
return true;
}
return false;
diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
index cf57b8dca063..f102ca96a5c1 100644
--- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This defines IdenticalExprChecker, a check that warns about
+/// This defines IdenticalExprChecker, a check that warns about
/// unintended use of identical expressions.
///
/// It checks for use of identical expressions with comparison operators and
@@ -296,7 +296,7 @@ bool FindIdenticalExprVisitor::VisitConditionalOperator(
return true;
}
-/// \brief Determines whether two statement trees are identical regarding
+/// Determines whether two statement trees are identical regarding
/// operators and symbols.
///
/// Exceptions: expressions containing macros or functions with possible side
diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
new file mode 100644
index 000000000000..ed877ab34518
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -0,0 +1,252 @@
+//=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a check that marks a raw pointer to a C++ container's
+// inner buffer released when the object is destroyed. This information can
+// be used by MallocChecker to detect use-after-free problems.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AllocationState.h"
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+using PtrSet = llvm::ImmutableSet<SymbolRef>;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+// This is a trick to gain access to PtrSet's Factory.
+namespace clang {
+namespace ento {
+template <>
+struct ProgramStateTrait<PtrSet> : public ProgramStatePartialTrait<PtrSet> {
+ static void *GDMIndex() {
+ static int Index = 0;
+ return &Index;
+ }
+};
+} // end namespace ento
+} // end namespace clang
+
+namespace {
+
+class InnerPointerChecker
+ : public Checker<check::DeadSymbols, check::PostCall> {
+
+ CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
+ InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
+ ShrinkToFitFn, SwapFn;
+
+public:
+ class InnerPointerBRVisitor : public BugReporterVisitor {
+ SymbolRef PtrToBuf;
+
+ public:
+ InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return &Tag;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ // FIXME: Scan the map once in the visitor's constructor and do a direct
+ // lookup by region.
+ bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
+ RawPtrMapTy Map = State->get<RawPtrMap>();
+ for (const auto Entry : Map) {
+ if (Entry.second.contains(Sym))
+ return true;
+ }
+ return false;
+ }
+ };
+
+ InnerPointerChecker()
+ : AppendFn("append"), AssignFn("assign"), ClearFn("clear"),
+ CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"),
+ PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"),
+ ReserveFn("reserve"), ResizeFn("resize"),
+ ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
+
+ /// Check whether the function called on the container object is a
+ /// member function that potentially invalidates pointers referring
+ /// to the objects's internal buffer.
+ bool mayInvalidateBuffer(const CallEvent &Call) const;
+
+ /// Record the connection between the symbol returned by c_str() and the
+ /// corresponding string object region in the ProgramState. Mark the symbol
+ /// released if the string object is destroyed.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ /// Clean up the ProgramState map.
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+// [string.require]
+//
+// "References, pointers, and iterators referring to the elements of a
+// basic_string sequence may be invalidated by the following uses of that
+// basic_string object:
+//
+// -- TODO: As an argument to any standard library function taking a reference
+// to non-const basic_string as an argument. For example, as an argument to
+// non-member functions swap(), operator>>(), and getline(), or as an argument
+// to basic_string::swap().
+//
+// -- Calling non-const member functions, except operator[], at, front, back,
+// begin, rbegin, end, and rend."
+//
+bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const {
+ if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
+ OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
+ if (Opc == OO_Equal || Opc == OO_PlusEqual)
+ return true;
+ return false;
+ }
+ return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
+ Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
+ Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
+ Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
+ Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
+ Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
+ Call.isCalled(SwapFn));
+}
+
+void InnerPointerChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *ICall = dyn_cast<CXXInstanceCall>(&Call);
+ if (!ICall)
+ return;
+
+ SVal Obj = ICall->getCXXThisVal();
+ const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(Obj.getAsRegion());
+ if (!ObjRegion)
+ return;
+
+ auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl();
+ if (TypeDecl->getName() != "basic_string")
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
+ SVal RawPtr = Call.getReturnValue();
+ if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+ // Start tracking this raw pointer by adding it to the set of symbols
+ // associated with this container object in the program state map.
+ PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
+ const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
+ PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
+ assert(C.wasInlined || !Set.contains(Sym));
+ Set = F.add(Set, Sym);
+ State = State->set<RawPtrMap>(ObjRegion, Set);
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ if (mayInvalidateBuffer(Call)) {
+ if (const PtrSet *PS = State->get<RawPtrMap>(ObjRegion)) {
+ // Mark all pointer symbols associated with the deleted object released.
+ const Expr *Origin = Call.getOriginExpr();
+ for (const auto Symbol : *PS) {
+ // NOTE: `Origin` may be null, and will be stored so in the symbol's
+ // `RefState` in MallocChecker's `RegionState` program state map.
+ State = allocation_state::markReleased(State, Symbol, Origin);
+ }
+ State = State->remove<RawPtrMap>(ObjRegion);
+ C.addTransition(State);
+ return;
+ }
+ }
+}
+
+void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
+ RawPtrMapTy RPM = State->get<RawPtrMap>();
+ for (const auto Entry : RPM) {
+ if (!SymReaper.isLiveRegion(Entry.first)) {
+ // Due to incomplete destructor support, some dead regions might
+ // remain in the program state map. Clean them up.
+ State = State->remove<RawPtrMap>(Entry.first);
+ }
+ if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
+ PtrSet CleanedUpSet = *OldSet;
+ for (const auto Symbol : Entry.second) {
+ if (!SymReaper.isLive(Symbol))
+ CleanedUpSet = F.remove(CleanedUpSet, Symbol);
+ }
+ State = CleanedUpSet.isEmpty()
+ ? State->remove<RawPtrMap>(Entry.first)
+ : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
+ }
+ }
+ C.addTransition(State);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+
+ if (!isSymbolTracked(N->getState(), PtrToBuf) ||
+ isSymbolTracked(PrevN->getState(), PtrToBuf))
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Dangling inner pointer obtained here";
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
+ nullptr);
+}
+
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
+ return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
+void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
+ registerNewDeleteChecker(Mgr);
+ Mgr.registerChecker<InnerPointerChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
index 0f9b749506fa..56c250cd1678 100644
--- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -46,19 +46,25 @@
// use setter and getters functions which separate the three cases. To store
// them we use a pointer union of symbol and memory region.
//
-// The checker works the following way: We record the past-end iterator for
-// all containers whenever their `.end()` is called. Since the Constraint
-// Manager cannot handle SVals we need to take over its role. We post-check
-// equality and non-equality comparisons and propagate the position of the
-// iterator to the other side of the comparison if it is past-end and we are in
-// the 'equal' branch (true-branch for `==` and false-branch for `!=`).
+// The checker works the following way: We record the begin and the
+// past-end iterator for all containers whenever their `.begin()` and `.end()`
+// are called. Since the Constraint Manager cannot handle such SVals we need
+// to take over its role. We post-check equality and non-equality comparisons
+// and record that the two sides are equal if we are in the 'equal' branch
+// (true-branch for `==` and false-branch for `!=`).
//
// In case of type-I or type-II iterators we get a concrete integer as a result
// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
// this latter case we record the symbol and reload it in evalAssume() and do
// the propagation there. We also handle (maybe double) negated comparisons
-// which are represented in the form of (x == 0 or x !=0 ) where x is the
+// which are represented in the form of (x == 0 or x != 0) where x is the
// comparison itself.
+//
+// Since `SimpleConstraintManager` cannot handle complex symbolic expressions
+// we only use expressions of the format S, S+n or S-n for iterator positions
+// where S is a conjured symbol and n is an unsigned concrete integer. When
+// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
+// a constraint which we later retrieve when doing an actual comparison.
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -80,7 +86,7 @@ private:
const MemRegion *Cont;
// Abstract offset
- SymbolRef Offset;
+ const SymbolRef Offset;
IteratorPosition(const MemRegion *C, SymbolRef Of)
: Cont(C), Offset(Of) {}
@@ -113,31 +119,39 @@ public:
typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
-// Structure to record the symbolic end position of a container
+// Structure to record the symbolic begin and end position of a container
struct ContainerData {
private:
- SymbolRef End;
+ const SymbolRef Begin, End;
- ContainerData(SymbolRef E) : End(E) {}
+ ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
public:
+ static ContainerData fromBegin(SymbolRef B) {
+ return ContainerData(B, nullptr);
+ }
+
static ContainerData fromEnd(SymbolRef E) {
- return ContainerData(E);
+ return ContainerData(nullptr, E);
}
+ SymbolRef getBegin() const { return Begin; }
SymbolRef getEnd() const { return End; }
- ContainerData newEnd(SymbolRef E) const { return ContainerData(E); }
+ ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
+
+ ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
bool operator==(const ContainerData &X) const {
- return End == X.End;
+ return Begin == X.Begin && End == X.End;
}
bool operator!=(const ContainerData &X) const {
- return End != X.End;
+ return Begin != X.Begin || End != X.End;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(Begin);
ID.Add(End);
}
};
@@ -167,8 +181,9 @@ public:
class IteratorChecker
: public Checker<check::PreCall, check::PostCall,
+ check::PreStmt<CXXOperatorCallExpr>,
check::PostStmt<MaterializeTemporaryExpr>,
- check::DeadSymbols,
+ check::LiveSymbols, check::DeadSymbols,
eval::Assume> {
std::unique_ptr<BugType> OutOfRangeBugType;
@@ -176,10 +191,22 @@ class IteratorChecker
void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
const SVal &RVal, OverloadedOperatorKind Op) const;
void verifyDereference(CheckerContext &C, const SVal &Val) const;
+ void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ bool Postfix) const;
+ void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ bool Postfix) const;
+ void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
+ const SVal &RetVal, const SVal &LHS,
+ const SVal &RHS) const;
+ void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ const SVal &Cont) const;
void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const SVal &Cont) const;
void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
const MemRegion *Cont) const;
+ void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
+ const SVal &RetVal, const SVal &LHS,
+ const SVal &RHS) const;
void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
CheckerContext &C, ExplodedNode *ErrNode) const;
@@ -196,8 +223,10 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreStmt(const CXXOperatorCallExpr *COCE, CheckerContext &C) const;
void checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const;
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
bool Assumption) const;
@@ -217,9 +246,13 @@ namespace {
bool isIteratorType(const QualType &Type);
bool isIterator(const CXXRecordDecl *CRD);
+bool isBeginCall(const FunctionDecl *Func);
bool isEndCall(const FunctionDecl *Func);
bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
bool isDereferenceOperator(OverloadedOperatorKind OK);
+bool isIncrementOperator(OverloadedOperatorKind OK);
+bool isDecrementOperator(OverloadedOperatorKind OK);
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
BinaryOperator::Opcode getOpcode(const SymExpr *SE);
const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
const ProgramStateRef processComparison(ProgramStateRef State,
@@ -230,7 +263,11 @@ const ProgramStateRef saveComparison(ProgramStateRef State,
const SVal &RVal, bool Eq);
const IteratorComparison *loadComparison(ProgramStateRef State,
const SymExpr *Condition);
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont,
+ const SymbolRef Sym);
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
const SymbolRef Sym);
const IteratorPosition *getIteratorPosition(ProgramStateRef State,
@@ -255,6 +292,7 @@ const ContainerData *getContainerData(ProgramStateRef State,
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
const ContainerData &CData);
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos);
+bool isZero(ProgramStateRef State, const NonLoc &Val);
} // namespace
IteratorChecker::IteratorChecker() {
@@ -272,6 +310,22 @@ void IteratorChecker::checkPreCall(const CallEvent &Call,
if (Func->isOverloadedOperator()) {
if (ChecksEnabled[CK_IteratorRangeChecker] &&
+ isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ // Check for out-of-range incrementions and decrementions
+ if (Call.getNumArgs() >= 1) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(),
+ InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ }
+ } else {
+ if (Call.getNumArgs() >= 2) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getArgSVal(1));
+ }
+ }
+ } else if (ChecksEnabled[CK_IteratorRangeChecker] &&
isDereferenceOperator(Func->getOverloadedOperator())) {
// Check for dereference of out-of-range iterators
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
@@ -300,6 +354,36 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1), Op);
}
+ } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() >= 1) {
+ handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(),
+ InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ }
+ } else {
+ if (Call.getNumArgs() >= 2) {
+ handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getArgSVal(1));
+ }
+ }
+ } else if (isIncrementOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ } else {
+ handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ }
+ } else if (isDecrementOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ } else {
+ handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ }
}
} else {
const auto *OrigExpr = Call.getOriginExpr();
@@ -315,6 +399,11 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
return;
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (isBeginCall(Func)) {
+ handleBegin(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal());
+ return;
+ }
if (isEndCall(Func)) {
handleEnd(C, OrigExpr, Call.getReturnValue(),
InstCall->getCXXThisVal());
@@ -351,19 +440,80 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
}
}
+void IteratorChecker::checkPreStmt(const CXXOperatorCallExpr *COCE,
+ CheckerContext &C) const {
+ const auto *ThisExpr = COCE->getArg(0);
+
+ auto State = C.getState();
+ const auto *LCtx = C.getLocationContext();
+
+ const auto CurrentThis = State->getSVal(ThisExpr, LCtx);
+ if (const auto *Reg = CurrentThis.getAsRegion()) {
+ if (!Reg->getAs<CXXTempObjectRegion>())
+ return;
+ const auto OldState = C.getPredecessor()->getFirstPred()->getState();
+ const auto OldThis = OldState->getSVal(ThisExpr, LCtx);
+ // FIXME: This solution is unreliable. It may happen that another checker
+ // subscribes to the pre-statement check of `CXXOperatorCallExpr`
+ // and adds a transition before us. The proper fix is to make the
+ // CFG provide a `ConstructionContext` for the `CXXOperatorCallExpr`,
+ // which would turn the corresponding `CFGStmt` element into a
+ // `CFGCXXRecordTypedCall` element, which will allow `ExprEngine` to
+ // foresee that the `begin()`/`end()` call constructs the object
+ // directly in the temporary region that `CXXOperatorCallExpr` takes
+ // as its implicit object argument.
+ const auto *Pos = getIteratorPosition(OldState, OldThis);
+ if (!Pos)
+ return;
+ State = setIteratorPosition(State, CurrentThis, *Pos);
+ C.addTransition(State);
+ }
+}
+
void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
CheckerContext &C) const {
/* Transfer iterator state to temporary objects */
auto State = C.getState();
- const auto *LCtx = C.getLocationContext();
const auto *Pos =
- getIteratorPosition(State, State->getSVal(MTE->GetTemporaryExpr(), LCtx));
+ getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
if (!Pos)
return;
- State = setIteratorPosition(State, State->getSVal(MTE, LCtx), *Pos);
+ State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
C.addTransition(State);
}
+void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
+ // Keep symbolic expressions of iterator positions, container begins and ends
+ // alive
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto Reg : RegionMap) {
+ const auto Offset = Reg.second.getOffset();
+ for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
+ if (isa<SymbolData>(*i))
+ SR.markLive(*i);
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto Sym : SymbolMap) {
+ const auto Offset = Sym.second.getOffset();
+ for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
+ if (isa<SymbolData>(*i))
+ SR.markLive(*i);
+ }
+
+ auto ContMap = State->get<ContainerMap>();
+ for (const auto Cont : ContMap) {
+ const auto CData = Cont.second;
+ if (CData.getBegin()) {
+ SR.markLive(CData.getBegin());
+ }
+ if (CData.getEnd()) {
+ SR.markLive(CData.getEnd());
+ }
+ }
+}
+
void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
// Cleanup
@@ -471,13 +621,209 @@ void IteratorChecker::verifyDereference(CheckerContext &C,
static CheckerProgramPointTag Tag("IteratorRangeChecker",
"IteratorOutOfRange");
auto *N = C.generateNonFatalErrorNode(State, &Tag);
- if (!N) {
+ if (!N)
return;
- }
reportOutOfRangeBug("Iterator accessed outside of its range.", Val, C, N);
}
}
+void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
+ // Increment the symbolic expressions which represents the position of the
+ // iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (Pos) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto OldOffset = Pos->getOffset();
+ auto NewOffset =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(OldOffset),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(OldOffset)).getAsSymbol();
+ auto NewPos = Pos->setTo(NewOffset);
+ State = setIteratorPosition(State, Iter, NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
+ C.addTransition(State);
+ }
+}
+
+void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
+ // Decrement the symbolic expressions which represents the position of the
+ // iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (Pos) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto OldOffset = Pos->getOffset();
+ auto NewOffset =
+ SVB.evalBinOp(State, BO_Sub,
+ nonloc::SymbolVal(OldOffset),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(OldOffset)).getAsSymbol();
+ auto NewPos = Pos->setTo(NewOffset);
+ State = setIteratorPosition(State, Iter, NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
+ C.addTransition(State);
+ }
+}
+
+// This function tells the analyzer's engine that symbols produced by our
+// checker, most notably iterator positions, are relatively small.
+// A distance between items in the container should not be very large.
+// By assuming that it is within around 1/8 of the address space,
+// we can help the analyzer perform operations on these symbols
+// without being afraid of integer overflows.
+// FIXME: Should we provide it as an API, so that all checkers could use it?
+static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
+ long Scale) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+
+ QualType T = Sym->getType();
+ assert(T->isSignedIntegerOrEnumerationType());
+ APSIntType AT = BV.getAPSIntType(T);
+
+ ProgramStateRef NewState = State;
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
+ SVal IsCappedFromAbove =
+ SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Max), SVB.getConditionType());
+ if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
+ }
+
+ llvm::APSInt Min = -Max;
+ SVal IsCappedFromBelow =
+ SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Min), SVB.getConditionType());
+ if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
+ }
+
+ return NewState;
+}
+
+void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const SVal &RetVal,
+ const SVal &LHS,
+ const SVal &RHS) const {
+ // Increment or decrement the symbolic expressions which represents the
+ // position of the iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, LHS);
+ if (!Pos)
+ return;
+
+ const auto *value = &RHS;
+ if (auto loc = RHS.getAs<Loc>()) {
+ const auto val = State->getRawSVal(*loc);
+ value = &val;
+ }
+
+ auto &SymMgr = C.getSymbolManager();
+ auto &SVB = C.getSValBuilder();
+ auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
+ const auto OldOffset = Pos->getOffset();
+ SymbolRef NewOffset;
+ if (const auto intValue = value->getAs<nonloc::ConcreteInt>()) {
+ // For concrete integers we can calculate the new position
+ NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
+ *intValue,
+ SymMgr.getType(OldOffset)).getAsSymbol();
+ } else {
+ // For other symbols create a new symbol to keep expressions simple
+ const auto &LCtx = C.getLocationContext();
+ NewOffset = SymMgr.conjureSymbol(nullptr, LCtx, SymMgr.getType(OldOffset),
+ C.blockCount());
+ State = assumeNoOverflow(State, NewOffset, 4);
+ }
+ auto NewPos = Pos->setTo(NewOffset);
+ auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
+ State = setIteratorPosition(State, TgtVal, NewPos);
+ C.addTransition(State);
+}
+
+void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const SVal &RetVal,
+ const SVal &LHS,
+ const SVal &RHS) const {
+ auto State = C.getState();
+
+ // If the iterator is initially inside its range, then the operation is valid
+ const auto *Pos = getIteratorPosition(State, LHS);
+ if (!Pos || !isOutOfRange(State, *Pos))
+ return;
+
+ auto value = RHS;
+ if (auto loc = RHS.getAs<Loc>()) {
+ value = State->getRawSVal(*loc);
+ }
+
+ // Incremention or decremention by 0 is never bug
+ if (isZero(State, value.castAs<NonLoc>()))
+ return;
+
+ auto &SymMgr = C.getSymbolManager();
+ auto &SVB = C.getSValBuilder();
+ auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
+ const auto OldOffset = Pos->getOffset();
+ const auto intValue = value.getAs<nonloc::ConcreteInt>();
+ if (!intValue)
+ return;
+
+ auto NewOffset = SVB.evalBinOp(State, BinOp, nonloc::SymbolVal(OldOffset),
+ *intValue,
+ SymMgr.getType(OldOffset)).getAsSymbol();
+ auto NewPos = Pos->setTo(NewOffset);
+
+ // If out of range, the only valid operation is to step into the range
+ if (isOutOfRange(State, NewPos)) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportOutOfRangeBug("Iterator accessed past its end.", LHS, C, N);
+ }
+}
+
+void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal, const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ while (const auto *CBOR = ContReg->getAs<CXXBaseObjectRegion>()) {
+ ContReg = CBOR->getSuperRegion();
+ }
+
+ // If the container already has a begin symbol then use it. Otherwise first
+ // create a new one.
+ auto State = C.getState();
+ auto BeginSym = getContainerBegin(State, ContReg);
+ if (!BeginSym) {
+ auto &SymMgr = C.getSymbolManager();
+ BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, BeginSym, 4);
+ State = createContainerBegin(State, ContReg, BeginSym);
+ }
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(ContReg, BeginSym));
+ C.addTransition(State);
+}
+
void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
const SVal &RetVal, const SVal &Cont) const {
const auto *ContReg = Cont.getAsRegion();
@@ -496,6 +842,7 @@ void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
auto &SymMgr = C.getSymbolManager();
EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, EndSym, 4);
State = createContainerEnd(State, ContReg, EndSym);
}
State = setIteratorPosition(State, RetVal,
@@ -514,6 +861,7 @@ void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
auto &SymMgr = C.getSymbolManager();
auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, Sym, 4);
State = setIteratorPosition(State, RetVal,
IteratorPosition::getPosition(Cont, Sym));
C.addTransition(State);
@@ -529,9 +877,12 @@ void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
namespace {
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
BinaryOperator::Opcode Opc);
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc);
bool isIteratorType(const QualType &Type) {
if (Type->isPointerType())
@@ -585,6 +936,13 @@ bool isIterator(const CXXRecordDecl *CRD) {
HasPostIncrOp && HasDerefOp;
}
+bool isBeginCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ return IdInfo->getName().endswith_lower("begin");
+}
+
bool isEndCall(const FunctionDecl *Func) {
const auto *IdInfo = Func->getIdentifier();
if (!IdInfo)
@@ -601,11 +959,24 @@ bool isDereferenceOperator(OverloadedOperatorKind OK) {
OK == OO_Subscript;
}
+bool isIncrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_PlusPlus;
+}
+
+bool isDecrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_MinusMinus;
+}
+
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
+ return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
+ OK == OO_MinusEqual;
+}
+
BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
return BSE->getOpcode();
} else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
- const auto *COE = dyn_cast<CXXOperatorCallExpr>(SC->getStmt());
+ const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
if (!COE)
return BO_Comma; // Extremal value, neither EQ nor NE
if (COE->getOperator() == OO_EqualEqual) {
@@ -660,6 +1031,14 @@ const IteratorComparison *loadComparison(ProgramStateRef State,
return State->get<IteratorComparisonMap>(Condition);
}
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (!CDataPtr)
+ return nullptr;
+
+ return CDataPtr->getBegin();
+}
+
SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
const auto *CDataPtr = getContainerData(State, Cont);
if (!CDataPtr)
@@ -668,6 +1047,22 @@ SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
return CDataPtr->getEnd();
}
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont,
+ const SymbolRef Sym) {
+ // Only create if it does not exist
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (CDataPtr) {
+ if (CDataPtr->getBegin()) {
+ return State;
+ }
+ const auto CData = CDataPtr->newBegin(Sym);
+ return setContainerData(State, Cont, CData);
+ }
+ const auto CData = ContainerData::fromBegin(Sym);
+ return setContainerData(State, Cont, CData);
+}
+
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
const SymbolRef Sym) {
// Only create if it does not exist
@@ -675,14 +1070,12 @@ ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
if (CDataPtr) {
if (CDataPtr->getEnd()) {
return State;
- } else {
- const auto CData = CDataPtr->newEnd(Sym);
- return setContainerData(State, Cont, CData);
}
- } else {
- const auto CData = ContainerData::fromEnd(Sym);
+ const auto CData = CDataPtr->newEnd(Sym);
return setContainerData(State, Cont, CData);
}
+ const auto CData = ContainerData::fromEnd(Sym);
+ return setContainerData(State, Cont, CData);
}
const ContainerData *getContainerData(ProgramStateRef State,
@@ -767,17 +1160,39 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State,
const IteratorPosition &Pos1,
const IteratorPosition &Pos2,
bool Equal) {
- // Try to compare them and get a defined value
auto &SVB = State->getStateManager().getSValBuilder();
+
+ // FIXME: This code should be reworked as follows:
+ // 1. Subtract the operands using evalBinOp().
+ // 2. Assume that the result doesn't overflow.
+ // 3. Compare the result to 0.
+ // 4. Assume the result of the comparison.
const auto comparison =
SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
- nonloc::SymbolVal(Pos2.getOffset()), SVB.getConditionType())
- .getAs<DefinedSVal>();
- if (comparison) {
- return State->assume(*comparison, Equal);
+ nonloc::SymbolVal(Pos2.getOffset()),
+ SVB.getConditionType());
+
+ assert(comparison.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+
+ auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
+ if (const auto CompSym = comparison.getAsSymbol()) {
+ assert(isa<SymIntExpr>(CompSym) &&
+ "Symbol comparison must be a `SymIntExpr`");
+ assert(BinaryOperator::isComparisonOp(
+ cast<SymIntExpr>(CompSym)->getOpcode()) &&
+ "Symbol comparison must be a comparison");
+ return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
}
- return State;
+ return NewState;
+}
+
+bool isZero(ProgramStateRef State, const NonLoc &Val) {
+ auto &BVF = State->getBasicVals();
+ return compare(State, Val,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
+ BO_EQ);
}
bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
@@ -789,6 +1204,13 @@ bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
// Out of range means less than the begin symbol or greater or equal to the
// end symbol.
+ const auto Beg = CData->getBegin();
+ if (Beg) {
+ if (isLess(State, Pos.getOffset(), Beg)) {
+ return true;
+ }
+ }
+
const auto End = CData->getEnd();
if (End) {
if (isGreaterOrEqual(State, Pos.getOffset(), End)) {
@@ -799,25 +1221,30 @@ bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
return false;
}
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_LT);
+}
+
bool isGreaterOrEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
return compare(State, Sym1, Sym2, BO_GE);
}
bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
BinaryOperator::Opcode Opc) {
- auto &SMgr = State->getStateManager();
- auto &SVB = SMgr.getSValBuilder();
+ return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
+}
+
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc) {
+ auto &SVB = State->getStateManager().getSValBuilder();
const auto comparison =
- SVB.evalBinOp(State, Opc, nonloc::SymbolVal(Sym1),
- nonloc::SymbolVal(Sym2), SVB.getConditionType())
- .getAs<DefinedSVal>();
+ SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
- if(comparison) {
- return !!State->assume(*comparison, true);
- }
+ assert(comparison.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
- return false;
+ return !State->assume(comparison.castAs<DefinedSVal>(), false);
}
} // namespace
@@ -831,3 +1258,4 @@ bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
}
REGISTER_CHECKER(IteratorRangeChecker)
+
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 8076ca09591f..2fb627184eb9 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -20,7 +20,7 @@
// been called on them. An invalidation method should either invalidate all
// the ivars or call another invalidation method (on self).
//
-// Partial invalidor annotation allows to addess cases when ivars are
+// Partial invalidor annotation allows to address cases when ivars are
// invalidated by other methods, which might or might not be called from
// the invalidation method. The checker checks that each invalidation
// method and all the partial methods cumulatively invalidate all ivars.
@@ -402,13 +402,13 @@ visit(const ObjCImplementationDecl *ImplD) const {
// Find the setter and the getter.
const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
if (SetterD) {
- SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
+ SetterD = SetterD->getCanonicalDecl();
PropSetterToIvarMap[SetterD] = ID;
}
const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
if (GetterD) {
- GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
+ GetterD = GetterD->getCanonicalDecl();
PropGetterToIvarMap[GetterD] = ID;
}
}
@@ -606,7 +606,7 @@ void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
const ObjCMessageExpr *ME) {
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD) {
- MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
+ MD = MD->getCanonicalDecl();
MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
if (IvI != PropertyGetterToIvarMap.end())
markInvalidated(IvI->second);
@@ -630,7 +630,7 @@ void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
if (PA->isImplicitProperty()) {
const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
if (MD) {
- MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
+ MD = MD->getCanonicalDecl();
MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
if (IvI != PropertyGetterToIvarMap.end())
markInvalidated(IvI->second);
@@ -702,7 +702,7 @@ void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
// Check if we call a setter and set the property to 'nil'.
if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
- MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
+ MD = MD->getCanonicalDecl();
MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
if (IvI != PropertySetterToIvarMap.end()) {
markInvalidated(IvI->second);
diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 655ce33390c9..849b1193c042 100644
--- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -113,8 +113,7 @@ NonLocalizedStringChecker::NonLocalizedStringChecker() {
}
namespace {
-class NonLocalizedStringBRVisitor final
- : public BugReporterVisitorImpl<NonLocalizedStringBRVisitor> {
+class NonLocalizedStringBRVisitor final : public BugReporterVisitor {
const MemRegion *NonLocalizedString;
bool Satisfied;
@@ -1017,8 +1016,7 @@ NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
if (!LiteralExpr)
return nullptr;
- ProgramStateRef State = Succ->getState();
- SVal LiteralSVal = State->getSVal(LiteralExpr, Succ->getLocationContext());
+ SVal LiteralSVal = Succ->getSVal(LiteralExpr);
if (LiteralSVal.getAsRegion() != NonLocalizedString)
return nullptr;
@@ -1108,7 +1106,7 @@ void EmptyLocalizationContextChecker::checkASTDecl(
void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
const ObjCMessageExpr *ME) {
- // FIXME: We may be able to use PPCallbacks to check for empy context
+ // FIXME: We may be able to use PPCallbacks to check for empty context
// comments as part of preprocessing and avoid this re-lexing hack.
const ObjCInterfaceDecl *OD = ME->getReceiverInterface();
if (!OD)
@@ -1389,7 +1387,7 @@ void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError(
// Generate the bug report.
BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse",
"Localizability Issue (Apple)",
- "Plural cases are not supported accross all languages. "
+ "Plural cases are not supported across all languages. "
"Use a .stringsdict file instead",
PathDiagnosticLocation(S, BR.getSourceManager(), AC));
}
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
index 0ee91cca4793..40eb0631d7c5 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -78,7 +78,7 @@ private:
/// Bug visitor class to find the node where the request region was previously
/// used in order to include it into the BugReport path.
- class RequestNodeVisitor : public BugReporterVisitorImpl<RequestNodeVisitor> {
+ class RequestNodeVisitor : public BugReporterVisitor {
public:
RequestNodeVisitor(const MemRegion *const MemoryRegion,
const std::string &ErrText)
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index f8473dbd7647..b8ef6701c0df 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -120,8 +120,7 @@ private:
/// The bug visitor which allows us to print extra diagnostics along the
/// BugReport path. For example, showing the allocation site of the leaked
/// region.
- class SecKeychainBugVisitor
- : public BugReporterVisitorImpl<SecKeychainBugVisitor> {
+ class SecKeychainBugVisitor : public BugReporterVisitor {
protected:
// The allocated region symbol tracked by the main analysis.
SymbolRef Sym;
@@ -202,7 +201,7 @@ static bool isBadDeallocationArgument(const MemRegion *Arg) {
static SymbolRef getAsPointeeSymbol(const Expr *Expr,
CheckerContext &C) {
ProgramStateRef State = C.getState();
- SVal ArgV = State->getSVal(Expr, C.getLocationContext());
+ SVal ArgV = C.getSVal(Expr);
if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
StoreManager& SM = C.getStoreManager();
@@ -297,7 +296,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
// Check the argument to the deallocator.
const Expr *ArgExpr = CE->getArg(paramIdx);
- SVal ArgSVal = State->getSVal(ArgExpr, C.getLocationContext());
+ SVal ArgSVal = C.getSVal(ArgExpr);
// Undef is reported by another checker.
if (ArgSVal.isUndef())
@@ -426,8 +425,7 @@ void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
// allocated value symbol, since our diagnostics depend on the value
// returned by the call. Ex: Data should only be freed if noErr was
// returned during allocation.)
- SymbolRef RetStatusSymbol =
- State->getSVal(CE, C.getLocationContext()).getAsSymbol();
+ SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
// Track the allocated value in the checker state.
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 851114004b96..8f07f413e81f 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -30,6 +30,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
#include <climits>
#include <utility>
@@ -45,7 +46,8 @@ enum AllocationFamily {
AF_CXXNew,
AF_CXXNewArray,
AF_IfNameIndex,
- AF_Alloca
+ AF_Alloca,
+ AF_InnerBuffer
};
class RefState {
@@ -134,10 +136,10 @@ enum ReallocPairKind {
};
/// \class ReallocPair
-/// \brief Stores information about the symbol being reallocated by a call to
+/// Stores information about the symbol being reallocated by a call to
/// 'realloc' to allow modeling failed reallocation later in the path.
struct ReallocPair {
- // \brief The symbol which realloc reallocated.
+ // The symbol which realloc reallocated.
SymbolRef ReallocatedSym;
ReallocPairKind Kind;
@@ -162,6 +164,7 @@ class MallocChecker : public Checker<check::DeadSymbols,
check::PreCall,
check::PostStmt<CallExpr>,
check::PostStmt<CXXNewExpr>,
+ check::NewAllocator,
check::PreStmt<CXXDeleteExpr>,
check::PostStmt<BlockExpr>,
check::PostObjCMessage,
@@ -207,6 +210,8 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
@@ -253,20 +258,20 @@ private:
void initIdentifierInfo(ASTContext &C) const;
- /// \brief Determine family of a deallocation expression.
+ /// Determine family of a deallocation expression.
AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const;
- /// \brief Print names of allocators and deallocators.
+ /// Print names of allocators and deallocators.
///
/// \returns true on success.
bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
const Expr *E) const;
- /// \brief Print expected name of an allocator based on the deallocator's
+ /// Print expected name of an allocator based on the deallocator's
/// family derived from the DeallocExpr.
void printExpectedAllocName(raw_ostream &os, CheckerContext &C,
const Expr *DeallocExpr) const;
- /// \brief Print expected name of a deallocator based on the allocator's
+ /// Print expected name of a deallocator based on the allocator's
/// family.
void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
@@ -281,10 +286,18 @@ private:
bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
///@}
- /// \brief Perform a zero-allocation check.
+ /// Process C++ operator new()'s allocation, which is the part of C++
+ /// new-expression that goes before the constructor.
+ void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
+ SVal Target) const;
+
+ /// Perform a zero-allocation check.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
const unsigned AllocationSizeArg,
- ProgramStateRef State) const;
+ ProgramStateRef State,
+ Optional<SVal> RetVal = None) const;
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
@@ -300,7 +313,7 @@ private:
AllocationFamily Family = AF_Malloc);
static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
- ProgramStateRef State);
+ ProgramStateRef State, SVal Target);
// Check if this malloc() for special flags. At present that means M_ZERO or
// __GFP_ZERO (in which case, treat it like calloc).
@@ -309,9 +322,12 @@ private:
const ProgramStateRef &State) const;
/// Update the RefState to reflect the new memory allocation.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
static ProgramStateRef
MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
- AllocationFamily Family = AF_Malloc);
+ AllocationFamily Family = AF_Malloc,
+ Optional<SVal> RetVal = None);
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att,
@@ -337,7 +353,7 @@ private:
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State);
- ///\brief Check if the memory associated with this symbol was released.
+ ///Check if the memory associated with this symbol was released.
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
@@ -415,8 +431,7 @@ private:
/// The bug visitor which allows us to print extra diagnostics along the
/// BugReport path. For example, showing the allocation site of the leaked
/// region.
- class MallocBugVisitor final
- : public BugReporterVisitorImpl<MallocBugVisitor> {
+ class MallocBugVisitor final : public BugReporterVisitor {
protected:
enum NotificationMode {
Normal,
@@ -432,15 +447,24 @@ private:
// A symbol from when the primary region should have been reallocated.
SymbolRef FailedReallocSymbol;
+ // A C++ destructor stack frame in which memory was released. Used for
+ // miscellaneous false positive suppression.
+ const StackFrameContext *ReleaseDestructorLC;
+
bool IsLeak;
public:
MallocBugVisitor(SymbolRef S, bool isLeak = false)
- : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), IsLeak(isLeak) {}
+ : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr),
+ ReleaseDestructorLC(nullptr), IsLeak(isLeak) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return &Tag;
+ }
void Profile(llvm::FoldingSetNodeID &ID) const override {
- static int X = 0;
- ID.AddPointer(&X);
+ ID.AddPointer(getTag());
ID.AddPointer(Sym);
}
@@ -456,8 +480,13 @@ private:
inline bool isReleased(const RefState *S, const RefState *SPrev,
const Stmt *Stmt) {
// Did not track -> released. Other state (allocated) -> released.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt)) &&
- (S && S->isReleased()) && (!SPrev || !SPrev->isReleased()));
+ // The statement associated with the release might be missing.
+ bool IsReleased = (S && S->isReleased()) &&
+ (!SPrev || !SPrev->isReleased());
+ assert(!IsReleased ||
+ (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
+ (!Stmt && S->getAllocationFamily() == AF_InnerBuffer));
+ return IsReleased;
}
inline bool isRelinquished(const RefState *S, const RefState *SPrev,
@@ -486,7 +515,7 @@ private:
BugReporterContext &BRC,
BugReport &BR) override;
- std::unique_ptr<PathDiagnosticPiece>
+ std::shared_ptr<PathDiagnosticPiece>
getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
BugReport &BR) override {
if (!IsLeak)
@@ -496,7 +525,7 @@ private:
PathDiagnosticLocation::createEndOfPath(EndPathNode,
BRC.getSourceManager());
// Do not add the statement itself as a range in case of leak.
- return llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(),
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
false);
}
@@ -758,7 +787,7 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
return None;
const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
- const SVal V = State->getSVal(FlagsEx, C.getLocationContext());
+ const SVal V = C.getSVal(FlagsEx);
if (!V.getAs<NonLoc>()) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
@@ -949,13 +978,15 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
}
// Performs a 0-sized allocations check.
-ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
- const Expr *E,
- const unsigned AllocationSizeArg,
- ProgramStateRef State) const {
+ProgramStateRef MallocChecker::ProcessZeroAllocation(
+ CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg,
+ ProgramStateRef State, Optional<SVal> RetVal) const {
if (!State)
return nullptr;
+ if (!RetVal)
+ RetVal = C.getSVal(E);
+
const Expr *Arg = nullptr;
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
@@ -972,8 +1003,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
assert(Arg);
- Optional<DefinedSVal> DefArgVal =
- State->getSVal(Arg, C.getLocationContext()).getAs<DefinedSVal>();
+ Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>();
if (!DefArgVal)
return State;
@@ -988,8 +1018,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(CheckerContext &C,
State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
if (TrueState && !FalseState) {
- SVal retVal = State->getSVal(E, C.getLocationContext());
- SymbolRef Sym = retVal.getAsLocSymbol();
+ SymbolRef Sym = RetVal->getAsLocSymbol();
if (!Sym)
return State;
@@ -1050,9 +1079,9 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
return false;
}
-void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
- CheckerContext &C) const {
-
+void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
+ CheckerContext &C,
+ SVal Target) const {
if (NE->getNumPlacementArgs())
for (CXXNewExpr::const_arg_iterator I = NE->placement_arg_begin(),
E = NE->placement_arg_end(); I != E; ++I)
@@ -1072,37 +1101,48 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
// MallocUpdateRefState() instead of MallocMemAux() which breakes the
// existing binding.
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
- : AF_CXXNew);
- State = addExtentSize(C, NE, State);
- State = ProcessZeroAllocation(C, NE, 0, State);
+ : AF_CXXNew, Target);
+ State = addExtentSize(C, NE, State, Target);
+ State = ProcessZeroAllocation(C, NE, 0, State, Target);
C.addTransition(State);
}
+void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ if (!C.getAnalysisManager().getAnalyzerOptions().mayInlineCXXAllocator())
+ processNewAllocation(NE, C, C.getSVal(NE));
+}
+
+void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &C) const {
+ if (!C.wasInlined)
+ processNewAllocation(NE, C, Target);
+}
+
// Sets the extent value of the MemRegion allocated by
// new expression NE to its size in Bytes.
//
ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
const CXXNewExpr *NE,
- ProgramStateRef State) {
+ ProgramStateRef State,
+ SVal Target) {
if (!State)
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal ElementCount;
- const LocationContext *LCtx = C.getLocationContext();
const SubRegion *Region;
if (NE->isArray()) {
const Expr *SizeExpr = NE->getArraySize();
- ElementCount = State->getSVal(SizeExpr, C.getLocationContext());
+ ElementCount = C.getSVal(SizeExpr);
// Store the extent size for the (symbolic)region
// containing the elements.
- Region = (State->getSVal(NE, LCtx))
- .getAsRegion()
+ Region = Target.getAsRegion()
->getAs<SubRegion>()
- ->getSuperRegion()
+ ->StripCasts()
->getAs<SubRegion>();
} else {
ElementCount = svalBuilder.makeIntVal(1, true);
- Region = (State->getSVal(NE, LCtx)).getAsRegion()->getAs<SubRegion>();
+ Region = Target.getAsRegion()->getAs<SubRegion>();
}
assert(Region);
@@ -1199,7 +1239,8 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
if (I != E) {
- return MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), State);
+ return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
+ State);
}
return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
}
@@ -1212,8 +1253,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
if (!State)
return nullptr;
- return MallocMemAux(C, CE, State->getSVal(SizeEx, C.getLocationContext()),
- Init, State, Family);
+ return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family);
}
ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -1239,7 +1279,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
State = State->BindExpr(CE, C.getLocationContext(), RetVal);
// Fill the region with the initialization value.
- State = State->bindDefault(RetVal, Init, LCtx);
+ State = State->bindDefaultInitial(RetVal, Init, LCtx);
// Set the region's extent equal to the Size parameter.
const SymbolicRegion *R =
@@ -1263,18 +1303,22 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
const Expr *E,
ProgramStateRef State,
- AllocationFamily Family) {
+ AllocationFamily Family,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
// Get the return value.
- SVal retVal = State->getSVal(E, C.getLocationContext());
+ if (!RetVal)
+ RetVal = C.getSVal(E);
// We expect the malloc functions to return a pointer.
- if (!retVal.getAs<Loc>())
+ if (!RetVal->getAs<Loc>())
return nullptr;
- SymbolRef Sym = retVal.getAsLocSymbol();
+ SymbolRef Sym = RetVal->getAsLocSymbol();
+ // This is a return value of a function that was not inlined, such as malloc()
+ // or new(). We've checked that in the caller. Therefore, it must be a symbol.
assert(Sym);
// Set the symbol's state to Allocated.
@@ -1294,9 +1338,9 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
bool ReleasedAllocated = false;
for (const auto &Arg : Att->args()) {
- ProgramStateRef StateI = FreeMemAux(C, CE, State, Arg,
- Att->getOwnKind() == OwnershipAttr::Holds,
- ReleasedAllocated);
+ ProgramStateRef StateI = FreeMemAux(
+ C, CE, State, Arg.getASTIndex(),
+ Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
if (StateI)
State = StateI;
}
@@ -1429,6 +1473,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
case AF_CXXNew: os << "'new'"; return;
case AF_CXXNewArray: os << "'new[]'"; return;
case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+ case AF_InnerBuffer: os << "container-specific allocator"; return;
case AF_Alloca:
case AF_None: llvm_unreachable("not a deallocation expression");
}
@@ -1441,6 +1486,7 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os,
case AF_CXXNew: os << "'delete'"; return;
case AF_CXXNewArray: os << "'delete[]'"; return;
case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
+ case AF_InnerBuffer: os << "container-specific deallocator"; return;
case AF_Alloca:
case AF_None: llvm_unreachable("suspicious argument");
}
@@ -1457,7 +1503,7 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (!State)
return nullptr;
- SVal ArgVal = State->getSVal(ArgExpr, C.getLocationContext());
+ SVal ArgVal = C.getSVal(ArgExpr);
if (!ArgVal.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();
@@ -1615,7 +1661,9 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
return Optional<MallocChecker::CheckKind>();
}
case AF_CXXNew:
- case AF_CXXNewArray: {
+ case AF_CXXNewArray:
+ // FIXME: Add new CheckKind for AF_InnerBuffer.
+ case AF_InnerBuffer: {
if (IsALeakCheck) {
if (ChecksEnabled[CK_NewDeleteLeaksChecker])
return CK_NewDeleteLeaksChecker;
@@ -1945,6 +1993,11 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
R->markInteresting(Sym);
R->addRange(Range);
R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ if (RS->getAllocationFamily() == AF_InnerBuffer)
+ R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));
+
C.emitReport(std::move(R));
}
}
@@ -2047,8 +2100,8 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
if (ExplodedNode *N = C.generateErrorNode()) {
if (!BT_BadFree[*CheckKind])
- BT_BadFree[*CheckKind].reset(
- new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error"));
+ BT_BadFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Bad free", categories::MemoryError));
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
@@ -2084,8 +2137,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
return nullptr;
const Expr *arg0Expr = CE->getArg(0);
- const LocationContext *LCtx = C.getLocationContext();
- SVal Arg0Val = State->getSVal(arg0Expr, LCtx);
+ SVal Arg0Val = C.getSVal(arg0Expr);
if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
return nullptr;
DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
@@ -2099,7 +2151,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
const Expr *Arg1 = CE->getArg(1);
// Get the value of the size argument.
- SVal TotalSize = State->getSVal(Arg1, LCtx);
+ SVal TotalSize = C.getSVal(Arg1);
if (SuffixWithN)
TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));
if (!TotalSize.getAs<DefinedOrUnknownSVal>())
@@ -2133,7 +2185,7 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
// Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
assert(!PrtIsNull);
SymbolRef FromPtr = arg0Val.getAsSymbol();
- SVal RetVal = State->getSVal(CE, LCtx);
+ SVal RetVal = C.getSVal(CE);
SymbolRef ToPtr = RetVal.getAsSymbol();
if (!FromPtr || !ToPtr)
return nullptr;
@@ -2216,7 +2268,7 @@ MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
// Do not show local variables belonging to a function other than
// where the error is reported.
if (!VR ||
- (VR->getStackFrame() == LeakContext->getCurrentStackFrame()))
+ (VR->getStackFrame() == LeakContext->getStackFrame()))
ReferenceRegion = MR;
}
}
@@ -2406,7 +2458,7 @@ void MallocChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const {
// Check if we are returning a symbol.
ProgramStateRef State = C.getState();
- SVal RetVal = State->getSVal(E, C.getLocationContext());
+ SVal RetVal = C.getSVal(E);
SymbolRef Sym = RetVal.getAsSymbol();
if (!Sym)
// If we are returning a field of the allocated struct or an array element,
@@ -2436,8 +2488,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
ProgramStateRef state = C.getState();
const BlockDataRegion *R =
- cast<BlockDataRegion>(state->getSVal(BE,
- C.getLocationContext()).getAsRegion());
+ cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
E = R->referenced_vars_end();
@@ -2793,36 +2844,133 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
return nullptr;
}
+static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) {
+ if (const IdentifierInfo *II = DD->getParent()->getIdentifier()) {
+ StringRef N = II->getName();
+ if (N.contains_lower("ptr") || N.contains_lower("pointer")) {
+ if (N.contains_lower("ref") || N.contains_lower("cnt") ||
+ N.contains_lower("intrusive") || N.contains_lower("shared")) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
BugReport &BR) {
+
ProgramStateRef state = N->getState();
ProgramStateRef statePrev = PrevN->getState();
const RefState *RS = state->get<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(Sym);
- if (!RS)
- return nullptr;
const Stmt *S = PathDiagnosticLocation::getStmt(N);
- if (!S)
+ // When dealing with containers, we sometimes want to give a note
+ // even if the statement is missing.
+ if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
return nullptr;
+ const LocationContext *CurrentLC = N->getLocationContext();
+
+ // If we find an atomic fetch_add or fetch_sub within the destructor in which
+ // the pointer was released (before the release), this is likely a destructor
+ // of a shared pointer.
+ // Because we don't model atomics, and also because we don't know that the
+ // original reference count is positive, we should not report use-after-frees
+ // on objects deleted in such destructors. This can probably be improved
+ // through better shared pointer modeling.
+ if (ReleaseDestructorLC) {
+ if (const auto *AE = dyn_cast<AtomicExpr>(S)) {
+ AtomicExpr::AtomicOp Op = AE->getOp();
+ if (Op == AtomicExpr::AO__c11_atomic_fetch_add ||
+ Op == AtomicExpr::AO__c11_atomic_fetch_sub) {
+ if (ReleaseDestructorLC == CurrentLC ||
+ ReleaseDestructorLC->isParentOf(CurrentLC)) {
+ BR.markInvalid(getTag(), S);
+ }
+ }
+ }
+ }
+
// FIXME: We will eventually need to handle non-statement-based events
// (__attribute__((cleanup))).
// Find out if this is an interesting point and what is the kind.
- const char *Msg = nullptr;
+ StringRef Msg;
StackHintGeneratorForSymbol *StackHint = nullptr;
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+
if (Mode == Normal) {
if (isAllocated(RS, RSPrev, S)) {
Msg = "Memory is allocated";
StackHint = new StackHintGeneratorForSymbol(Sym,
"Returned allocated memory");
} else if (isReleased(RS, RSPrev, S)) {
- Msg = "Memory is released";
+ const auto Family = RS->getAllocationFamily();
+ switch (Family) {
+ case AF_Alloca:
+ case AF_Malloc:
+ case AF_CXXNew:
+ case AF_CXXNewArray:
+ case AF_IfNameIndex:
+ Msg = "Memory is released";
+ break;
+ case AF_InnerBuffer: {
+ OS << "Inner pointer invalidated by call to ";
+ if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
+ OS << "destructor";
+ } else {
+ OS << "'";
+ const Stmt *S = RS->getStmt();
+ if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
+ OS << MemCallE->getMethodDecl()->getNameAsString();
+ } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
+ OS << OpCallE->getDirectCallee()->getNameAsString();
+ }
+ OS << "'";
+ }
+ Msg = OS.str();
+ break;
+ }
+ case AF_None:
+ llvm_unreachable("Unhandled allocation family!");
+ }
StackHint = new StackHintGeneratorForSymbol(Sym,
"Returning; memory was released");
+
+ // See if we're releasing memory while inlining a destructor
+ // (or one of its callees). This turns on various common
+ // false positive suppressions.
+ bool FoundAnyDestructor = false;
+ for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {
+ if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {
+ if (isReferenceCountingPointerDestructor(DD)) {
+ // This immediately looks like a reference-counting destructor.
+ // We're bad at guessing the original reference count of the object,
+ // so suppress the report for now.
+ BR.markInvalid(getTag(), DD);
+ } else if (!FoundAnyDestructor) {
+ assert(!ReleaseDestructorLC &&
+ "There can be only one release point!");
+ // Suspect that it's a reference counting pointer destructor.
+ // On one of the next nodes might find out that it has atomic
+ // reference counting operations within it (see the code above),
+ // and if so, we'd conclude that it likely is a reference counting
+ // pointer destructor.
+ ReleaseDestructorLC = LC->getStackFrame();
+ // It is unlikely that releasing memory is delegated to a destructor
+ // inside a destructor of a shared pointer, because it's fairly hard
+ // to pass the information that the pointer indeed needs to be
+ // released into it. So we're only interested in the innermost
+ // destructor.
+ FoundAnyDestructor = true;
+ }
+ }
+ }
} else if (isRelinquished(RS, RSPrev, S)) {
Msg = "Memory ownership is transferred";
StackHint = new StackHintGeneratorForSymbol(Sym, "");
@@ -2856,13 +3004,24 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
}
}
- if (!Msg)
+ if (Msg.empty())
return nullptr;
assert(StackHint);
// Generate the extra diagnostic.
- PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
- N->getLocationContext());
+ PathDiagnosticLocation Pos;
+ if (!S) {
+ assert(RS->getAllocationFamily() == AF_InnerBuffer);
+ auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();
+ if (!PostImplCall)
+ return nullptr;
+ Pos = PathDiagnosticLocation(PostImplCall->getLocation(),
+ BRC.getSourceManager());
+ } else {
+ Pos = PathDiagnosticLocation(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ }
+
return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint);
}
@@ -2890,6 +3049,20 @@ void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
}
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+ AllocationFamily Family = AF_InnerBuffer;
+ return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
registerCStringCheckerBasic(mgr);
MallocChecker *checker = mgr.registerChecker<MallocChecker>();
@@ -2900,8 +3073,13 @@ void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
mgr.getCurrentCheckName();
// We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
// checker.
- if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker])
+ if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) {
checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
+ // FIXME: This does not set the correct name, but without this workaround
+ // no name will be set at all.
+ checker->CheckNames[MallocChecker::CK_NewDeleteChecker] =
+ mgr.getCurrentCheckName();
+ }
}
#define REGISTER_CHECKER(name) \
diff --git a/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp
index 497978f07815..19c1d077afa1 100644
--- a/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MisusedMovedObjectChecker.cpp
@@ -46,7 +46,7 @@ class MisusedMovedObjectChecker
: public Checker<check::PreCall, check::PostCall, check::EndFunction,
check::DeadSymbols, check::RegionChanges> {
public:
- void checkEndFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
@@ -61,7 +61,7 @@ public:
private:
enum MisuseKind {MK_FunCall, MK_Copy, MK_Move};
- class MovedBugVisitor : public BugReporterVisitorImpl<MovedBugVisitor> {
+ class MovedBugVisitor : public BugReporterVisitor {
public:
MovedBugVisitor(const MemRegion *R) : Region(R), Found(false) {}
@@ -101,8 +101,6 @@ static ProgramStateRef removeFromState(ProgramStateRef State,
const MemRegion *Region) {
if (!Region)
return State;
- // Note: The isSubRegionOf function is not reflexive.
- State = State->remove<TrackedRegionMap>(Region);
for (auto &E : State->get<TrackedRegionMap>()) {
if (E.first->isSubRegionOf(Region))
State = State->remove<TrackedRegionMap>(E.first);
@@ -224,7 +222,8 @@ ExplodedNode *MisusedMovedObjectChecker::reportBug(const MemRegion *Region,
// Removing the function parameters' MemRegion from the state. This is needed
// for PODs where the trivial destructor does not even created nor executed.
-void MisusedMovedObjectChecker::checkEndFunction(CheckerContext &C) const {
+void MisusedMovedObjectChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
auto State = C.getState();
TrackedRegionMapTy Objects = State->get<TrackedRegionMap>();
if (Objects.isEmpty())
diff --git a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
new file mode 100644
index 000000000000..5060b0e0a6e0
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -0,0 +1,88 @@
+// MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker tests the 3rd argument of mmap's calls to check if
+// it is writable and executable in the same time. It's somehow
+// an optional checker since for example in JIT libraries it is pretty common.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+using llvm::APSInt;
+
+namespace {
+class MmapWriteExecChecker : public Checker<check::PreCall> {
+ CallDescription MmapFn;
+ CallDescription MprotectFn;
+ static int ProtWrite;
+ static int ProtExec;
+ static int ProtRead;
+ mutable std::unique_ptr<BugType> BT;
+public:
+ MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ int ProtExecOv;
+ int ProtReadOv;
+};
+}
+
+int MmapWriteExecChecker::ProtWrite = 0x02;
+int MmapWriteExecChecker::ProtExec = 0x04;
+int MmapWriteExecChecker::ProtRead = 0x01;
+
+void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) {
+ SVal ProtVal = Call.getArgSVal(2);
+ Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
+ int64_t Prot = ProtLoc->getValue().getSExtValue();
+ if (ProtExecOv != ProtExec)
+ ProtExec = ProtExecOv;
+ if (ProtReadOv != ProtRead)
+ ProtRead = ProtReadOv;
+
+ // Wrong settings
+ if (ProtRead == ProtExec)
+ return;
+
+ if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
+ if (!BT)
+ BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
+
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can "
+ "lead to exploitable memory regions, which could be overwritten "
+ "with malicious code", N);
+ Report->addRange(Call.getArgSourceRange(2));
+ C.emitReport(std::move(Report));
+ }
+ }
+}
+
+void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
+ MmapWriteExecChecker *Mwec =
+ mgr.registerChecker<MmapWriteExecChecker>();
+ Mwec->ProtExecOv =
+ mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtExec", 0x04, Mwec);
+ Mwec->ProtReadOv =
+ mgr.getAnalyzerOptions().getOptionAsInteger("MmapProtRead", 0x01, Mwec);
+}
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 559c75d7a5b0..2bd68b625c1f 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -186,8 +186,7 @@ static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
}
static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
- const StackFrameContext *
- SFC = C.getLocationContext()->getCurrentStackFrame();
+ const StackFrameContext * SFC = C.getStackFrame();
if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const VarRegion *VR = R->getAs<VarRegion>())
diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 6d05159e51b0..01d2c0491b85 100644
--- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -44,13 +44,9 @@ public:
};
} // end anonymous namespace
-void NonNullParamChecker::checkPreCall(const CallEvent &Call,
- CheckerContext &C) const {
+/// \return Bitvector marking non-null attributes.
+static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
const Decl *FD = Call.getDecl();
- if (!FD)
- return;
-
- // Merge all non-null attributes
unsigned NumArgs = Call.getNumArgs();
llvm::SmallBitVector AttrNonNull(NumArgs);
for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
@@ -58,49 +54,54 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
AttrNonNull.set(0, NumArgs);
break;
}
- for (unsigned Val : NonNull->args()) {
- if (Val >= NumArgs)
+ for (const ParamIdx &Idx : NonNull->args()) {
+ unsigned IdxAST = Idx.getASTIndex();
+ if (IdxAST >= NumArgs)
continue;
- AttrNonNull.set(Val);
+ AttrNonNull.set(IdxAST);
}
}
+ return AttrNonNull;
+}
- ProgramStateRef state = C.getState();
+void NonNullParamChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.getDecl())
+ return;
+
+ llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
+ unsigned NumArgs = Call.getNumArgs();
- CallEvent::param_type_iterator TyI = Call.param_type_begin(),
- TyE = Call.param_type_end();
+ ProgramStateRef state = C.getState();
+ ArrayRef<ParmVarDecl*> parms = Call.parameters();
for (unsigned idx = 0; idx < NumArgs; ++idx) {
+ // For vararg functions, a corresponding parameter decl may not exist.
+ bool HasParam = idx < parms.size();
// Check if the parameter is a reference. We want to report when reference
// to a null pointer is passed as a parameter.
- bool haveRefTypeParam = false;
- if (TyI != TyE) {
- haveRefTypeParam = (*TyI)->isReferenceType();
- TyI++;
- }
-
+ bool haveRefTypeParam =
+ HasParam ? parms[idx]->getType()->isReferenceType() : false;
bool haveAttrNonNull = AttrNonNull[idx];
- if (!haveAttrNonNull) {
- // Check if the parameter is also marked 'nonnull'.
- ArrayRef<ParmVarDecl*> parms = Call.parameters();
- if (idx < parms.size())
- haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
- }
- if (!haveRefTypeParam && !haveAttrNonNull)
+ // Check if the parameter is also marked 'nonnull'.
+ if (!haveAttrNonNull && HasParam)
+ haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
+
+ if (!haveAttrNonNull && !haveRefTypeParam)
continue;
// If the value is unknown or undefined, we can't perform this check.
const Expr *ArgE = Call.getArgExpr(idx);
SVal V = Call.getArgSVal(idx);
- Optional<DefinedSVal> DV = V.getAs<DefinedSVal>();
+ auto DV = V.getAs<DefinedSVal>();
if (!DV)
continue;
- // Process the case when the argument is not a location.
assert(!haveRefTypeParam || DV->getAs<Loc>());
+ // Process the case when the argument is not a location.
if (haveAttrNonNull && !DV->getAs<Loc>()) {
// If the argument is a union type, we want to handle a potential
// transparent_union GCC extension.
@@ -112,66 +113,63 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
continue;
- if (Optional<nonloc::CompoundVal> CSV =
- DV->getAs<nonloc::CompoundVal>()) {
- nonloc::CompoundVal::iterator CSV_I = CSV->begin();
- assert(CSV_I != CSV->end());
- V = *CSV_I;
- DV = V.getAs<DefinedSVal>();
- assert(++CSV_I == CSV->end());
- // FIXME: Handle (some_union){ some_other_union_val }, which turns into
- // a LazyCompoundVal inside a CompoundVal.
- if (!V.getAs<Loc>())
- continue;
- // Retrieve the corresponding expression.
- if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
- if (const InitListExpr *IE =
- dyn_cast<InitListExpr>(CE->getInitializer()))
- ArgE = dyn_cast<Expr>(*(IE->begin()));
-
- } else {
- // FIXME: Handle LazyCompoundVals?
+ auto CSV = DV->getAs<nonloc::CompoundVal>();
+
+ // FIXME: Handle LazyCompoundVals?
+ if (!CSV)
continue;
- }
+
+ V = *(CSV->begin());
+ DV = V.getAs<DefinedSVal>();
+ assert(++CSV->begin() == CSV->end());
+ // FIXME: Handle (some_union){ some_other_union_val }, which turns into
+ // a LazyCompoundVal inside a CompoundVal.
+ if (!V.getAs<Loc>())
+ continue;
+
+ // Retrieve the corresponding expression.
+ if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
+ if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
+ ArgE = dyn_cast<Expr>(*(IE->begin()));
}
ConstraintManager &CM = C.getConstraintManager();
ProgramStateRef stateNotNull, stateNull;
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
- if (stateNull) {
- if (!stateNotNull) {
- // Generate an error node. Check for a null node in case
- // we cache out.
- if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
-
- std::unique_ptr<BugReport> R;
- if (haveAttrNonNull)
- R = genReportNullAttrNonNull(errorNode, ArgE);
- else if (haveRefTypeParam)
- R = genReportReferenceToNullPointer(errorNode, ArgE);
-
- // Highlight the range of the argument that was null.
- R->addRange(Call.getArgSourceRange(idx));
-
- // Emit the bug report.
- C.emitReport(std::move(R));
- }
-
- // Always return. Either we cached out or we just emitted an error.
- return;
+ // Generate an error node. Check for a null node in case
+ // we cache out.
+ if (stateNull && !stateNotNull) {
+ if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
+
+ std::unique_ptr<BugReport> R;
+ if (haveAttrNonNull)
+ R = genReportNullAttrNonNull(errorNode, ArgE);
+ else if (haveRefTypeParam)
+ R = genReportReferenceToNullPointer(errorNode, ArgE);
+
+ // Highlight the range of the argument that was null.
+ R->addRange(Call.getArgSourceRange(idx));
+
+ // Emit the bug report.
+ C.emitReport(std::move(R));
}
+
+ // Always return. Either we cached out or we just emitted an error.
+ return;
+ }
+
+ if (stateNull) {
if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
ImplicitNullDerefEvent event = {
- V, false, N, &C.getBugReporter(),
- /*IsDirectDereference=*/haveRefTypeParam};
+ V, false, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/haveRefTypeParam};
dispatchEvent(event);
}
}
// If a pointer value passed the check we should assume that it is
// indeed not null from this point forward.
- assert(stateNotNull);
state = stateNotNull;
}
diff --git a/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index 0b4ecb41d20f..6f3180eb839a 100644
--- a/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -54,7 +54,7 @@ private:
} // namespace
-/// Lazily initialize cache for required identifier informations.
+/// Lazily initialize cache for required identifier information.
void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (NSStringII)
return;
@@ -73,9 +73,9 @@ void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
return;
ProgramStateRef State = C.getState();
- SVal V = State->getSVal(location.castAs<Loc>());
if (isGlobalConstString(location)) {
+ SVal V = State->getSVal(location.castAs<Loc>());
Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>();
if (Constr) {
diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index fa9a317683ba..7d1ca61c97a9 100644
--- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -30,6 +30,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -40,21 +41,6 @@ using namespace clang;
using namespace ento;
namespace {
-// Do not reorder! The getMostNullable method relies on the order.
-// Optimization: Most pointers expected to be unspecified. When a symbol has an
-// unspecified or nonnull type non of the rules would indicate any problem for
-// that symbol. For this reason only nullable and contradicted nullability are
-// stored for a symbol. When a symbol is already contradicted, it can not be
-// casted back to nullable.
-enum class Nullability : char {
- Contradicted, // Tracked nullability is contradicted by an explicit cast. Do
- // not report any nullability related issue for this symbol.
- // This nullability is propagated aggressively to avoid false
- // positive results. See the comment on getMostNullable method.
- Nullable,
- Unspecified,
- Nonnull
-};
/// Returns the most nullable nullability. This is used for message expressions
/// like [receiver method], where the nullability of this expression is either
@@ -142,8 +128,7 @@ public:
DefaultBool NeedTracking;
private:
- class NullabilityBugVisitor
- : public BugReporterVisitorImpl<NullabilityBugVisitor> {
+ class NullabilityBugVisitor : public BugReporterVisitor {
public:
NullabilityBugVisitor(const MemRegion *M) : Region(M) {}
@@ -265,7 +250,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *,
// initial direct violation has been discovered, and (3) warning after a direct
// violation that has been implicitly or explicitly suppressed (for
// example, with a cast of NULL to _Nonnull). In essence, once an invariant
-// violation is detected on a path, this checker will be esentially turned off
+// violation is detected on a path, this checker will be essentially turned off
// for the rest of the analysis
//
// The analyzer takes this approach (rather than generating a sink node) to
@@ -345,17 +330,6 @@ NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N,
nullptr);
}
-static Nullability getNullabilityAnnotation(QualType Type) {
- const auto *AttrType = Type->getAs<AttributedType>();
- if (!AttrType)
- return Nullability::Unspecified;
- if (AttrType->getAttrKind() == AttributedType::attr_nullable)
- return Nullability::Nullable;
- else if (AttrType->getAttrKind() == AttributedType::attr_nonnull)
- return Nullability::Nonnull;
- return Nullability::Unspecified;
-}
-
/// Returns true when the value stored at the given location is null
/// and the passed in type is nonnnull.
static bool checkValueAtLValForInvariantViolation(ProgramStateRef State,
@@ -560,8 +534,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
if (State->get<InvariantViolated>())
return;
- auto RetSVal =
- State->getSVal(S, C.getLocationContext()).getAs<DefinedOrUnknownSVal>();
+ auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>();
if (!RetSVal)
return;
@@ -873,7 +846,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// are either item retrieval related or not interesting nullability wise.
// Using this fact, to keep the code easier to read just ignore the return
// value of every instance method of dictionaries.
- if (M.isInstanceMessage() && Name.find("Dictionary") != StringRef::npos) {
+ if (M.isInstanceMessage() && Name.contains("Dictionary")) {
State =
State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
C.addTransition(State);
@@ -881,7 +854,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
}
// For similar reasons ignore some methods of Cocoa arrays.
StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0);
- if (Name.find("Array") != StringRef::npos &&
+ if (Name.contains("Array") &&
(FirstSelectorSlot == "firstObject" ||
FirstSelectorSlot == "lastObject")) {
State =
@@ -894,7 +867,7 @@ void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
// encodings are used. Using lossless encodings is so frequent that ignoring
// this class of methods reduced the emitted diagnostics by about 30% on
// some projects (and all of that was false positives).
- if (Name.find("String") != StringRef::npos) {
+ if (Name.contains("String")) {
for (auto Param : M.parameters()) {
if (Param->getName() == "encoding") {
State = State->set<NullabilityMap>(ReturnRegion,
@@ -977,8 +950,7 @@ void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
if (DestNullability == Nullability::Unspecified)
return;
- auto RegionSVal =
- State->getSVal(CE, C.getLocationContext()).getAs<DefinedOrUnknownSVal>();
+ auto RegionSVal = C.getSVal(CE).getAs<DefinedOrUnknownSVal>();
const MemRegion *Region = getTrackRegion(*RegionSVal);
if (!Region)
return;
diff --git a/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
index 40e379cb2efc..d1749cfdbe27 100644
--- a/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -270,8 +270,10 @@ void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
hasRHS(SuspiciousNumberObjectExprM)));
auto ConversionThroughBranchingM =
- ifStmt(hasCondition(SuspiciousNumberObjectExprM))
- .bind("pedantic");
+ ifStmt(allOf(
+ hasCondition(SuspiciousNumberObjectExprM),
+ unless(hasConditionVariableStatement(declStmt())
+ ))).bind("pedantic");
auto ConversionThroughCallM =
callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM),
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index cbaa5c23592d..b7339fe79f69 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -39,7 +39,7 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
const Expr *Ex = S->getSynchExpr();
ProgramStateRef state = C.getState();
- SVal V = state->getSVal(Ex, C.getLocationContext());
+ SVal V = C.getSVal(Ex);
// Uninitialized value used for the mutex?
if (V.getAs<UndefinedVal>()) {
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
new file mode 100644
index 000000000000..81bcda51b8f8
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
@@ -0,0 +1,209 @@
+//===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ObjCAutoreleaseWriteChecker which warns against writes
+// into autoreleased out parameters which cause crashes.
+// An example of a problematic write is a write to {@code error} in the example
+// below:
+//
+// - (BOOL) mymethod:(NSError *__autoreleasing *)error list:(NSArray*) list {
+// [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+// NSString *myString = obj;
+// if ([myString isEqualToString:@"error"] && error)
+// *error = [NSError errorWithDomain:@"MyDomain" code:-1];
+// }];
+// return false;
+// }
+//
+// Such code will crash on read from `*error` due to the autorelease pool
+// in `enumerateObjectsUsingBlock` implementation freeing the error object
+// on exit from the function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/Twine.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+const char *ProblematicWriteBind = "problematicwrite";
+const char *CapturedBind = "capturedbind";
+const char *ParamBind = "parambind";
+const char *IsMethodBind = "ismethodbind";
+
+class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+private:
+ std::vector<std::string> SelectorsWithAutoreleasingPool = {
+ // Common to NSArray, NSSet, NSOrderedSet
+ "enumerateObjectsUsingBlock:",
+ "enumerateObjectsWithOptions:usingBlock:",
+
+ // Common to NSArray and NSOrderedSet
+ "enumerateObjectsAtIndexes:options:usingBlock:",
+ "indexOfObjectAtIndexes:options:passingTest:",
+ "indexesOfObjectsAtIndexes:options:passingTest:",
+ "indexOfObjectPassingTest:",
+ "indexOfObjectWithOptions:passingTest:",
+ "indexesOfObjectsPassingTest:",
+ "indexesOfObjectsWithOptions:passingTest:",
+
+ // NSDictionary
+ "enumerateKeysAndObjectsUsingBlock:",
+ "enumerateKeysAndObjectsWithOptions:usingBlock:",
+ "keysOfEntriesPassingTest:",
+ "keysOfEntriesWithOptions:passingTest:",
+
+ // NSSet
+ "objectsPassingTest:",
+ "objectsWithOptions:passingTest:",
+ "enumerateIndexPathsWithOptions:usingBlock:",
+
+ // NSIndexSet
+ "enumerateIndexesWithOptions:usingBlock:",
+ "enumerateIndexesUsingBlock:",
+ "enumerateIndexesInRange:options:usingBlock:",
+ "enumerateRangesUsingBlock:",
+ "enumerateRangesWithOptions:usingBlock:",
+ "enumerateRangesInRange:options:usingBlock:",
+ "indexPassingTest:",
+ "indexesPassingTest:",
+ "indexWithOptions:passingTest:",
+ "indexesWithOptions:passingTest:",
+ "indexInRange:options:passingTest:",
+ "indexesInRange:options:passingTest:"
+ };
+
+ std::vector<std::string> FunctionsWithAutoreleasingPool = {
+ "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"};
+};
+}
+
+static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
+ return std::vector<llvm::StringRef>(V.begin(), V.end());
+}
+
+static auto callsNames(std::vector<std::string> FunctionNames)
+ -> decltype(callee(functionDecl())) {
+ return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
+}
+
+static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
+ AnalysisManager &AM,
+ const ObjCAutoreleaseWriteChecker *Checker) {
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *PVD = Match.getNodeAs<ParmVarDecl>(ParamBind);
+ QualType Ty = PVD->getType();
+ if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing)
+ return;
+ const char *ActionMsg = "Write to";
+ const auto *MarkedStmt = Match.getNodeAs<Expr>(ProblematicWriteBind);
+ bool IsCapture = false;
+
+ // Prefer to warn on write, but if not available, warn on capture.
+ if (!MarkedStmt) {
+ MarkedStmt = Match.getNodeAs<Expr>(CapturedBind);
+ assert(MarkedStmt);
+ ActionMsg = "Capture of";
+ IsCapture = true;
+ }
+
+ SourceRange Range = MarkedStmt->getSourceRange();
+ PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
+ MarkedStmt, BR.getSourceManager(), ADC);
+ bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
+ const char *Name = IsMethod ? "method" : "function";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(), Checker,
+ /*Name=*/(llvm::Twine(ActionMsg)
+ + " autoreleasing out parameter inside autorelease pool").str(),
+ /*Category=*/"Memory",
+ (llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
+ (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
+ "autorelease pool that may exit before " + Name + " returns; consider "
+ "writing first to a strong local variable declared outside of the block")
+ .str(),
+ Location,
+ Range);
+}
+
+void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+
+ auto DoublePointerParamM =
+ parmVarDecl(hasType(hasCanonicalType(pointerType(
+ pointee(hasCanonicalType(objcObjectPointerType()))))))
+ .bind(ParamBind);
+
+ auto ReferencedParamM =
+ declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind);
+
+ // Write into a binded object, e.g. *ParamBind = X.
+ auto WritesIntoM = binaryOperator(
+ hasLHS(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(
+ ignoringParenImpCasts(ReferencedParamM))
+ )),
+ hasOperatorName("=")
+ ).bind(ProblematicWriteBind);
+
+ auto ArgumentCaptureM = hasAnyArgument(
+ ignoringParenImpCasts(ReferencedParamM));
+ auto CapturedInParamM = stmt(anyOf(
+ callExpr(ArgumentCaptureM),
+ objcMessageExpr(ArgumentCaptureM)));
+
+ // WritesIntoM happens inside a block passed as an argument.
+ auto WritesOrCapturesInBlockM = hasAnyArgument(allOf(
+ hasType(hasCanonicalType(blockPointerType())),
+ forEachDescendant(
+ stmt(anyOf(WritesIntoM, CapturedInParamM))
+ )));
+
+ auto BlockPassedToMarkedFuncM = stmt(anyOf(
+ callExpr(allOf(
+ callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
+ objcMessageExpr(allOf(
+ hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)),
+ WritesOrCapturesInBlockM))
+ ));
+
+ auto HasParamAndWritesInMarkedFuncM = allOf(
+ hasAnyParameter(DoublePointerParamM),
+ forEachDescendant(BlockPassedToMarkedFuncM));
+
+ auto MatcherM = decl(anyOf(
+ objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
+ functionDecl(HasParamAndWritesInMarkedFuncM),
+ blockDecl(HasParamAndWritesInMarkedFuncM)));
+
+ auto Matches = match(MatcherM, *D, AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, this);
+}
+
+void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index 58ebf72660b6..fb05ca630b45 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -39,7 +39,7 @@ class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
}
inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
- SVal ArrayRef = C.getState()->getSVal(E, C.getLocationContext());
+ SVal ArrayRef = C.getSVal(E);
SymbolRef ArraySym = ArrayRef.getAsSymbol();
return ArraySym;
}
@@ -66,13 +66,13 @@ REGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)
void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- SVal SizeV = State->getSVal(Size, C.getLocationContext());
+ SVal SizeV = C.getSVal(Size);
// Undefined is reported by another checker.
if (SizeV.isUnknownOrUndef())
return;
// Get the ArrayRef symbol.
- SVal ArrayRef = State->getSVal(Array, C.getLocationContext());
+ SVal ArrayRef = C.getSVal(Array);
SymbolRef ArraySym = ArrayRef.getAsSymbol();
if (!ArraySym)
return;
@@ -128,7 +128,7 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
// Get the index.
const Expr *IdxExpr = CE->getArg(1);
- SVal IdxVal = State->getSVal(IdxExpr, C.getLocationContext());
+ SVal IdxVal = C.getSVal(IdxExpr);
if (IdxVal.isUnknownOrUndef())
return;
DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 32a1adb587bf..d01c6ae6e093 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -81,7 +81,7 @@ private:
}
-/// \brief Determine whether the given class has a superclass that we want
+/// Determine whether the given class has a superclass that we want
/// to check. The name of the found superclass is stored in SuperclassName.
///
/// \param D The declaration to check for superclasses.
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index ffa3a2700616..629520437369 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -86,11 +86,11 @@ public:
namespace {
enum SelfFlagEnum {
- /// \brief No flag set.
+ /// No flag set.
SelfFlag_None = 0x0,
- /// \brief Value came from 'self'.
+ /// Value came from 'self'.
SelfFlag_Self = 0x1,
- /// \brief Value came from the result of an initializer (e.g. [super init]).
+ /// Value came from the result of an initializer (e.g. [super init]).
SelfFlag_InitRes = 0x2
};
}
@@ -98,7 +98,7 @@ enum SelfFlagEnum {
REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, unsigned)
REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)
-/// \brief A call receiving a reference to 'self' invalidates the object that
+/// A call receiving a reference to 'self' invalidates the object that
/// 'self' contains. This keeps the "self flags" assigned to the 'self'
/// object before the call so we can assign them to the new object that 'self'
/// points to after the call.
@@ -128,11 +128,11 @@ static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {
return getSelfFlags(val, C) & flag;
}
-/// \brief Returns true of the value of the expression is the object that 'self'
+/// Returns true of the value of the expression is the object that 'self'
/// points to and is an object that did not come from the result of calling
/// an initializer.
static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
- SVal exprVal = C.getState()->getSVal(E, C.getLocationContext());
+ SVal exprVal = C.getSVal(E);
if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
return false; // value did not come from 'self'.
if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
@@ -183,7 +183,7 @@ void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
// value out when we return from this method.
state = state->set<CalledInit>(true);
- SVal V = state->getSVal(Msg.getOriginExpr(), C.getLocationContext());
+ SVal V = C.getSVal(Msg.getOriginExpr());
addSelfFlag(state, V, SelfFlag_InitRes, C);
return;
}
@@ -407,7 +407,7 @@ static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
return ID != nullptr;
}
-/// \brief Returns true if the location is 'self'.
+/// Returns true if the location is 'self'.
static bool isSelfVar(SVal location, CheckerContext &C) {
AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
if (!analCtx->getSelfDecl())
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index 69b19a785938..fcba3b33f3e0 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -62,9 +62,7 @@ private:
REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)
namespace {
-class SuperDeallocBRVisitor final
- : public BugReporterVisitorImpl<SuperDeallocBRVisitor> {
-
+class SuperDeallocBRVisitor final : public BugReporterVisitor {
SymbolRef ReceiverSymbol;
bool Satisfied;
diff --git a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 6c0c53dd64cb..f69f3492edb1 100644
--- a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -67,7 +67,7 @@ public:
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}
- /// \brief Look for records of overly padded types. If padding *
+ /// Look for records of overly padded types. If padding *
/// PadMultiplier exceeds AllowedPad, then generate a report.
/// PadMultiplier is used to share code with the array padding
/// checker.
@@ -97,7 +97,7 @@ public:
reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
}
- /// \brief Look for arrays of overly padded types. If the padding of the
+ /// Look for arrays of overly padded types. If the padding of the
/// array type exceeds AllowedPad, then generate a report.
void visitVariable(const VarDecl *VD) const {
const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
@@ -237,7 +237,7 @@ public:
};
std::transform(RD->field_begin(), RD->field_end(),
std::back_inserter(Fields), GatherSizesAndAlignments);
- std::sort(Fields.begin(), Fields.end());
+ llvm::sort(Fields.begin(), Fields.end());
// This lets us skip over vptrs and non-virtual bases,
// so that we can just worry about the fields in our object.
// Note that this does cause us to miss some cases where we
diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index 8caf6df4d970..63f82b275ba2 100644
--- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -154,8 +154,7 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
return;
ProgramStateRef State = C.getState();
- const MemRegion *Region =
- State->getSVal(E, C.getLocationContext()).getAsRegion();
+ const MemRegion *Region = C.getSVal(E).getAsRegion();
if (!Region)
return;
if (PointedNeeded)
@@ -227,7 +226,7 @@ void PointerArithChecker::checkPostStmt(const CallExpr *CE,
if (AllocFunctions.count(FunI) == 0)
return;
- SVal SV = State->getSVal(CE, C.getLocationContext());
+ SVal SV = C.getSVal(CE);
const MemRegion *Region = SV.getAsRegion();
if (!Region)
return;
@@ -248,7 +247,7 @@ void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
AllocKind Kind = getKindOfNewOp(NE, FD);
ProgramStateRef State = C.getState();
- SVal AllocedVal = State->getSVal(NE, C.getLocationContext());
+ SVal AllocedVal = C.getSVal(NE);
const MemRegion *Region = AllocedVal.getAsRegion();
if (!Region)
return;
@@ -263,7 +262,7 @@ void PointerArithChecker::checkPostStmt(const CastExpr *CE,
const Expr *CastedExpr = CE->getSubExpr();
ProgramStateRef State = C.getState();
- SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
+ SVal CastedVal = C.getSVal(CastedExpr);
const MemRegion *Region = CastedVal.getAsRegion();
if (!Region)
@@ -281,7 +280,7 @@ void PointerArithChecker::checkPreStmt(const CastExpr *CE,
const Expr *CastedExpr = CE->getSubExpr();
ProgramStateRef State = C.getState();
- SVal CastedVal = State->getSVal(CastedExpr, C.getLocationContext());
+ SVal CastedVal = C.getSVal(CastedExpr);
const MemRegion *Region = CastedVal.getAsRegion();
if (!Region)
@@ -304,12 +303,15 @@ void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- SVal Idx = State->getSVal(SubsExpr->getIdx(), C.getLocationContext());
+ SVal Idx = C.getSVal(SubsExpr->getIdx());
// Indexing with 0 is OK.
if (Idx.isZeroConstant())
return;
+
+ // Indexing vector-type expressions is also OK.
+ if (SubsExpr->getBase()->getType()->isVectorType())
+ return;
reportPointerArithMisuse(SubsExpr->getBase(), C);
}
@@ -324,14 +326,14 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
ProgramStateRef State = C.getState();
if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
- SVal RHSVal = State->getSVal(Rhs, C.getLocationContext());
+ SVal RHSVal = C.getSVal(Rhs);
if (State->isNull(RHSVal).isConstrainedTrue())
return;
reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
}
// The int += ptr; case is not valid C++.
if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
- SVal LHSVal = State->getSVal(Lhs, C.getLocationContext());
+ SVal LHSVal = C.getSVal(Lhs);
if (State->isNull(LHSVal).isConstrainedTrue())
return;
reportPointerArithMisuse(Rhs, C);
diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 2d33ebc2610d..9aa5348e4c34 100644
--- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -39,10 +39,8 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
if (B->getOpcode() != BO_Sub)
return;
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
- SVal LV = state->getSVal(B->getLHS(), LCtx);
- SVal RV = state->getSVal(B->getRHS(), LCtx);
+ SVal LV = C.getSVal(B->getLHS());
+ SVal RV = C.getSVal(B->getRHS());
const MemRegion *LR = LV.getAsRegion();
const MemRegion *RR = RV.getAsRegion();
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index dab29be1c8fb..10ab952e069b 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -109,8 +109,6 @@ REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
StringRef FName = C.getCalleeName(CE);
if (FName.empty())
return;
@@ -121,34 +119,31 @@ void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
if (FName == "pthread_mutex_lock" ||
FName == "pthread_rwlock_rdlock" ||
FName == "pthread_rwlock_wrlock")
- AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
- false, PthreadSemantics);
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
else if (FName == "lck_mtx_lock" ||
FName == "lck_rw_lock_exclusive" ||
FName == "lck_rw_lock_shared")
- AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
- false, XNUSemantics);
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
else if (FName == "pthread_mutex_trylock" ||
FName == "pthread_rwlock_tryrdlock" ||
FName == "pthread_rwlock_trywrlock")
- AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
true, PthreadSemantics);
else if (FName == "lck_mtx_try_lock" ||
FName == "lck_rw_try_lock_exclusive" ||
FName == "lck_rw_try_lock_shared")
- AcquireLock(C, CE, state->getSVal(CE->getArg(0), LCtx),
- true, XNUSemantics);
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
else if (FName == "pthread_mutex_unlock" ||
FName == "pthread_rwlock_unlock" ||
FName == "lck_mtx_unlock" ||
FName == "lck_rw_done")
- ReleaseLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+ ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
else if (FName == "pthread_mutex_destroy")
- DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), PthreadSemantics);
+ DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
else if (FName == "lck_mtx_destroy")
- DestroyLock(C, CE, state->getSVal(CE->getArg(0), LCtx), XNUSemantics);
+ DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
else if (FName == "pthread_mutex_init")
- InitLock(C, CE, state->getSVal(CE->getArg(0), LCtx));
+ InitLock(C, CE, C.getSVal(CE->getArg(0)));
}
// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
@@ -232,7 +227,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
if (sym)
state = resolvePossiblyDestroyedMutex(state, lockR, sym);
- SVal X = state->getSVal(CE, C.getLocationContext());
+ SVal X = C.getSVal(CE);
if (X.isUnknownOrUndef())
return;
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
index e47494a3e90b..2c1e139330d6 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp
@@ -555,7 +555,7 @@ public:
}
const RetainSummary *find(IdentifierInfo* II, Selector S) {
- // FIXME: Class method lookup. Right now we dont' have a good way
+ // FIXME: Class method lookup. Right now we don't have a good way
// of going between IdentifierInfo* and the class hierarchy.
MapTy::iterator I = M.find(ObjCSummaryKey(II, S));
@@ -883,21 +883,22 @@ RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
//===----------------------------------------------------------------------===//
static bool isRetain(const FunctionDecl *FD, StringRef FName) {
- return FName.endswith("Retain");
+ return FName.startswith_lower("retain") || FName.endswith_lower("retain");
}
static bool isRelease(const FunctionDecl *FD, StringRef FName) {
- return FName.endswith("Release");
+ return FName.startswith_lower("release") || FName.endswith_lower("release");
}
static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
- return FName.endswith("Autorelease");
+ return FName.startswith_lower("autorelease") ||
+ FName.endswith_lower("autorelease");
}
static bool isMakeCollectable(const FunctionDecl *FD, StringRef FName) {
// FIXME: Remove FunctionDecl parameter.
// FIXME: Is it really okay if MakeCollectable isn't a suffix?
- return FName.find("MakeCollectable") != StringRef::npos;
+ return FName.find_lower("MakeCollectable") != StringRef::npos;
}
static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
@@ -1787,8 +1788,7 @@ namespace {
//===---------===//
// Bug Reports. //
//===---------===//
-
- class CFRefReportVisitor : public BugReporterVisitorImpl<CFRefReportVisitor> {
+ class CFRefReportVisitor : public BugReporterVisitor {
protected:
SymbolRef Sym;
const SummaryLogTy &SummaryLog;
@@ -1809,7 +1809,7 @@ namespace {
BugReporterContext &BRC,
BugReport &BR) override;
- std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
+ std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
BugReport &BR) override;
};
@@ -1820,18 +1820,9 @@ namespace {
const SummaryLogTy &log)
: CFRefReportVisitor(sym, GCEnabled, log) {}
- std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
+ std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
const ExplodedNode *N,
BugReport &BR) override;
-
- std::unique_ptr<BugReporterVisitor> clone() const override {
- // The curiously-recurring template pattern only works for one level of
- // subclassing. Rather than make a new template base for
- // CFRefReportVisitor, we simply override clone() to do the right thing.
- // This could be trouble someday if BugReporterVisitorImpl is ever
- // used for something else besides a convenient implementation of clone().
- return llvm::make_unique<CFRefLeakReportVisitor>(*this);
- }
};
class CFRefReport : public BugReport {
@@ -1929,6 +1920,14 @@ static bool isNumericLiteralExpression(const Expr *E) {
isa<CXXBoolLiteralExpr>(E);
}
+static Optional<std::string> describeRegion(const MemRegion *MR) {
+ if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
+ return std::string(VR->getDecl()->getName());
+ // Once we support more storage locations for bindings,
+ // this would need to be improved.
+ return None;
+}
+
/// Returns true if this stack frame is for an Objective-C method that is a
/// property getter or setter whose body has been synthesized by the analyzer.
static bool isSynthesizedAccessor(const StackFrameContext *SFC) {
@@ -1969,8 +1968,8 @@ CFRefReportVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
if (isa<ObjCIvarRefExpr>(S) &&
- isSynthesizedAccessor(LCtx->getCurrentStackFrame())) {
- S = LCtx->getCurrentStackFrame()->getCallSite();
+ isSynthesizedAccessor(LCtx->getStackFrame())) {
+ S = LCtx->getStackFrame()->getCallSite();
}
if (isa<ObjCArrayLiteral>(S)) {
@@ -2298,7 +2297,7 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
const VarRegion *VR = R->getBaseRegion()->getAs<VarRegion>();
// Do not show local variables belonging to a function other than
// where the error is reported.
- if (!VR || VR->getStackFrame() == LeakContext->getCurrentStackFrame())
+ if (!VR || VR->getStackFrame() == LeakContext->getStackFrame())
FirstBinding = R;
}
@@ -2356,14 +2355,14 @@ GetAllocationSite(ProgramStateManager& StateMgr, const ExplodedNode *N,
InterestingMethodContext);
}
-std::unique_ptr<PathDiagnosticPiece>
+std::shared_ptr<PathDiagnosticPiece>
CFRefReportVisitor::getEndPath(BugReporterContext &BRC,
const ExplodedNode *EndN, BugReport &BR) {
BR.markInteresting(Sym);
return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
}
-std::unique_ptr<PathDiagnosticPiece>
+std::shared_ptr<PathDiagnosticPiece>
CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
const ExplodedNode *EndN, BugReport &BR) {
@@ -2393,9 +2392,9 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << "Object leaked: ";
- if (FirstBinding) {
- os << "object allocated and stored into '"
- << FirstBinding->getString() << '\'';
+ Optional<std::string> RegionDescription = describeRegion(FirstBinding);
+ if (RegionDescription) {
+ os << "object allocated and stored into '" << *RegionDescription << '\'';
}
else
os << "allocated object";
@@ -2450,7 +2449,7 @@ CFRefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
os << " is not referenced later in this execution path and has a retain "
"count of +" << RV->getCount();
- return llvm::make_unique<PathDiagnosticEventPiece>(L, os.str());
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
void CFRefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
@@ -2513,7 +2512,8 @@ void CFRefLeakReport::deriveAllocLocation(CheckerContext &Ctx,SymbolRef sym) {
UniqueingDecl = AllocNode->getLocationContext()->getDecl();
}
-void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled, bool IncludeAllocationLine) {
+void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled,
+ bool IncludeAllocationLine) {
assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
Description.clear();
llvm::raw_string_ostream os(Description);
@@ -2522,8 +2522,9 @@ void CFRefLeakReport::createDescription(CheckerContext &Ctx, bool GCEnabled, boo
os << "(when using garbage collection) ";
os << "of an object";
- if (AllocBinding) {
- os << " stored into '" << AllocBinding->getString() << '\'';
+ Optional<std::string> RegionDescription = describeRegion(AllocBinding);
+ if (RegionDescription) {
+ os << " stored into '" << *RegionDescription << '\'';
if (IncludeAllocationLine) {
FullSourceLoc SL(AllocStmt->getLocStart(), Ctx.getSourceManager());
os << " (allocated on line " << SL.getSpellingLineNumber() << ")";
@@ -2742,7 +2743,7 @@ public:
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkBeginFunction(CheckerContext &C) const;
- void checkEndFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym,
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
@@ -2799,9 +2800,7 @@ void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
return;
ProgramStateRef state = C.getState();
- const BlockDataRegion *R =
- cast<BlockDataRegion>(state->getSVal(BE,
- C.getLocationContext()).getAsRegion());
+ auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
E = R->referenced_vars_end();
@@ -2851,7 +2850,7 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
}
ProgramStateRef state = C.getState();
- SymbolRef Sym = state->getSVal(CE, C.getLocationContext()).getAsLocSymbol();
+ SymbolRef Sym = C.getSVal(CE).getAsLocSymbol();
if (!Sym)
return;
const RefVal* T = getRefBinding(state, Sym);
@@ -2874,7 +2873,7 @@ void RetainCountChecker::processObjCLiterals(CheckerContext &C,
ProgramStateRef state = C.getState();
const ExplodedNode *pred = C.getPredecessor();
for (const Stmt *Child : Ex->children()) {
- SVal V = state->getSVal(Child, pred->getLocationContext());
+ SVal V = pred->getSVal(Child);
if (SymbolRef sym = V.getAsSymbol())
if (const RefVal* T = getRefBinding(state, sym)) {
RefVal::Kind hasErr = (RefVal::Kind) 0;
@@ -2913,10 +2912,9 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
CheckerContext &C) const {
const ExplodedNode *Pred = C.getPredecessor();
- const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
- if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) {
+ if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) {
QualType ResultTy = Ex->getType();
State = setRefBinding(State, Sym,
RefVal::makeNotOwned(RetEffect::ObjC, ResultTy));
@@ -3993,7 +3991,8 @@ void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
Ctx.addTransition(state);
}
-void RetainCountChecker::checkEndFunction(CheckerContext &Ctx) const {
+void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &Ctx) const {
ProgramStateRef state = Ctx.getState();
RefBindingsTy B = state->get<RefBindings>();
ExplodedNode *Pred = Ctx.getPredecessor();
diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 19fa0fb193cc..1952715a9b7c 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -40,7 +40,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
if (!RetE)
return;
- SVal V = state->getSVal(RetE, C.getLocationContext());
+ SVal V = C.getSVal(RetE);
const MemRegion *R = V.getAsRegion();
const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
diff --git a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
new file mode 100644
index 000000000000..64b61a0213d2
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
@@ -0,0 +1,217 @@
+//=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//
+//===----------------------------------------------------------------------===//
+//
+// A checker for detecting leaks resulting from allocating temporary
+// autoreleased objects before starting the main run loop.
+//
+// Checks for two antipatterns:
+// 1. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in the same
+// autorelease pool.
+// 2. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in no
+// autorelease pool.
+//
+// Any temporary objects autoreleased in code called in those expressions
+// will not be deallocated until the program exits, and are effectively leaks.
+//
+//===----------------------------------------------------------------------===//
+//
+
+#include "ClangSACheckers.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+const char * RunLoopBind = "NSRunLoopM";
+const char * RunLoopRunBind = "RunLoopRunM";
+const char * OtherMsgBind = "OtherMessageSentM";
+const char * AutoreleasePoolBind = "AutoreleasePoolM";
+
+class RunLoopAutoreleaseLeakChecker : public Checker<
+ check::ASTCodeBody> {
+
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+
+};
+
+} // end anonymous namespace
+
+
+using TriBoolTy = Optional<bool>;
+using MemoizationMapTy = llvm::DenseMap<const Stmt *, Optional<TriBoolTy>>;
+
+static TriBoolTy
+seenBeforeRec(const Stmt *Parent, const Stmt *A, const Stmt *B,
+ MemoizationMapTy &Memoization) {
+ for (const Stmt *C : Parent->children()) {
+ if (C == A)
+ return true;
+
+ if (C == B)
+ return false;
+
+ Optional<TriBoolTy> &Cached = Memoization[C];
+ if (!Cached)
+ Cached = seenBeforeRec(C, A, B, Memoization);
+
+ if (Cached->hasValue())
+ return Cached->getValue();
+ }
+
+ return None;
+}
+
+/// \return Whether {@code A} occurs before {@code B} in traversal of
+/// {@code Parent}.
+/// Conceptually a very incomplete/unsound approximation of happens-before
+/// relationship (A is likely to be evaluated before B),
+/// but useful enough in this case.
+static bool seenBefore(const Stmt *Parent, const Stmt *A, const Stmt *B) {
+ MemoizationMapTy Memoization;
+ TriBoolTy Val = seenBeforeRec(Parent, A, B, Memoization);
+ return Val.getValue();
+}
+
+static void emitDiagnostics(BoundNodes &Match,
+ const Decl *D,
+ BugReporter &BR,
+ AnalysisManager &AM,
+ const RunLoopAutoreleaseLeakChecker *Checker) {
+
+ assert(D->hasBody());
+ const Stmt *DeclBody = D->getBody();
+
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *ME = Match.getNodeAs<ObjCMessageExpr>(OtherMsgBind);
+ assert(ME);
+
+ const auto *AP =
+ Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind);
+ bool HasAutoreleasePool = (AP != nullptr);
+
+ const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind);
+ const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind);
+ assert(RLR && "Run loop launch not found");
+
+ assert(ME != RLR);
+ if (HasAutoreleasePool && seenBefore(AP, RLR, ME))
+ return;
+
+ if (!HasAutoreleasePool && seenBefore(DeclBody, RLR, ME))
+ return;
+
+ PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
+ ME, BR.getSourceManager(), ADC);
+ SourceRange Range = ME->getSourceRange();
+
+ BR.EmitBasicReport(ADC->getDecl(), Checker,
+ /*Name=*/"Memory leak inside autorelease pool",
+ /*Category=*/"Memory",
+ /*Name=*/
+ (Twine("Temporary objects allocated in the") +
+ " autorelease pool " +
+ (HasAutoreleasePool ? "" : "of last resort ") +
+ "followed by the launch of " +
+ (RL ? "main run loop " : "xpc_main ") +
+ "may never get released; consider moving them to a "
+ "separate autorelease pool")
+ .str(),
+ Location, Range);
+}
+
+static StatementMatcher getRunLoopRunM(StatementMatcher Extra = anything()) {
+ StatementMatcher MainRunLoopM =
+ objcMessageExpr(hasSelector("mainRunLoop"),
+ hasReceiverType(asString("NSRunLoop")),
+ Extra)
+ .bind(RunLoopBind);
+
+ StatementMatcher MainRunLoopRunM = objcMessageExpr(hasSelector("run"),
+ hasReceiver(MainRunLoopM),
+ Extra).bind(RunLoopRunBind);
+
+ StatementMatcher XPCRunM =
+ callExpr(callee(functionDecl(hasName("xpc_main")))).bind(RunLoopRunBind);
+ return anyOf(MainRunLoopRunM, XPCRunM);
+}
+
+static StatementMatcher getOtherMessageSentM(StatementMatcher Extra = anything()) {
+ return objcMessageExpr(unless(anyOf(equalsBoundNode(RunLoopBind),
+ equalsBoundNode(RunLoopRunBind))),
+ Extra)
+ .bind(OtherMsgBind);
+}
+
+static void
+checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
+ const RunLoopAutoreleaseLeakChecker *Chkr) {
+ StatementMatcher RunLoopRunM = getRunLoopRunM();
+ StatementMatcher OtherMessageSentM = getOtherMessageSentM();
+
+ StatementMatcher RunLoopInAutorelease =
+ autoreleasePoolStmt(
+ hasDescendant(RunLoopRunM),
+ hasDescendant(OtherMessageSentM)).bind(AutoreleasePoolBind);
+
+ DeclarationMatcher GroupM = decl(hasDescendant(RunLoopInAutorelease));
+
+ auto Matches = match(GroupM, *D, AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, Chkr);
+}
+
+static void
+checkTempObjectsInNoPool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
+ const RunLoopAutoreleaseLeakChecker *Chkr) {
+
+ auto NoPoolM = unless(hasAncestor(autoreleasePoolStmt()));
+
+ StatementMatcher RunLoopRunM = getRunLoopRunM(NoPoolM);
+ StatementMatcher OtherMessageSentM = getOtherMessageSentM(NoPoolM);
+
+ DeclarationMatcher GroupM = functionDecl(
+ isMain(),
+ hasDescendant(RunLoopRunM),
+ hasDescendant(OtherMessageSentM)
+ );
+
+ auto Matches = match(GroupM, *D, AM.getASTContext());
+
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, Chkr);
+
+}
+
+void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ checkTempObjectsInSamePool(D, AM, BR, this);
+ checkTempObjectsInNoPool(D, AM, BR, this);
+}
+
+void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) {
+ mgr.registerChecker<RunLoopAutoreleaseLeakChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 25975628c553..feae9e59b343 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -47,7 +47,7 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
- void checkEndFunction(CheckerContext &Ctx) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
private:
void checkReturnedBlockCaptures(const BlockDataRegion &B,
@@ -120,7 +120,7 @@ bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
CheckerContext &C) {
const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
- return S->getStackFrame() != C.getLocationContext()->getCurrentStackFrame();
+ return S->getStackFrame() != C.getStackFrame();
}
bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
@@ -255,8 +255,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
return;
RetE = RetE->IgnoreParens();
- const LocationContext *LCtx = C.getLocationContext();
- SVal V = C.getState()->getSVal(RetE, LCtx);
+ SVal V = C.getSVal(RetE);
const MemRegion *R = V.getAsRegion();
if (!R)
return;
@@ -288,7 +287,8 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
EmitStackError(C, R, RetE);
}
-void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
+void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &Ctx) const {
if (!ChecksEnabled[CK_StackAddrEscapeChecker])
return;
@@ -304,8 +304,7 @@ void StackAddrEscapeChecker::checkEndFunction(CheckerContext &Ctx) const {
public:
SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
- CallBack(CheckerContext &CC)
- : Ctx(CC), CurSFC(CC.getLocationContext()->getCurrentStackFrame()) {}
+ CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
SVal Val) override {
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 915514b42133..d77975559e3f 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -242,22 +242,19 @@ void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
return;
}
void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(3), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
return;
}
void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!(state = CheckNullStream(state->getSVal(CE->getArg(0),
- C.getLocationContext()), state, C)))
+ if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
return;
// Check the legality of the 'whence' argument of 'fseek'.
SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
@@ -283,57 +280,49 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
ProgramStateRef state = C.getState();
- if (!CheckNullStream(state->getSVal(CE->getArg(0), C.getLocationContext()),
- state, C))
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
return;
}
@@ -363,8 +352,7 @@ ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
ProgramStateRef state,
CheckerContext &C) const {
- SymbolRef Sym =
- state->getSVal(CE->getArg(0), C.getLocationContext()).getAsSymbol();
+ SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
if (!Sym)
return state;
diff --git a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index 5268bbf5562e..f4c0edbab3f0 100644
--- a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -55,7 +55,7 @@ public:
}
};
-class DivisionBRVisitor : public BugReporterVisitorImpl<DivisionBRVisitor> {
+class DivisionBRVisitor : public BugReporterVisitor {
private:
SymbolRef ZeroSymbol;
const StackFrameContext *SFC;
@@ -85,7 +85,7 @@ class TestAfterDivZeroChecker
public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
- void checkEndFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
void setDivZeroMap(SVal Var, CheckerContext &C) const;
bool hasDivZeroMap(SVal Var, const CheckerContext &C) const;
bool isZero(SVal S, CheckerContext &C) const;
@@ -114,8 +114,7 @@ DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred,
if (!E)
return nullptr;
- ProgramStateRef State = Succ->getState();
- SVal S = State->getSVal(E, Succ->getLocationContext());
+ SVal S = Succ->getSVal(E);
if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) {
Satisfied = true;
@@ -181,7 +180,8 @@ void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
}
}
-void TestAfterDivZeroChecker::checkEndFunction(CheckerContext &C) const {
+void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
ProgramStateRef State = C.getState();
DivZeroMapTy DivZeroes = State->get<DivZeroMap>();
diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
index 8ad962875b06..ee185b813611 100644
--- a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -30,7 +30,7 @@ class TraversalDumper : public Checker< check::BranchCondition,
public:
void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
void checkBeginFunction(CheckerContext &C) const;
- void checkEndFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
};
}
@@ -56,7 +56,8 @@ void TraversalDumper::checkBeginFunction(CheckerContext &C) const {
llvm::outs() << "--BEGIN FUNCTION--\n";
}
-void TraversalDumper::checkEndFunction(CheckerContext &C) const {
+void TraversalDumper::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
llvm::outs() << "--END FUNCTION--\n";
}
diff --git a/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
new file mode 100644
index 000000000000..f3d68014224d
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
@@ -0,0 +1,90 @@
+//== TrustNonnullChecker.cpp - Checker for trusting annotations -*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker adds an assumption that methods annotated with _Nonnull
+// which come from system headers actually return a non-null pointer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class TrustNonnullChecker : public Checker<check::PostCall> {
+private:
+ /// \returns Whether we trust the result of the method call to be
+ /// a non-null pointer.
+ bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const {
+ QualType ExprRetType = Call.getResultType();
+ if (!ExprRetType->isAnyPointerType())
+ return false;
+
+ if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull)
+ return true;
+
+ // The logic for ObjC instance method calls is more complicated,
+ // as the return value is nil when the receiver is nil.
+ if (!isa<ObjCMethodCall>(&Call))
+ return false;
+
+ const auto *MCall = cast<ObjCMethodCall>(&Call);
+ const ObjCMethodDecl *MD = MCall->getDecl();
+
+ // Distrust protocols.
+ if (isa<ObjCProtocolDecl>(MD->getDeclContext()))
+ return false;
+
+ QualType DeclRetType = MD->getReturnType();
+ if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull)
+ return false;
+
+ // For class messages it is sufficient for the declaration to be
+ // annotated _Nonnull.
+ if (!MCall->isInstanceMessage())
+ return true;
+
+ // Alternatively, the analyzer could know that the receiver is not null.
+ SVal Receiver = MCall->getReceiverSVal();
+ ConditionTruthVal TV = C.getState()->isNonNull(Receiver);
+ if (TV.isConstrainedTrue())
+ return true;
+
+ return false;
+ }
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ // Only trust annotations for system headers for non-protocols.
+ if (!Call.isInSystemHeader())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (isNonNullPtr(Call, C))
+ if (auto L = Call.getReturnValue().getAs<Loc>())
+ State = State->assume(*L, /*Assumption=*/true);
+
+ C.addTransition(State);
+ }
+};
+
+} // end empty namespace
+
+
+void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<TrustNonnullChecker>();
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 0a274292aa39..934ee63318fa 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -59,7 +59,7 @@ public:
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
CheckerContext &Ctx) const {
- SVal X = Ctx.getState()->getSVal(Condition, Ctx.getLocationContext());
+ SVal X = Ctx.getSVal(Condition);
if (X.isUndef()) {
// Generate a sink node, which implicitly marks both outgoing branches as
// infeasible.
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 17fe8610da06..6a93c10c7644 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -55,9 +55,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
return;
ProgramStateRef state = C.getState();
- const BlockDataRegion *R =
- cast<BlockDataRegion>(state->getSVal(BE,
- C.getLocationContext()).getAsRegion());
+ auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
E = R->referenced_vars_end();
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 172ce346f1ba..b9a93bedca2e 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -37,12 +37,11 @@ public:
static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
if (!isa<ArraySubscriptExpr>(Ex))
return false;
- SVal Loc = state->getSVal(Ex, LCtx);
+ SVal Loc = C.getSVal(Ex);
if (!Loc.isValid())
return false;
@@ -64,11 +63,18 @@ static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
}
+static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
+ CheckerContext &C) {
+ SValBuilder &SB = C.getSValBuilder();
+ ProgramStateRef State = C.getState();
+ const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
+ const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
+ return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
+}
+
void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
CheckerContext &C) const {
- ProgramStateRef state = C.getState();
- const LocationContext *LCtx = C.getLocationContext();
- if (state->getSVal(B, LCtx).isUndef()) {
+ if (C.getSVal(B).isUndef()) {
// Do not report assignments of uninitialized values inside swap functions.
// This should allow to swap partially uninitialized structs
@@ -92,11 +98,11 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
const Expr *Ex = nullptr;
bool isLeft = true;
- if (state->getSVal(B->getLHS(), LCtx).isUndef()) {
+ if (C.getSVal(B->getLHS()).isUndef()) {
Ex = B->getLHS()->IgnoreParenCasts();
isLeft = true;
}
- else if (state->getSVal(B->getRHS(), LCtx).isUndef()) {
+ else if (C.getSVal(B->getRHS()).isUndef()) {
Ex = B->getRHS()->IgnoreParenCasts();
isLeft = false;
}
@@ -141,6 +147,19 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
C.isNegative(B->getLHS())) {
OS << "The result of the left shift is undefined because the left "
"operand is negative";
+ } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
+ isLeftShiftResultUnrepresentable(B, C)) {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SB = C.getSValBuilder();
+ const llvm::APSInt *LHS =
+ SB.getKnownValue(State, C.getSVal(B->getLHS()));
+ const llvm::APSInt *RHS =
+ SB.getKnownValue(State, C.getSVal(B->getRHS()));
+ OS << "The result of the left shift is undefined due to shifting \'"
+ << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
+ << "\', which is unrepresentable in the unsigned version of "
+ << "the return type \'" << B->getLHS()->getType().getAsString()
+ << "\'";
} else {
OS << "The result of the '"
<< BinaryOperator::getOpcodeStr(B->getOpcode())
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index c3dcf1fac197..2ef6855ba6b7 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -51,17 +51,20 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (!N)
return;
- const char *str = "Assigned value is garbage or undefined";
-
+ static const char *const DefaultMsg =
+ "Assigned value is garbage or undefined";
if (!BT)
- BT.reset(new BuiltinBug(this, str));
+ BT.reset(new BuiltinBug(this, DefaultMsg));
// Generate a report for this bug.
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+
const Expr *ex = nullptr;
while (StoreE) {
if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
- str = "The expression is an uninitialized value. "
+ OS << "The expression is an uninitialized value. "
"The computed value will also be garbage";
ex = U->getSubExpr();
@@ -70,9 +73,8 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
if (B->isCompoundAssignmentOp()) {
- ProgramStateRef state = C.getState();
- if (state->getSVal(B->getLHS(), C.getLocationContext()).isUndef()) {
- str = "The left expression of the compound assignment is an "
+ if (C.getSVal(B->getLHS()).isUndef()) {
+ OS << "The left expression of the compound assignment is an "
"uninitialized value. The computed value will also be garbage";
ex = B->getLHS();
break;
@@ -88,10 +90,26 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
ex = VD->getInit();
}
+ if (const auto *CD =
+ dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
+ if (CD->isImplicit()) {
+ for (auto I : CD->inits()) {
+ if (I->getInit()->IgnoreImpCasts() == StoreE) {
+ OS << "Value assigned to field '" << I->getMember()->getName()
+ << "' in implicit constructor is garbage or undefined";
+ break;
+ }
+ }
+ }
+ }
+
break;
}
- auto R = llvm::make_unique<BugReport>(*BT, str, N);
+ if (OS.str().empty())
+ OS << DefaultMsg;
+
+ auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
if (ex) {
R->addRange(ex->getSourceRange());
bugreporter::trackNullOrUndefValue(N, ex, *R);
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
new file mode 100644
index 000000000000..398228a9d887
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
@@ -0,0 +1,688 @@
+//===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that reports uninitialized fields in objects
+// created after a constructor call.
+//
+// This checker has two options:
+// - "Pedantic" (boolean). If its not set or is set to false, the checker
+// won't emit warnings for objects that don't have at least one initialized
+// field. This may be set with
+//
+// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`.
+//
+// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
+// warning for each uninitalized field, as opposed to emitting one warning
+// per constructor call, and listing the uninitialized fields that belongs
+// to it in notes. Defaults to false.
+//
+// `-analyzer-config alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <algorithm>
+
+using namespace clang;
+using namespace clang::ento;
+
+namespace {
+
+class UninitializedObjectChecker : public Checker<check::EndFunction> {
+ std::unique_ptr<BuiltinBug> BT_uninitField;
+
+public:
+ // These fields will be initialized when registering the checker.
+ bool IsPedantic;
+ bool ShouldConvertNotesToWarnings;
+
+ UninitializedObjectChecker()
+ : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+};
+
+/// Represents a field chain. A field chain is a vector of fields where the
+/// first element of the chain is the object under checking (not stored), and
+/// every other element is a field, and the element that precedes it is the
+/// object that contains it.
+///
+/// Note that this class is immutable, and new fields may only be added through
+/// constructor calls.
+class FieldChainInfo {
+ using FieldChain = llvm::ImmutableList<const FieldRegion *>;
+
+ FieldChain Chain;
+
+ const bool IsDereferenced = false;
+
+public:
+ FieldChainInfo() = default;
+
+ FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
+ : Chain(Other.Chain), IsDereferenced(IsDereferenced) {}
+
+ FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR,
+ const bool IsDereferenced = false);
+
+ bool contains(const FieldRegion *FR) const { return Chain.contains(FR); }
+ bool isPointer() const;
+
+ /// If this is a fieldchain whose last element is an uninitialized region of a
+ /// pointer type, `IsDereferenced` will store whether the pointer itself or
+ /// the pointee is uninitialized.
+ bool isDereferenced() const;
+ const FieldDecl *getEndOfChain() const;
+ void print(llvm::raw_ostream &Out) const;
+
+private:
+ /// Prints every element except the last to `Out`. Since ImmutableLists store
+ /// elements in reverse order, and have no reverse iterators, we use a
+ /// recursive function to print the fieldchain correctly. The last element in
+ /// the chain is to be printed by `print`.
+ static void printTail(llvm::raw_ostream &Out,
+ const llvm::ImmutableListImpl<const FieldRegion *> *L);
+ friend struct FieldChainInfoComparator;
+};
+
+struct FieldChainInfoComparator {
+ bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) const {
+ assert(!lhs.Chain.isEmpty() && !rhs.Chain.isEmpty() &&
+ "Attempted to store an empty fieldchain!");
+ return *lhs.Chain.begin() < *rhs.Chain.begin();
+ }
+};
+
+using UninitFieldSet = std::set<FieldChainInfo, FieldChainInfoComparator>;
+
+/// Searches for and stores uninitialized fields in a non-union object.
+class FindUninitializedFields {
+ ProgramStateRef State;
+ const TypedValueRegion *const ObjectR;
+
+ const bool IsPedantic;
+ bool IsAnyFieldInitialized = false;
+
+ UninitFieldSet UninitFields;
+
+public:
+ FindUninitializedFields(ProgramStateRef State,
+ const TypedValueRegion *const R, bool IsPedantic);
+ const UninitFieldSet &getUninitFields();
+
+private:
+ /// Adds a FieldChainInfo object to UninitFields. Return true if an insertion
+ /// took place.
+ bool addFieldToUninits(FieldChainInfo LocalChain);
+
+ // For the purposes of this checker, we'll regard the object under checking as
+ // a directed tree, where
+ // * the root is the object under checking
+ // * every node is an object that is
+ // - a union
+ // - a non-union record
+ // - a pointer/reference
+ // - an array
+ // - of a primitive type, which we'll define later in a helper function.
+ // * the parent of each node is the object that contains it
+ // * every leaf is an array, a primitive object, a nullptr or an undefined
+ // pointer.
+ //
+ // Example:
+ //
+ // struct A {
+ // struct B {
+ // int x, y = 0;
+ // };
+ // B b;
+ // int *iptr = new int;
+ // B* bptr;
+ //
+ // A() {}
+ // };
+ //
+ // The directed tree:
+ //
+ // ->x
+ // /
+ // ->b--->y
+ // /
+ // A-->iptr->(int value)
+ // \
+ // ->bptr
+ //
+ // From this we'll construct a vector of fieldchains, where each fieldchain
+ // represents an uninitialized field. An uninitialized field may be a
+ // primitive object, a pointer, a pointee or a union without a single
+ // initialized field.
+ // In the above example, for the default constructor call we'll end up with
+ // these fieldchains:
+ //
+ // this->b.x
+ // this->iptr (pointee uninit)
+ // this->bptr (pointer uninit)
+ //
+ // We'll traverse each node of the above graph with the appropiate one of
+ // these methods:
+
+ /// This method checks a region of a union object, and returns true if no
+ /// field is initialized within the region.
+ bool isUnionUninit(const TypedValueRegion *R);
+
+ /// This method checks a region of a non-union object, and returns true if
+ /// an uninitialized field is found within the region.
+ bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain);
+
+ /// This method checks a region of a pointer or reference object, and returns
+ /// true if the ptr/ref object itself or any field within the pointee's region
+ /// is uninitialized.
+ bool isPointerOrReferenceUninit(const FieldRegion *FR,
+ FieldChainInfo LocalChain);
+
+ /// This method returns true if the value of a primitive object is
+ /// uninitialized.
+ bool isPrimitiveUninit(const SVal &V);
+
+ // Note that we don't have a method for arrays -- the elements of an array are
+ // often left uninitialized intentionally even when it is of a C++ record
+ // type, so we'll assume that an array is always initialized.
+ // TODO: Add a support for nonloc::LocAsInteger.
+};
+
+} // end of anonymous namespace
+
+// Static variable instantionations.
+
+static llvm::ImmutableListFactory<const FieldRegion *> Factory;
+
+// Utility function declarations.
+
+/// Returns the object that was constructed by CtorDecl, or None if that isn't
+/// possible.
+static Optional<nonloc::LazyCompoundVal>
+getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
+
+/// Checks whether the constructor under checking is called by another
+/// constructor.
+static bool isCalledByConstructor(const CheckerContext &Context);
+
+/// Returns whether FD can be (transitively) dereferenced to a void pointer type
+/// (void*, void**, ...). The type of the region behind a void pointer isn't
+/// known, and thus FD can not be analyzed.
+static bool isVoidPointer(const FieldDecl *FD);
+
+/// Returns true if T is a primitive type. We defined this type so that for
+/// objects that we'd only like analyze as much as checking whether their
+/// value is undefined or not, such as ints and doubles, can be analyzed with
+/// ease. This also helps ensuring that every special field type is handled
+/// correctly.
+static bool isPrimitiveType(const QualType &T) {
+ return T->isBuiltinType() || T->isEnumeralType() || T->isMemberPointerType();
+}
+
+/// Constructs a note message for a given FieldChainInfo object.
+static void printNoteMessage(llvm::raw_ostream &Out,
+ const FieldChainInfo &Chain);
+
+/// Returns with Field's name. This is a helper function to get the correct name
+/// even if Field is a captured lambda variable.
+static StringRef getVariableName(const FieldDecl *Field);
+
+//===----------------------------------------------------------------------===//
+// Methods for UninitializedObjectChecker.
+//===----------------------------------------------------------------------===//
+
+void UninitializedObjectChecker::checkEndFunction(
+ const ReturnStmt *RS, CheckerContext &Context) const {
+
+ const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
+ Context.getLocationContext()->getDecl());
+ if (!CtorDecl)
+ return;
+
+ if (!CtorDecl->isUserProvided())
+ return;
+
+ if (CtorDecl->getParent()->isUnion())
+ return;
+
+ // This avoids essentially the same error being reported multiple times.
+ if (isCalledByConstructor(Context))
+ return;
+
+ Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
+ if (!Object)
+ return;
+
+ FindUninitializedFields F(Context.getState(), Object->getRegion(),
+ IsPedantic);
+
+ const UninitFieldSet &UninitFields = F.getUninitFields();
+
+ if (UninitFields.empty())
+ return;
+
+ // There are uninitialized fields in the record.
+
+ ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState());
+ if (!Node)
+ return;
+
+ PathDiagnosticLocation LocUsedForUniqueing;
+ const Stmt *CallSite = Context.getStackFrame()->getCallSite();
+ if (CallSite)
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ CallSite, Context.getSourceManager(), Node->getLocationContext());
+
+ // For Plist consumers that don't support notes just yet, we'll convert notes
+ // to warnings.
+ if (ShouldConvertNotesToWarnings) {
+ for (const auto &Chain : UninitFields) {
+ SmallString<100> WarningBuf;
+ llvm::raw_svector_ostream WarningOS(WarningBuf);
+
+ printNoteMessage(WarningOS, Chain);
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
+ Node->getLocationContext()->getDecl());
+ Context.emitReport(std::move(Report));
+ }
+ return;
+ }
+
+ SmallString<100> WarningBuf;
+ llvm::raw_svector_ostream WarningOS(WarningBuf);
+ WarningOS << UninitFields.size() << " uninitialized field"
+ << (UninitFields.size() == 1 ? "" : "s")
+ << " at the end of the constructor call";
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
+ Node->getLocationContext()->getDecl());
+
+ for (const auto &Chain : UninitFields) {
+ SmallString<200> NoteBuf;
+ llvm::raw_svector_ostream NoteOS(NoteBuf);
+
+ printNoteMessage(NoteOS, Chain);
+
+ Report->addNote(NoteOS.str(),
+ PathDiagnosticLocation::create(Chain.getEndOfChain(),
+ Context.getSourceManager()));
+ }
+ Context.emitReport(std::move(Report));
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for FindUninitializedFields.
+//===----------------------------------------------------------------------===//
+
+FindUninitializedFields::FindUninitializedFields(
+ ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic)
+ : State(State), ObjectR(R), IsPedantic(IsPedantic) {}
+
+const UninitFieldSet &FindUninitializedFields::getUninitFields() {
+ isNonUnionUninit(ObjectR, FieldChainInfo());
+
+ if (!IsPedantic && !IsAnyFieldInitialized)
+ UninitFields.clear();
+
+ return UninitFields;
+}
+
+bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) {
+ if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
+ Chain.getEndOfChain()->getLocation()))
+ return false;
+
+ return UninitFields.insert(Chain).second;
+}
+
+bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
+ FieldChainInfo LocalChain) {
+ assert(R->getValueType()->isRecordType() &&
+ !R->getValueType()->isUnionType() &&
+ "This method only checks non-union record objects!");
+
+ const RecordDecl *RD =
+ R->getValueType()->getAs<RecordType>()->getDecl()->getDefinition();
+ assert(RD && "Referred record has no definition");
+
+ bool ContainsUninitField = false;
+
+ // Are all of this non-union's fields initialized?
+ for (const FieldDecl *I : RD->fields()) {
+
+ const auto FieldVal =
+ State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
+ const auto *FR = FieldVal.getRegionAs<FieldRegion>();
+ QualType T = I->getType();
+
+ // If LocalChain already contains FR, then we encountered a cyclic
+ // reference. In this case, region FR is already under checking at an
+ // earlier node in the directed tree.
+ if (LocalChain.contains(FR))
+ return false;
+
+ if (T->isStructureOrClassType()) {
+ if (isNonUnionUninit(FR, {LocalChain, FR}))
+ ContainsUninitField = true;
+ continue;
+ }
+
+ if (T->isUnionType()) {
+ if (isUnionUninit(FR)) {
+ if (addFieldToUninits({LocalChain, FR}))
+ ContainsUninitField = true;
+ } else
+ IsAnyFieldInitialized = true;
+ continue;
+ }
+
+ if (T->isArrayType()) {
+ IsAnyFieldInitialized = true;
+ continue;
+ }
+
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (isPointerOrReferenceUninit(FR, LocalChain))
+ ContainsUninitField = true;
+ continue;
+ }
+
+ if (isPrimitiveType(T)) {
+ SVal V = State->getSVal(FieldVal);
+
+ if (isPrimitiveUninit(V)) {
+ if (addFieldToUninits({LocalChain, FR}))
+ ContainsUninitField = true;
+ }
+ continue;
+ }
+
+ llvm_unreachable("All cases are handled!");
+ }
+
+ // Checking bases.
+ // FIXME: As of now, because of `isCalledByConstructor`, objects whose type
+ // is a descendant of another type will emit warnings for uninitalized
+ // inherited members.
+ // This is not the only way to analyze bases of an object -- if we didn't
+ // filter them out, and didn't analyze the bases, this checker would run for
+ // each base of the object in order of base initailization and in theory would
+ // find every uninitalized field. This approach could also make handling
+ // diamond inheritances more easily.
+ //
+ // This rule (that a descendant type's cunstructor is responsible for
+ // initializing inherited data members) is not obvious, and should it should
+ // be.
+ const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
+ if (!CXXRD)
+ return ContainsUninitField;
+
+ for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
+ const auto *BaseRegion = State->getLValue(BaseSpec, R)
+ .castAs<loc::MemRegionVal>()
+ .getRegionAs<TypedValueRegion>();
+
+ if (isNonUnionUninit(BaseRegion, LocalChain))
+ ContainsUninitField = true;
+ }
+
+ return ContainsUninitField;
+}
+
+bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
+ assert(R->getValueType()->isUnionType() &&
+ "This method only checks union objects!");
+ // TODO: Implement support for union fields.
+ return false;
+}
+
+// Note that pointers/references don't contain fields themselves, so in this
+// function we won't add anything to LocalChain.
+bool FindUninitializedFields::isPointerOrReferenceUninit(
+ const FieldRegion *FR, FieldChainInfo LocalChain) {
+
+ assert((FR->getDecl()->getType()->isPointerType() ||
+ FR->getDecl()->getType()->isReferenceType()) &&
+ "This method only checks pointer/reference objects!");
+
+ SVal V = State->getSVal(FR);
+
+ if (V.isUnknown() || V.isZeroConstant()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ if (V.isUndef()) {
+ return addFieldToUninits({LocalChain, FR});
+ }
+
+ const FieldDecl *FD = FR->getDecl();
+
+ // TODO: The dynamic type of a void pointer may be retrieved with
+ // `getDynamicTypeInfo`.
+ if (isVoidPointer(FD)) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ assert(V.getAs<Loc>() && "V should be Loc at this point!");
+
+ // At this point the pointer itself is initialized and points to a valid
+ // location, we'll now check the pointee.
+ SVal DerefdV = State->getSVal(V.castAs<Loc>());
+
+ // TODO: Dereferencing should be done according to the dynamic type.
+ while (Optional<Loc> L = DerefdV.getAs<Loc>()) {
+ DerefdV = State->getSVal(*L);
+ }
+
+ // If V is a pointer pointing to a record type.
+ if (Optional<nonloc::LazyCompoundVal> RecordV =
+ DerefdV.getAs<nonloc::LazyCompoundVal>()) {
+
+ const TypedValueRegion *R = RecordV->getRegion();
+
+ // We can't reason about symbolic regions, assume its initialized.
+ // Note that this also avoids a potential infinite recursion, because
+ // constructors for list-like classes are checked without being called, and
+ // the Static Analyzer will construct a symbolic region for Node *next; or
+ // similar code snippets.
+ if (R->getSymbolicBase()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ const QualType T = R->getValueType();
+
+ if (T->isStructureOrClassType())
+ return isNonUnionUninit(R, {LocalChain, FR});
+
+ if (T->isUnionType()) {
+ if (isUnionUninit(R)) {
+ return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
+ } else {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+ }
+
+ if (T->isArrayType()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ llvm_unreachable("All cases are handled!");
+ }
+
+ // TODO: If possible, it should be asserted that the DerefdV at this point is
+ // primitive.
+
+ if (isPrimitiveUninit(DerefdV))
+ return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
+
+ IsAnyFieldInitialized = true;
+ return false;
+}
+
+bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
+ if (V.isUndef())
+ return true;
+
+ IsAnyFieldInitialized = true;
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for FieldChainInfo.
+//===----------------------------------------------------------------------===//
+
+FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other,
+ const FieldRegion *FR, const bool IsDereferenced)
+ : FieldChainInfo(Other, IsDereferenced) {
+ assert(!contains(FR) && "Can't add a field that is already a part of the "
+ "fieldchain! Is this a cyclic reference?");
+ Chain = Factory.add(FR, Other.Chain);
+}
+
+bool FieldChainInfo::isPointer() const {
+ assert(!Chain.isEmpty() && "Empty fieldchain!");
+ return (*Chain.begin())->getDecl()->getType()->isPointerType();
+}
+
+bool FieldChainInfo::isDereferenced() const {
+ assert(isPointer() && "Only pointers may or may not be dereferenced!");
+ return IsDereferenced;
+}
+
+const FieldDecl *FieldChainInfo::getEndOfChain() const {
+ assert(!Chain.isEmpty() && "Empty fieldchain!");
+ return (*Chain.begin())->getDecl();
+}
+
+// TODO: This function constructs an incorrect fieldchain string in the
+// following case:
+//
+// struct Base { int x; };
+// struct D1 : Base {}; struct D2 : Base {};
+//
+// struct MostDerived : D1, D2 {
+// MostDerived() {}
+// }
+//
+// A call to MostDerived::MostDerived() will cause two notes that say
+// "uninitialized field 'this->x'", but we can't refer to 'x' directly,
+// we need an explicit namespace resolution whether the uninit field was
+// 'D1::x' or 'D2::x'.
+void FieldChainInfo::print(llvm::raw_ostream &Out) const {
+ if (Chain.isEmpty())
+ return;
+
+ const llvm::ImmutableListImpl<const FieldRegion *> *L =
+ Chain.getInternalPointer();
+ printTail(Out, L->getTail());
+ Out << getVariableName(L->getHead()->getDecl());
+}
+
+void FieldChainInfo::printTail(
+ llvm::raw_ostream &Out,
+ const llvm::ImmutableListImpl<const FieldRegion *> *L) {
+ if (!L)
+ return;
+
+ printTail(Out, L->getTail());
+ const FieldDecl *Field = L->getHead()->getDecl();
+ Out << getVariableName(Field);
+ Out << (Field->getType()->isPointerType() ? "->" : ".");
+}
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static bool isVoidPointer(const FieldDecl *FD) {
+ QualType T = FD->getType();
+
+ while (!T.isNull()) {
+ if (T->isVoidPointerType())
+ return true;
+ T = T->getPointeeType();
+ }
+ return false;
+}
+
+static Optional<nonloc::LazyCompoundVal>
+getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) {
+
+ Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(),
+ Context.getStackFrame());
+ // Getting the value for 'this'.
+ SVal This = Context.getState()->getSVal(ThisLoc);
+
+ // Getting the value for '*this'.
+ SVal Object = Context.getState()->getSVal(This.castAs<Loc>());
+
+ return Object.getAs<nonloc::LazyCompoundVal>();
+}
+
+// TODO: We should also check that if the constructor was called by another
+// constructor, whether those two are in any relation to one another. In it's
+// current state, this introduces some false negatives.
+static bool isCalledByConstructor(const CheckerContext &Context) {
+ const LocationContext *LC = Context.getLocationContext()->getParent();
+
+ while (LC) {
+ if (isa<CXXConstructorDecl>(LC->getDecl()))
+ return true;
+
+ LC = LC->getParent();
+ }
+ return false;
+}
+
+static void printNoteMessage(llvm::raw_ostream &Out,
+ const FieldChainInfo &Chain) {
+ if (Chain.isPointer()) {
+ if (Chain.isDereferenced())
+ Out << "uninitialized pointee 'this->";
+ else
+ Out << "uninitialized pointer 'this->";
+ } else
+ Out << "uninitialized field 'this->";
+ Chain.print(Out);
+ Out << "'";
+}
+
+static StringRef getVariableName(const FieldDecl *Field) {
+ // If Field is a captured lambda variable, Field->getName() will return with
+ // an empty string. We can however acquire it's name from the lambda's
+ // captures.
+ const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
+
+ if (CXXParent && CXXParent->isLambda()) {
+ assert(CXXParent->captures_begin());
+ auto It = CXXParent->captures_begin() + Field->getFieldIndex();
+ return It->getCapturedVar()->getName();
+ }
+
+ return Field->getName();
+}
+
+void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
+ auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
+ Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
+ "Pedantic", /*DefaultVal*/ false, Chk);
+ Chk->ShouldConvertNotesToWarnings = Mgr.getAnalyzerOptions().getBooleanOption(
+ "NotesAsWarnings", /*DefaultVal*/ false, Chk);
+}
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 7f9a00ff876d..a6b50dc37740 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -194,7 +194,7 @@ void UnixAPIChecker::CheckOpenVariant(CheckerContext &C,
// Now check if oflags has O_CREAT set.
const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
- const SVal V = state->getSVal(oflagsEx, C.getLocationContext());
+ const SVal V = C.getSVal(oflagsEx);
if (!V.getAs<NonLoc>()) {
// The case where 'V' can be a location can only be due to a bad header,
// so in this case bail out.
@@ -248,8 +248,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
// Check if the first argument is stack allocated. If so, issue a warning
// because that's likely to be bad news.
ProgramStateRef state = C.getState();
- const MemRegion *R =
- state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
+ const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
return;
@@ -336,7 +335,7 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
ProgramStateRef state = C.getState();
ProgramStateRef trueState = nullptr, falseState = nullptr;
const Expr *arg = CE->getArg(sizeArg);
- SVal argVal = state->getSVal(arg, C.getLocationContext());
+ SVal argVal = C.getSVal(arg);
if (argVal.isUnknownOrUndef())
return;
@@ -364,7 +363,7 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
unsigned int i;
for (i = 0; i < nArgs; i++) {
const Expr *arg = CE->getArg(i);
- SVal argVal = state->getSVal(arg, C.getLocationContext());
+ SVal argVal = C.getSVal(arg);
if (argVal.isUnknownOrUndef()) {
if (i == 0)
continue;
diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index 6f21e868b174..dbd12cc9b65a 100644
--- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -132,7 +132,8 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
ci != ce; ++ci) {
if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
- if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable) {
+ if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable ||
+ CE->isBuiltinAssumeFalse(Eng.getContext())) {
foundUnreachable = true;
break;
}
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 40217bdee892..2584f2011819 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -32,19 +32,18 @@ class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
mutable std::unique_ptr<BugType> BT;
enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative };
- void reportBug(VLASize_Kind Kind,
- const Expr *SizeE,
- ProgramStateRef State,
- CheckerContext &C) const;
+ void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+
public:
void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
};
} // end anonymous namespace
-void VLASizeChecker::reportBug(VLASize_Kind Kind,
- const Expr *SizeE,
- ProgramStateRef State,
- CheckerContext &C) const {
+void VLASizeChecker::reportBug(
+ VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
// Generate an error node.
ExplodedNode *N = C.generateErrorNode(State);
if (!N)
@@ -73,6 +72,7 @@ void VLASizeChecker::reportBug(VLASize_Kind Kind,
}
auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ report->addVisitor(std::move(Visitor));
report->addRange(SizeE->getSourceRange());
bugreporter::trackNullOrUndefValue(N, SizeE, *report);
C.emitReport(std::move(report));
@@ -94,7 +94,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
// FIXME: Handle multi-dimensional VLAs.
const Expr *SE = VLA->getSizeExpr();
ProgramStateRef state = C.getState();
- SVal sizeV = state->getSVal(SE, C.getLocationContext());
+ SVal sizeV = C.getSVal(SE);
if (sizeV.isUndef()) {
reportBug(VLA_Garbage, SE, state, C);
@@ -108,7 +108,8 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
// Check if the size is tainted.
if (state->isTainted(sizeV)) {
- reportBug(VLA_Tainted, SE, nullptr, C);
+ reportBug(VLA_Tainted, SE, nullptr, C,
+ llvm::make_unique<TaintBugVisitor>(sizeV));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index 06c4ef71d80b..bd657340fcfb 100644
--- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -56,7 +56,6 @@ public:
private:
const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr,
bool &IsSymbolic, CheckerContext &C) const;
- StringRef getVariableNameFromRegion(const MemRegion *Reg) const;
const ExplodedNode *getStartCallSite(const ExplodedNode *N,
const MemRegion *Reg) const;
@@ -64,13 +63,13 @@ private:
CheckerContext &C) const;
void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
StringRef Msg2, CheckerContext &C, ExplodedNode *N,
- bool ForceReport = false) const;
+ bool ReportUninit = false) const;
void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
bool IsCopy) const;
void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
- class ValistBugVisitor : public BugReporterVisitorImpl<ValistBugVisitor> {
+ class ValistBugVisitor : public BugReporterVisitor {
public:
ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
: Reg(Reg), IsLeak(IsLeak) {}
@@ -79,7 +78,7 @@ private:
ID.AddPointer(&X);
ID.AddPointer(Reg);
}
- std::unique_ptr<PathDiagnosticPiece>
+ std::shared_ptr<PathDiagnosticPiece>
getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
BugReport &BR) override {
if (!IsLeak)
@@ -88,8 +87,7 @@ private:
PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(
EndPathNode, BRC.getSourceManager());
// Do not add the statement itself as a range in case of leak.
- return llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(),
- false);
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false);
}
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
@@ -189,7 +187,7 @@ void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
const Expr *VASubExpr = VAA->getSubExpr();
- SVal VAListSVal = State->getSVal(VASubExpr, C.getLocationContext());
+ SVal VAListSVal = C.getSVal(VASubExpr);
bool Symbolic;
const MemRegion *VAList =
getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
@@ -267,15 +265,19 @@ void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
StringRef Msg1, StringRef Msg2,
CheckerContext &C, ExplodedNode *N,
- bool ForceReport) const {
+ bool ReportUninit) const {
if (!(ChecksEnabled[CK_Unterminated] ||
- (ChecksEnabled[CK_Uninitialized] && ForceReport)))
+ (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
return;
for (auto Reg : LeakedVALists) {
if (!BT_leakedvalist) {
- BT_leakedvalist.reset(new BugType(CheckNames[CK_Unterminated],
- "Leaked va_list",
- categories::MemoryError));
+ // FIXME: maybe creating a new check name for this type of bug is a better
+ // solution.
+ BT_leakedvalist.reset(
+ new BugType(CheckNames[CK_Unterminated].getName().empty()
+ ? CheckNames[CK_Uninitialized]
+ : CheckNames[CK_Unterminated],
+ "Leaked va_list", categories::MemoryError));
BT_leakedvalist->setSuppressOnSink(true);
}
@@ -375,7 +377,7 @@ void ValistChecker::checkVAListEndCall(const CallEvent &Call,
std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode(
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
- BugReport &BR) {
+ BugReport &) {
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = PrevN->getState();
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index c5010f53785a..5b602468cdd4 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -48,7 +48,7 @@ public:
DefaultBool IsPureOnly;
void checkBeginFunction(CheckerContext &C) const;
- void checkEndFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
@@ -57,7 +57,7 @@ private:
void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
CheckerContext &C) const;
- class VirtualBugVisitor : public BugReporterVisitorImpl<VirtualBugVisitor> {
+ class VirtualBugVisitor : public BugReporterVisitor {
private:
const MemRegion *ObjectRegion;
bool Found;
@@ -108,7 +108,7 @@ VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
if (!MD)
return nullptr;
auto ThiSVal =
- State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
if (!Reg)
return nullptr;
@@ -167,7 +167,8 @@ void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
}
// The EndFunction callback when leave a constructor or a destructor.
-void VirtualCallChecker::checkEndFunction(CheckerContext &C) const {
+void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
registerCtorDtorCallInState(false, C);
}
@@ -230,7 +231,7 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
// Enter a constructor, set the corresponding memregion be true.
if (isa<CXXConstructorDecl>(MD)) {
auto ThiSVal =
- State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
const MemRegion *Reg = ThiSVal.getAsRegion();
if (IsBeginFunction)
State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
@@ -244,7 +245,7 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
// Enter a Destructor, set the corresponding memregion be true.
if (isa<CXXDestructorDecl>(MD)) {
auto ThiSVal =
- State->getSVal(SVB.getCXXThis(MD, LCtx->getCurrentStackFrame()));
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
const MemRegion *Reg = ThiSVal.getAsRegion();
if (IsBeginFunction)
State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 1cc08f0d9fe7..dc0d3ec8493a 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -26,9 +26,12 @@ AnalysisManager::AnalysisManager(
// Adding LoopExit elements to the CFG is a requirement for loop
// unrolling.
Options.includeLoopExitInCFG() || Options.shouldUnrollLoops(),
+ Options.includeScopesInCFG(),
Options.shouldSynthesizeBodies(),
Options.shouldConditionalizeStaticInitializers(),
/*addCXXNewAllocator=*/true,
+ Options.includeRichConstructorsInCFG(),
+ Options.shouldElideConstructors(),
injector),
Ctx(ASTCtx), Diags(diags), LangOpts(lang), PathConsumers(PDC),
CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 48e3e22af04a..9b2dc32e0600 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -1,4 +1,4 @@
-//===-- AnalyzerOptions.cpp - Analysis Engine Options -----------*- C++ -*-===//
+//===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -16,8 +16,15 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstddef>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ento;
@@ -55,9 +62,32 @@ AnalyzerOptions::UserModeKind AnalyzerOptions::getUserMode() {
return UserMode;
}
+AnalyzerOptions::ExplorationStrategyKind
+AnalyzerOptions::getExplorationStrategy() {
+ if (ExplorationStrategy == ExplorationStrategyKind::NotSet) {
+ StringRef StratStr =
+ Config
+ .insert(std::make_pair("exploration_strategy", "unexplored_first_queue"))
+ .first->second;
+ ExplorationStrategy =
+ llvm::StringSwitch<ExplorationStrategyKind>(StratStr)
+ .Case("dfs", ExplorationStrategyKind::DFS)
+ .Case("bfs", ExplorationStrategyKind::BFS)
+ .Case("unexplored_first",
+ ExplorationStrategyKind::UnexploredFirst)
+ .Case("unexplored_first_queue",
+ ExplorationStrategyKind::UnexploredFirstQueue)
+ .Case("bfs_block_dfs_contents",
+ ExplorationStrategyKind::BFSBlockDFSContents)
+ .Default(ExplorationStrategyKind::NotSet);
+ assert(ExplorationStrategy != ExplorationStrategyKind::NotSet &&
+ "User mode is invalid.");
+ }
+ return ExplorationStrategy;
+}
+
IPAKind AnalyzerOptions::getIPAMode() {
if (IPAMode == IPAK_NotSet) {
-
// Use the User Mode to set the default IPA value.
// Note, we have to add the string to the Config map for the ConfigDumper
// checker to function properly.
@@ -169,7 +199,7 @@ bool AnalyzerOptions::getBooleanOption(Optional<bool> &V, StringRef Name,
bool AnalyzerOptions::includeTemporaryDtorsInCFG() {
return getBooleanOption(IncludeTemporaryDtorsInCFG,
"cfg-temporary-dtors",
- /* Default = */ false);
+ /* Default = */ true);
}
bool AnalyzerOptions::includeImplicitDtorsInCFG() {
@@ -185,7 +215,19 @@ bool AnalyzerOptions::includeLifetimeInCFG() {
bool AnalyzerOptions::includeLoopExitInCFG() {
return getBooleanOption(IncludeLoopExitInCFG, "cfg-loopexit",
- /* Default = */ false);
+ /* Default = */ false);
+}
+
+bool AnalyzerOptions::includeRichConstructorsInCFG() {
+ return getBooleanOption(IncludeRichConstructorsInCFG,
+ "cfg-rich-constructors",
+ /* Default = */ true);
+}
+
+bool AnalyzerOptions::includeScopesInCFG() {
+ return getBooleanOption(IncludeScopesInCFG,
+ "cfg-scopes",
+ /* Default = */ false);
}
bool AnalyzerOptions::mayInlineCXXStandardLibrary() {
@@ -203,7 +245,7 @@ bool AnalyzerOptions::mayInlineTemplateFunctions() {
bool AnalyzerOptions::mayInlineCXXAllocator() {
return getBooleanOption(InlineCXXAllocator,
"c++-allocator-inlining",
- /*Default=*/false);
+ /*Default=*/true);
}
bool AnalyzerOptions::mayInlineCXXContainerMethods() {
@@ -218,6 +260,11 @@ bool AnalyzerOptions::mayInlineCXXSharedPtrDtor() {
/*Default=*/false);
}
+bool AnalyzerOptions::mayInlineCXXTemporaryDtors() {
+ return getBooleanOption(InlineCXXTemporaryDtors,
+ "c++-temp-dtor-inlining",
+ /*Default=*/true);
+}
bool AnalyzerOptions::mayInlineObjCMethod() {
return getBooleanOption(ObjCInliningMode,
@@ -249,6 +296,12 @@ bool AnalyzerOptions::shouldSuppressFromCXXStandardLibrary() {
/* Default = */ true);
}
+bool AnalyzerOptions::shouldCrosscheckWithZ3() {
+ return getBooleanOption(CrosscheckWithZ3,
+ "crosscheck-with-z3",
+ /* Default = */ false);
+}
+
bool AnalyzerOptions::shouldReportIssuesInMainSourceFile() {
return getBooleanOption(ReportIssuesInMainSourceFile,
"report-in-main-source-file",
@@ -262,6 +315,18 @@ bool AnalyzerOptions::shouldWriteStableReportFilename() {
/* Default = */ false);
}
+bool AnalyzerOptions::shouldSerializeStats() {
+ return getBooleanOption(SerializeStats,
+ "serialize-stats",
+ /* Default = */ false);
+}
+
+bool AnalyzerOptions::shouldElideConstructors() {
+ return getBooleanOption(ElideConstructors,
+ "elide-constructors",
+ /* Default = */ true);
+}
+
int AnalyzerOptions::getOptionAsInteger(StringRef Name, int DefaultVal,
const CheckerBase *C,
bool SearchInParents) {
@@ -299,7 +364,6 @@ unsigned AnalyzerOptions::getAlwaysInlineSize() {
unsigned AnalyzerOptions::getMaxInlinableSize() {
if (!MaxInlinableSize.hasValue()) {
-
int DefaultValue = 0;
UserModeKind HighLevelMode = getUserMode();
switch (HighLevelMode) {
@@ -324,6 +388,12 @@ unsigned AnalyzerOptions::getGraphTrimInterval() {
return GraphTrimInterval.getValue();
}
+unsigned AnalyzerOptions::getMaxSymbolComplexity() {
+ if (!MaxSymbolComplexity.hasValue())
+ MaxSymbolComplexity = getOptionAsInteger("max-symbol-complexity", 35);
+ return MaxSymbolComplexity.getValue();
+}
+
unsigned AnalyzerOptions::getMaxTimesInlineLarge() {
if (!MaxTimesInlineLarge.hasValue())
MaxTimesInlineLarge = getOptionAsInteger("max-times-inline-large", 32);
@@ -392,3 +462,34 @@ bool AnalyzerOptions::shouldDisplayNotesAsEvents() {
getBooleanOption("notes-as-events", /*Default=*/false);
return DisplayNotesAsEvents.getValue();
}
+
+bool AnalyzerOptions::shouldAggressivelySimplifyBinaryOperation() {
+ if (!AggressiveBinaryOperationSimplification.hasValue())
+ AggressiveBinaryOperationSimplification =
+ getBooleanOption("aggressive-binary-operation-simplification",
+ /*Default=*/false);
+ return AggressiveBinaryOperationSimplification.getValue();
+}
+
+StringRef AnalyzerOptions::getCTUDir() {
+ if (!CTUDir.hasValue()) {
+ CTUDir = getOptionAsString("ctu-dir", "");
+ if (!llvm::sys::fs::is_directory(*CTUDir))
+ CTUDir = "";
+ }
+ return CTUDir.getValue();
+}
+
+bool AnalyzerOptions::naiveCTUEnabled() {
+ if (!NaiveCTU.hasValue()) {
+ NaiveCTU = getBooleanOption("experimental-enable-naive-ctu-analysis",
+ /*Default=*/false);
+ }
+ return NaiveCTU.getValue();
+}
+
+StringRef AnalyzerOptions::getCTUIndexName() {
+ if (!CTUIndexName.hasValue())
+ CTUIndexName = getOptionAsString("ctu-index-name", "externalFnMap.txt");
+ return CTUIndexName.getValue();
+}
diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index ec7a7e9e4b1c..db4c1432ccc3 100644
--- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -1,4 +1,4 @@
-//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- C++ -*-//
+//===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,9 +13,18 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/ASTContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableList.h"
+#include "llvm/ADT/STLExtras.h"
+#include <cassert>
+#include <cstdint>
+#include <utility>
using namespace clang;
using namespace ento;
@@ -40,10 +49,11 @@ void PointerToMemberData::Profile(
ID.AddPointer(L.getInternalPointer());
}
-typedef std::pair<SVal, uintptr_t> SValData;
-typedef std::pair<SVal, SVal> SValPair;
+using SValData = std::pair<SVal, uintptr_t>;
+using SValPair = std::pair<SVal, SVal>;
namespace llvm {
+
template<> struct FoldingSetTrait<SValData> {
static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) {
X.first.Profile(ID);
@@ -57,20 +67,21 @@ template<> struct FoldingSetTrait<SValPair> {
X.second.Profile(ID);
}
};
-}
-typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData> >
- PersistentSValsTy;
+} // namespace llvm
-typedef llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair> >
- PersistentSValPairsTy;
+using PersistentSValsTy =
+ llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData>>;
+
+using PersistentSValPairsTy =
+ llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair>>;
BasicValueFactory::~BasicValueFactory() {
// Note that the dstor for the contents of APSIntSet will never be called,
// so we iterate over the set and invoke the dstor for each APSInt. This
// frees an aux. memory allocated to represent very large constants.
- for (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I)
- I->getValue().~APSInt();
+ for (const auto &I : APSIntSet)
+ I.getValue().~APSInt();
delete (PersistentSValsTy*) PersistentSVals;
delete (PersistentSValPairsTy*) PersistentSValPairs;
@@ -79,7 +90,8 @@ BasicValueFactory::~BasicValueFactory() {
const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) {
llvm::FoldingSetNodeID ID;
void *InsertPos;
- typedef llvm::FoldingSetNodeWrapper<llvm::APSInt> FoldNodeTy;
+
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<llvm::APSInt>;
X.Profile(ID);
FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos);
@@ -107,14 +119,12 @@ const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth,
}
const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) {
-
return getValue(getAPSIntType(T).getValue(X));
}
const CompoundValData*
BasicValueFactory::getCompoundValData(QualType T,
llvm::ImmutableList<SVal> Vals) {
-
llvm::FoldingSetNodeID ID;
CompoundValData::Profile(ID, T, Vals);
void *InsertPos;
@@ -150,7 +160,7 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
}
const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
- const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier*> L) {
+ const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
llvm::FoldingSetNodeID ID;
PointerToMemberData::Profile(ID, DD, L);
void *InsertPos;
@@ -167,7 +177,7 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
return D;
}
-const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase(
+const PointerToMemberData *BasicValueFactory::accumCXXBase(
llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
const nonloc::PointerToMember &PTM) {
nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData();
@@ -195,10 +205,9 @@ const clang::ento::PointerToMemberData *BasicValueFactory::accumCXXBase(
const llvm::APSInt*
BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
const llvm::APSInt& V1, const llvm::APSInt& V2) {
-
switch (Op) {
default:
- assert (false && "Invalid Opcode.");
+ assert(false && "Invalid Opcode.");
case BO_Mul:
return &getValue( V1 * V2 );
@@ -220,11 +229,9 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
return &getValue( V1 - V2 );
case BO_Shl: {
-
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
- // FIXME: Expand these checks to include all undefined behavior.
if (V1.isSigned() && V1.isNegative())
return nullptr;
@@ -236,16 +243,16 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
if (Amt >= V1.getBitWidth())
return nullptr;
+ if (V1.isSigned() && Amt > V1.countLeadingZeros())
+ return nullptr;
+
return &getValue( V1.operator<<( (unsigned) Amt ));
}
case BO_Shr: {
-
// FIXME: This logic should probably go higher up, where we can
// test these conditions symbolically.
- // FIXME: Expand these checks to include all undefined behavior.
-
if (V2.isSigned() && V2.isNegative())
return nullptr;
@@ -288,10 +295,8 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
}
}
-
const std::pair<SVal, uintptr_t>&
BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
-
// Lazily create the folding set.
if (!PersistentSVals) PersistentSVals = new PersistentSValsTy();
@@ -302,7 +307,8 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals);
- typedef llvm::FoldingSetNodeWrapper<SValData> FoldNodeTy;
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValData>;
+
FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
if (!P) {
@@ -316,7 +322,6 @@ BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
const std::pair<SVal, SVal>&
BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) {
-
// Lazily create the folding set.
if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy();
@@ -327,7 +332,8 @@ BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) {
PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs);
- typedef llvm::FoldingSetNodeWrapper<SValPair> FoldNodeTy;
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValPair>;
+
FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
if (!P) {
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index dc284888eb03..f990eb6a058d 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -1,4 +1,4 @@
-// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--//
+//===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,28 +13,62 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.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/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <iterator>
#include <memory>
#include <queue>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ento;
@@ -47,7 +81,7 @@ STATISTIC(MaxValidBugClassSize,
"The maximum number of bug reports in the same equivalence class "
"where at least one report is valid (not suppressed)");
-BugReporterVisitor::~BugReporterVisitor() {}
+BugReporterVisitor::~BugReporterVisitor() = default;
void BugReporterContext::anchor() {}
@@ -127,10 +161,9 @@ static void removeRedundantMsgs(PathPieces &path) {
if (i == N-1)
break;
- if (PathDiagnosticEventPiece *nextEvent =
+ if (auto *nextEvent =
dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
- PathDiagnosticEventPiece *event =
- cast<PathDiagnosticEventPiece>(piece.get());
+ auto *event = cast<PathDiagnosticEventPiece>(piece.get());
// Check to see if we should keep one of the two pieces. If we
// come up with a preference, record which piece to keep, and consume
// another piece from the path.
@@ -152,15 +185,16 @@ static void removeRedundantMsgs(PathPieces &path) {
/// A map from PathDiagnosticPiece to the LocationContext of the inlined
/// function call it represents.
-typedef llvm::DenseMap<const PathPieces *, const LocationContext *>
- LocationContextMap;
+using LocationContextMap =
+ llvm::DenseMap<const PathPieces *, const LocationContext *>;
/// Recursively scan through a path and prune out calls and macros pieces
/// that aren't needed. Return true if afterwards the path contains
/// "interesting stuff" which means it shouldn't be pruned from the parent path.
static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
- LocationContextMap &LCM) {
- bool containsSomethingInteresting = false;
+ LocationContextMap &LCM,
+ bool IsInteresting = false) {
+ bool containsSomethingInteresting = IsInteresting;
const unsigned N = pieces.size();
for (unsigned i = 0 ; i < N ; ++i) {
@@ -174,12 +208,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
auto &call = cast<PathDiagnosticCallPiece>(*piece);
// Check if the location context is interesting.
assert(LCM.count(&call.path));
- if (R->isInteresting(LCM[&call.path])) {
- containsSomethingInteresting = true;
- break;
- }
-
- if (!removeUnneededCalls(call.path, R, LCM))
+ if (!removeUnneededCalls(call.path, R, LCM,
+ R->isInteresting(LCM[&call.path])))
continue;
containsSomethingInteresting = true;
@@ -187,7 +217,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
}
case PathDiagnosticPiece::Macro: {
auto &macro = cast<PathDiagnosticMacroPiece>(*piece);
- if (!removeUnneededCalls(macro.subPieces, R, LCM))
+ if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting))
continue;
containsSomethingInteresting = true;
break;
@@ -225,13 +255,11 @@ static bool hasImplicitBody(const Decl *D) {
static void
adjustCallLocations(PathPieces &Pieces,
PathDiagnosticLocation *LastCallLocation = nullptr) {
- for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E; ++I) {
- PathDiagnosticCallPiece *Call = dyn_cast<PathDiagnosticCallPiece>(I->get());
+ for (const auto &I : Pieces) {
+ auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
- if (!Call) {
- assert((*I)->getLocation().asLocation().isValid());
+ if (!Call)
continue;
- }
if (LastCallLocation) {
bool CallerIsImplicit = hasImplicitBody(Call->getCaller());
@@ -314,29 +342,19 @@ static void removePiecesWithInvalidLocations(PathPieces &Pieces) {
//===----------------------------------------------------------------------===//
namespace {
-class NodeMapClosure : public BugReport::NodeResolver {
- InterExplodedGraphMap &M;
-public:
- NodeMapClosure(InterExplodedGraphMap &m) : M(m) {}
-
- const ExplodedNode *getOriginalNode(const ExplodedNode *N) override {
- return M.lookup(N);
- }
-};
class PathDiagnosticBuilder : public BugReporterContext {
BugReport *R;
PathDiagnosticConsumer *PDC;
- NodeMapClosure NMC;
+
public:
const LocationContext *LC;
PathDiagnosticBuilder(GRBugReporter &br,
BugReport *r, InterExplodedGraphMap &Backmap,
PathDiagnosticConsumer *pdc)
- : BugReporterContext(br),
- R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext())
- {}
+ : BugReporterContext(br, Backmap), R(r), PDC(pdc),
+ LC(r->getErrorNode()->getLocationContext()) {}
PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
@@ -353,19 +371,18 @@ public:
return getParentMap().getParent(S);
}
- NodeMapClosure& getNodeResolver() override { return NMC; }
-
PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const {
- return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive;
+ return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal;
}
bool supportsLogicalOpControlFlow() const {
return PDC ? PDC->supportsLogicalOpControlFlow() : true;
}
};
-} // end anonymous namespace
+
+} // namespace
PathDiagnosticLocation
PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
@@ -379,7 +396,6 @@ PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
PathDiagnosticLocation
PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
const ExplodedNode *N) {
-
// Slow, but probably doesn't matter.
if (os.str().empty())
os << ' ';
@@ -433,12 +449,12 @@ static PathDiagnosticLocation
getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P,
const LocationContext *LC, bool allowNestedContexts) {
if (!S)
- return PathDiagnosticLocation();
+ return {};
while (const Stmt *Parent = getEnclosingParent(S, P)) {
switch (Parent->getStmtClass()) {
case Stmt::BinaryOperatorClass: {
- const BinaryOperator *B = cast<BinaryOperator>(Parent);
+ const auto *B = cast<BinaryOperator>(Parent);
if (B->isLogicalOp())
return PathDiagnosticLocation(allowNestedContexts ? B : S, SMgr, LC);
break;
@@ -504,46 +520,21 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
}
//===----------------------------------------------------------------------===//
-// "Visitors only" path diagnostic generation algorithm.
-//===----------------------------------------------------------------------===//
-static bool GenerateVisitorsOnlyPathDiagnostic(
- PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N,
- ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) {
- // All path generation skips the very first node (the error node).
- // This is because there is special handling for the end-of-path note.
- N = N->getFirstPred();
- if (!N)
- return true;
-
- BugReport *R = PDB.getBugReport();
- while (const ExplodedNode *Pred = N->getFirstPred()) {
- for (auto &V : visitors)
- // Visit all the node pairs, but throw the path pieces away.
- V->VisitNode(N, Pred, PDB, *R);
-
- N = Pred;
- }
-
- return R->isValid();
-}
-
-//===----------------------------------------------------------------------===//
// "Minimal" path diagnostic generation algorithm.
//===----------------------------------------------------------------------===//
-typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair;
-typedef SmallVector<StackDiagPair, 6> StackDiagVector;
+using StackDiagPair =
+ std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
+using StackDiagVector = SmallVector<StackDiagPair, 6>;
static void updateStackPiecesWithMessage(PathDiagnosticPiece &P,
StackDiagVector &CallStack) {
// If the piece contains a special message, add it to all the call
// pieces on the active stack.
- if (PathDiagnosticEventPiece *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) {
-
+ if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) {
if (ep->hasCallStackHint())
- for (StackDiagVector::iterator I = CallStack.begin(),
- E = CallStack.end(); I != E; ++I) {
- PathDiagnosticCallPiece *CP = I->first;
- const ExplodedNode *N = I->second;
+ for (const auto &I : CallStack) {
+ PathDiagnosticCallPiece *CP = I.first;
+ const ExplodedNode *N = I.second;
std::string stackMsg = ep->getCallStackMessage(N);
// The last message on the path to final bug is the most important
@@ -557,693 +548,271 @@ static void updateStackPiecesWithMessage(PathDiagnosticPiece &P,
static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM);
-static bool GenerateMinimalPathDiagnostic(
- PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N,
- LocationContextMap &LCM,
- ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) {
-
- SourceManager& SMgr = PDB.getSourceManager();
- const LocationContext *LC = PDB.LC;
- const ExplodedNode *NextNode = N->pred_empty()
- ? nullptr : *(N->pred_begin());
-
- StackDiagVector CallStack;
-
- while (NextNode) {
- N = NextNode;
- PDB.LC = N->getLocationContext();
- NextNode = N->getFirstPred();
-
- ProgramPoint P = N->getLocation();
-
- do {
- if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
- auto C = PathDiagnosticCallPiece::construct(N, *CE, SMgr);
- // Record the mapping from call piece to LocationContext.
- LCM[&C->path] = CE->getCalleeContext();
- auto *P = C.get();
- PD.getActivePath().push_front(std::move(C));
- PD.pushActivePath(&P->path);
- CallStack.push_back(StackDiagPair(P, N));
- break;
- }
-
- if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
- // Flush all locations, and pop the active path.
- bool VisitedEntireCall = PD.isWithinCall();
- PD.popActivePath();
-
- // Either we just added a bunch of stuff to the top-level path, or
- // we have a previous CallExitEnd. If the former, it means that the
- // path terminated within a function call. We must then take the
- // current contents of the active path and place it within
- // a new PathDiagnosticCallPiece.
- PathDiagnosticCallPiece *C;
- if (VisitedEntireCall) {
- C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get());
- } else {
- const Decl *Caller = CE->getLocationContext()->getDecl();
- C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
- // Record the mapping from call piece to LocationContext.
- LCM[&C->path] = CE->getCalleeContext();
- }
-
- C->setCallee(*CE, SMgr);
- if (!CallStack.empty()) {
- assert(CallStack.back().first == C);
- CallStack.pop_back();
- }
- break;
- }
-
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
- const CFGBlock *Src = BE->getSrc();
- const CFGBlock *Dst = BE->getDst();
- const Stmt *T = Src->getTerminator();
-
- if (!T)
- break;
-
- PathDiagnosticLocation Start =
- PathDiagnosticLocation::createBegin(T, SMgr,
- N->getLocationContext());
-
- switch (T->getStmtClass()) {
- default:
- break;
-
- case Stmt::GotoStmtClass:
- case Stmt::IndirectGotoStmtClass: {
- const Stmt *S = PathDiagnosticLocation::getNextStmt(N);
-
- if (!S)
- break;
-
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
- const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
-
- os << "Control jumps to line "
- << End.asLocation().getExpansionLineNumber();
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- break;
- }
-
- case Stmt::SwitchStmtClass: {
- // Figure out what case arm we took.
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
-
- if (const Stmt *S = Dst->getLabel()) {
- PathDiagnosticLocation End(S, SMgr, LC);
-
- switch (S->getStmtClass()) {
- default:
- os << "No cases match in the switch statement. "
- "Control jumps to line "
- << End.asLocation().getExpansionLineNumber();
- break;
- case Stmt::DefaultStmtClass:
- os << "Control jumps to the 'default' case at line "
- << End.asLocation().getExpansionLineNumber();
- break;
-
- case Stmt::CaseStmtClass: {
- os << "Control jumps to 'case ";
- const CaseStmt *Case = cast<CaseStmt>(S);
- const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
-
- // Determine if it is an enum.
- bool GetRawInt = true;
-
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) {
- // FIXME: Maybe this should be an assertion. Are there cases
- // were it is not an EnumConstantDecl?
- const EnumConstantDecl *D =
- dyn_cast<EnumConstantDecl>(DR->getDecl());
-
- if (D) {
- GetRawInt = false;
- os << *D;
- }
- }
-
- if (GetRawInt)
- os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
-
- os << ":' at line "
- << End.asLocation().getExpansionLineNumber();
- break;
- }
- }
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- else {
- os << "'Default' branch taken. ";
- const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
-
- break;
- }
-
- case Stmt::BreakStmtClass:
- case Stmt::ContinueStmtClass: {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- break;
- }
-
- // Determine control-flow for ternary '?'.
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass: {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
- os << "'?' condition is ";
-
- if (*(Src->succ_begin()+1) == Dst)
- os << "false";
- else
- os << "true";
-
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
-
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- break;
- }
-
- // Determine control-flow for short-circuited '&&' and '||'.
- case Stmt::BinaryOperatorClass: {
- if (!PDB.supportsLogicalOpControlFlow())
- break;
- const BinaryOperator *B = cast<BinaryOperator>(T);
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
- os << "Left side of '";
-
- if (B->getOpcode() == BO_LAnd) {
- os << "&&" << "' is ";
-
- if (*(Src->succ_begin()+1) == Dst) {
- os << "false";
- PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
- PathDiagnosticLocation Start =
- PathDiagnosticLocation::createOperatorLoc(B, SMgr);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- else {
- os << "true";
- PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- }
- else {
- assert(B->getOpcode() == BO_LOr);
- os << "||" << "' is ";
-
- if (*(Src->succ_begin()+1) == Dst) {
- os << "false";
- PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- else {
- os << "true";
- PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
- PathDiagnosticLocation Start =
- PathDiagnosticLocation::createOperatorLoc(B, SMgr);
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- }
-
- break;
- }
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
+ const ExplodedNode *N,
+ const CFGBlock *Dst,
+ const SourceManager &SM,
+ const LocationContext *LC,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnosticLocation &Start
+ ) {
+ // Figure out what case arm we took.
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ PathDiagnosticLocation End;
- case Stmt::DoStmtClass: {
- if (*(Src->succ_begin()) == Dst) {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
-
- os << "Loop condition is true. ";
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
-
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- else {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
-
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(
- Start, End, "Loop condition is false. Exiting loop"));
- }
-
- break;
- }
-
- case Stmt::WhileStmtClass:
- case Stmt::ForStmtClass: {
- if (*(Src->succ_begin()+1) == Dst) {
- std::string sbuf;
- llvm::raw_string_ostream os(sbuf);
-
- os << "Loop condition is false. ";
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
- os.str()));
- }
- else {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(
- Start, End, "Loop condition is true. Entering loop body"));
- }
-
- break;
- }
+ if (const Stmt *S = Dst->getLabel()) {
+ End = PathDiagnosticLocation(S, SM, LC);
- case Stmt::IfStmtClass: {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ switch (S->getStmtClass()) {
+ default:
+ os << "No cases match in the switch statement. "
+ "Control jumps to line "
+ << End.asLocation().getExpansionLineNumber();
+ break;
+ case Stmt::DefaultStmtClass:
+ os << "Control jumps to the 'default' case at line "
+ << End.asLocation().getExpansionLineNumber();
+ break;
- if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ case Stmt::CaseStmtClass: {
+ os << "Control jumps to 'case ";
+ const auto *Case = cast<CaseStmt>(S);
+ const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
- if (*(Src->succ_begin()+1) == Dst)
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(
- Start, End, "Taking false branch"));
- else
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(
- Start, End, "Taking true branch"));
+ // Determine if it is an enum.
+ bool GetRawInt = true;
- break;
- }
- }
- }
- } while(0);
-
- if (NextNode) {
- // Add diagnostic pieces from custom visitors.
- BugReport *R = PDB.getBugReport();
- llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet;
- for (auto &V : visitors) {
- if (auto p = V->VisitNode(N, NextNode, PDB, *R)) {
- if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get())
- continue;
+ if (const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
+ // FIXME: Maybe this should be an assertion. Are there cases
+ // were it is not an EnumConstantDecl?
+ const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
- updateStackPiecesWithMessage(*p, CallStack);
- PD.getActivePath().push_front(std::move(p));
+ if (D) {
+ GetRawInt = false;
+ os << *D;
}
}
- }
- }
-
- if (!PDB.getBugReport()->isValid())
- return false;
-
- // After constructing the full PathDiagnostic, do a pass over it to compact
- // PathDiagnosticPieces that occur within a macro.
- CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager());
- return true;
-}
-
-//===----------------------------------------------------------------------===//
-// "Extensive" PathDiagnostic generation.
-//===----------------------------------------------------------------------===//
-
-static bool IsControlFlowExpr(const Stmt *S) {
- const Expr *E = dyn_cast<Expr>(S);
-
- if (!E)
- return false;
-
- E = E->IgnoreParenCasts();
-
- if (isa<AbstractConditionalOperator>(E))
- return true;
-
- if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E))
- if (B->isLogicalOp())
- return true;
- return false;
-}
-
-namespace {
-class ContextLocation : public PathDiagnosticLocation {
- bool IsDead;
-public:
- ContextLocation(const PathDiagnosticLocation &L, bool isdead = false)
- : PathDiagnosticLocation(L), IsDead(isdead) {}
-
- void markDead() { IsDead = true; }
- bool isDead() const { return IsDead; }
-};
-
-static PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L,
- const LocationContext *LC,
- bool firstCharOnly = false) {
- if (const Stmt *S = L.asStmt()) {
- const Stmt *Original = S;
- while (1) {
- // Adjust the location for some expressions that are best referenced
- // by one of their subexpressions.
- switch (S->getStmtClass()) {
- default:
- break;
- case Stmt::ParenExprClass:
- case Stmt::GenericSelectionExprClass:
- S = cast<Expr>(S)->IgnoreParens();
- firstCharOnly = true;
- continue;
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass:
- S = cast<AbstractConditionalOperator>(S)->getCond();
- firstCharOnly = true;
- continue;
- case Stmt::ChooseExprClass:
- S = cast<ChooseExpr>(S)->getCond();
- firstCharOnly = true;
- continue;
- case Stmt::BinaryOperatorClass:
- S = cast<BinaryOperator>(S)->getLHS();
- firstCharOnly = true;
- continue;
- }
+ if (GetRawInt)
+ os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
+ os << ":' at line " << End.asLocation().getExpansionLineNumber();
break;
}
-
- if (S != Original)
- L = PathDiagnosticLocation(S, L.getManager(), LC);
- }
-
- if (firstCharOnly)
- L = PathDiagnosticLocation::createSingleLocation(L);
-
- return L;
-}
-
-class EdgeBuilder {
- std::vector<ContextLocation> CLocs;
- typedef std::vector<ContextLocation>::iterator iterator;
- PathDiagnostic &PD;
- PathDiagnosticBuilder &PDB;
- PathDiagnosticLocation PrevLoc;
-
- bool IsConsumedExpr(const PathDiagnosticLocation &L);
-
- bool containsLocation(const PathDiagnosticLocation &Container,
- const PathDiagnosticLocation &Containee);
-
- PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L);
-
-
-
- void popLocation() {
- if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) {
- // For contexts, we only one the first character as the range.
- rawAddEdge(cleanUpLocation(CLocs.back(), PDB.LC, true));
}
- CLocs.pop_back();
+ } else {
+ os << "'Default' branch taken. ";
+ End = PDB.ExecutionContinues(os, N);
+ }
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str());
+}
+
+
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP(
+ const Stmt *S,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnosticLocation &Start) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
+ os << "Control jumps to line " << End.asLocation().getExpansionLineNumber();
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
+
+}
+
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP(
+ const ExplodedNode *N,
+ const Stmt *T,
+ const CFGBlock *Src,
+ const CFGBlock *Dst,
+ const SourceManager &SM,
+ PathDiagnosticBuilder &PDB,
+ const LocationContext *LC) {
+ const auto *B = cast<BinaryOperator>(T);
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "Left side of '";
+ PathDiagnosticLocation Start, End;
+
+ if (B->getOpcode() == BO_LAnd) {
+ os << "&&"
+ << "' is ";
+
+ if (*(Src->succ_begin() + 1) == Dst) {
+ os << "false";
+ End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SM);
+ } else {
+ os << "true";
+ Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PDB.ExecutionContinues(N);
+ }
+ } else {
+ assert(B->getOpcode() == BO_LOr);
+ os << "||"
+ << "' is ";
+
+ if (*(Src->succ_begin() + 1) == Dst) {
+ os << "false";
+ Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PDB.ExecutionContinues(N);
+ } else {
+ os << "true";
+ End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SM);
+ }
}
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str());
+}
-public:
- EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb)
- : PD(pd), PDB(pdb) {
-
- // If the PathDiagnostic already has pieces, add the enclosing statement
- // of the first piece as a context as well.
- if (!PD.path.empty()) {
- PrevLoc = (*PD.path.begin())->getLocation();
-
- if (const Stmt *S = PrevLoc.asStmt())
- addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
- }
- }
+void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
+ const SourceManager &SM,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnostic &PD) {
+ const LocationContext *LC = N->getLocationContext();
+ const CFGBlock *Src = BE.getSrc();
+ const CFGBlock *Dst = BE.getDst();
+ const Stmt *T = Src->getTerminator();
+ if (!T)
+ return;
- ~EdgeBuilder() {
- while (!CLocs.empty()) popLocation();
+ auto Start = PathDiagnosticLocation::createBegin(T, SM, LC);
+ switch (T->getStmtClass()) {
+ default:
+ break;
- // Finally, add an initial edge from the start location of the first
- // statement (if it doesn't already exist).
- PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
- PDB.LC,
- PDB.getSourceManager());
- if (L.isValid())
- rawAddEdge(L);
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass: {
+ if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N))
+ PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start));
+ break;
}
- void flushLocations() {
- while (!CLocs.empty())
- popLocation();
- PrevLoc = PathDiagnosticLocation();
+ case Stmt::SwitchStmtClass: {
+ PD.getActivePath().push_front(
+ generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start));
+ break;
}
- void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false,
- bool IsPostJump = false);
-
- void rawAddEdge(PathDiagnosticLocation NewLoc);
-
- void addContext(const Stmt *S);
- void addContext(const PathDiagnosticLocation &L);
- void addExtendedContext(const Stmt *S);
-};
-} // end anonymous namespace
-
-
-PathDiagnosticLocation
-EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) {
- if (const Stmt *S = L.asStmt()) {
- if (IsControlFlowExpr(S))
- return L;
-
- return PDB.getEnclosingStmtLocation(S);
+ case Stmt::BreakStmtClass:
+ case Stmt::ContinueStmtClass: {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
+ break;
}
- return L;
-}
-
-bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
- const PathDiagnosticLocation &Containee) {
-
- if (Container == Containee)
- return true;
+ // Determine control-flow for ternary '?'.
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "'?' condition is ";
- if (Container.asDecl())
- return true;
+ if (*(Src->succ_begin() + 1) == Dst)
+ os << "false";
+ else
+ os << "true";
- if (const Stmt *S = Containee.asStmt())
- if (const Stmt *ContainerS = Container.asStmt()) {
- while (S) {
- if (S == ContainerS)
- return true;
- S = PDB.getParent(S);
- }
- return false;
- }
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- // Less accurate: compare using source ranges.
- SourceRange ContainerR = Container.asRange();
- SourceRange ContaineeR = Containee.asRange();
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
- SourceManager &SM = PDB.getSourceManager();
- SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin());
- SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd());
- SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin());
- SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd());
-
- unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg);
- unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd);
- unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg);
- unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd);
-
- assert(ContainerBegLine <= ContainerEndLine);
- assert(ContaineeBegLine <= ContaineeEndLine);
-
- return (ContainerBegLine <= ContaineeBegLine &&
- ContainerEndLine >= ContaineeEndLine &&
- (ContainerBegLine != ContaineeBegLine ||
- SM.getExpansionColumnNumber(ContainerRBeg) <=
- SM.getExpansionColumnNumber(ContaineeRBeg)) &&
- (ContainerEndLine != ContaineeEndLine ||
- SM.getExpansionColumnNumber(ContainerREnd) >=
- SM.getExpansionColumnNumber(ContaineeREnd)));
-}
-
-void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
- if (!PrevLoc.isValid()) {
- PrevLoc = NewLoc;
- return;
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
+ break;
}
- const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc, PDB.LC);
- const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc, PDB.LC);
+ // Determine control-flow for short-circuited '&&' and '||'.
+ case Stmt::BinaryOperatorClass: {
+ if (!PDB.supportsLogicalOpControlFlow())
+ break;
- if (PrevLocClean.asLocation().isInvalid()) {
- PrevLoc = NewLoc;
- return;
+ std::shared_ptr<PathDiagnosticControlFlowPiece> Diag =
+ generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC);
+ PD.getActivePath().push_front(Diag);
+ break;
}
- if (NewLocClean.asLocation() == PrevLocClean.asLocation())
- return;
-
- // FIXME: Ignore intra-macro edges for now.
- if (NewLocClean.asLocation().getExpansionLoc() ==
- PrevLocClean.asLocation().getExpansionLoc())
- return;
-
- PD.getActivePath().push_front(
- std::make_shared<PathDiagnosticControlFlowPiece>(NewLocClean,
- PrevLocClean));
- PrevLoc = NewLoc;
-}
-
-void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd,
- bool IsPostJump) {
-
- if (!alwaysAdd && NewLoc.asLocation().isMacroID())
- return;
+ case Stmt::DoStmtClass:
+ if (*(Src->succ_begin()) == Dst) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
- const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc);
+ os << "Loop condition is true. ";
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
- while (!CLocs.empty()) {
- ContextLocation &TopContextLoc = CLocs.back();
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
- // Is the top location context the same as the one for the new location?
- if (TopContextLoc == CLoc) {
- if (alwaysAdd) {
- if (IsConsumedExpr(TopContextLoc))
- TopContextLoc.markDead();
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str()));
+ } else {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- rawAddEdge(NewLoc);
- }
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
- if (IsPostJump)
- TopContextLoc.markDead();
- return;
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Loop condition is false. Exiting loop"));
}
+ break;
- if (containsLocation(TopContextLoc, CLoc)) {
- if (alwaysAdd) {
- rawAddEdge(NewLoc);
-
- if (IsConsumedExpr(CLoc)) {
- CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/true));
- return;
- }
- }
+ case Stmt::WhileStmtClass:
+ case Stmt::ForStmtClass:
+ if (*(Src->succ_begin() + 1) == Dst) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "Loop condition is false. ";
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str()));
+ } else {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
- CLocs.push_back(ContextLocation(CLoc, /*IsDead=*/IsPostJump));
- return;
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Loop condition is true. Entering loop body"));
}
- // Context does not contain the location. Flush it.
- popLocation();
- }
-
- // If we reach here, there is no enclosing context. Just add the edge.
- rawAddEdge(NewLoc);
-}
+ break;
-bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) {
- if (const Expr *X = dyn_cast_or_null<Expr>(L.asStmt()))
- return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X);
+ case Stmt::IfStmtClass: {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
- return false;
-}
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
-void EdgeBuilder::addExtendedContext(const Stmt *S) {
- if (!S)
- return;
-
- const Stmt *Parent = PDB.getParent(S);
- while (Parent) {
- if (isa<CompoundStmt>(Parent))
- Parent = PDB.getParent(Parent);
+ if (*(Src->succ_begin() + 1) == Dst)
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Taking false branch"));
else
- break;
- }
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Taking true branch"));
- if (Parent) {
- switch (Parent->getStmtClass()) {
- case Stmt::DoStmtClass:
- case Stmt::ObjCAtSynchronizedStmtClass:
- addContext(Parent);
- default:
- break;
- }
+ break;
}
-
- addContext(S);
-}
-
-void EdgeBuilder::addContext(const Stmt *S) {
- if (!S)
- return;
-
- PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC);
- addContext(L);
-}
-
-void EdgeBuilder::addContext(const PathDiagnosticLocation &L) {
- while (!CLocs.empty()) {
- const PathDiagnosticLocation &TopContextLoc = CLocs.back();
-
- // Is the top location context the same as the one for the new location?
- if (TopContextLoc == L)
- return;
-
- if (containsLocation(TopContextLoc, L)) {
- CLocs.push_back(L);
- return;
- }
-
- // Context does not contain the location. Flush it.
- popLocation();
}
-
- CLocs.push_back(L);
}
// Cone-of-influence: support the reverse propagation of "interesting" symbols
@@ -1257,7 +826,7 @@ void EdgeBuilder::addContext(const PathDiagnosticLocation &L) {
// because the constraint solver sometimes simplifies certain symbolic values
// into constants when appropriate, and this complicates reasoning about
// interesting values.
-typedef llvm::DenseSet<const Expr *> InterestingExprs;
+using InterestingExprs = llvm::DenseSet<const Expr *>;
static void reversePropagateIntererstingSymbols(BugReport &R,
InterestingExprs &IE,
@@ -1276,7 +845,7 @@ static void reversePropagateIntererstingSymbols(BugReport &R,
case Stmt::BinaryOperatorClass:
case Stmt::UnaryOperatorClass: {
for (const Stmt *SubStmt : Ex->children()) {
- if (const Expr *child = dyn_cast_or_null<Expr>(SubStmt)) {
+ if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) {
IE.insert(child);
SVal ChildV = State->getSVal(child, LCtx);
R.markInteresting(ChildV);
@@ -1296,10 +865,10 @@ static void reversePropagateInterestingSymbols(BugReport &R,
const LocationContext *CallerCtx)
{
// FIXME: Handle non-CallExpr-based CallEvents.
- const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame();
+ const StackFrameContext *Callee = CalleeCtx->getStackFrame();
const Stmt *CallSite = Callee->getCallSite();
- if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
+ if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
FunctionDecl::param_const_iterator PI = FD->param_begin(),
PE = FD->param_end();
CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
@@ -1339,16 +908,6 @@ static bool isJumpToFalseBranch(const BlockEdge *BE) {
return (*(Src->succ_begin()+1) == BE->getDst());
}
-/// Return true if the terminator is a loop and the destination is the
-/// false branch.
-static bool isLoopJumpPastBody(const Stmt *Term, const BlockEdge *BE) {
- if (!isLoop(Term))
- return false;
-
- // Did we take the false branch?
- return isJumpToFalseBranch(BE);
-}
-
static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) {
while (SubS) {
if (SubS == S)
@@ -1376,7 +935,7 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
const Stmt *LoopBody = nullptr;
switch (Term->getStmtClass()) {
case Stmt::CXXForRangeStmtClass: {
- const CXXForRangeStmt *FR = cast<CXXForRangeStmt>(Term);
+ const auto *FR = cast<CXXForRangeStmt>(Term);
if (isContainedByStmt(PM, FR->getInc(), S))
return true;
if (isContainedByStmt(PM, FR->getLoopVarStmt(), S))
@@ -1385,14 +944,14 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
break;
}
case Stmt::ForStmtClass: {
- const ForStmt *FS = cast<ForStmt>(Term);
+ const auto *FS = cast<ForStmt>(Term);
if (isContainedByStmt(PM, FS->getInc(), S))
return true;
LoopBody = FS->getBody();
break;
}
case Stmt::ObjCForCollectionStmtClass: {
- const ObjCForCollectionStmt *FC = cast<ObjCForCollectionStmt>(Term);
+ const auto *FC = cast<ObjCForCollectionStmt>(Term);
LoopBody = FC->getBody();
break;
}
@@ -1405,210 +964,7 @@ static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
return isContainedByStmt(PM, LoopBody, S);
}
-//===----------------------------------------------------------------------===//
-// Top-level logic for generating extensive path diagnostics.
-//===----------------------------------------------------------------------===//
-
-static bool GenerateExtensivePathDiagnostic(
- PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N,
- LocationContextMap &LCM,
- ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) {
- EdgeBuilder EB(PD, PDB);
- const SourceManager& SM = PDB.getSourceManager();
- StackDiagVector CallStack;
- InterestingExprs IE;
-
- const ExplodedNode *NextNode = N->pred_empty() ? nullptr : *(N->pred_begin());
- while (NextNode) {
- N = NextNode;
- NextNode = N->getFirstPred();
- ProgramPoint P = N->getLocation();
-
- do {
- if (Optional<PostStmt> PS = P.getAs<PostStmt>()) {
- if (const Expr *Ex = PS->getStmtAs<Expr>())
- reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(), Ex,
- N->getLocationContext());
- }
-
- if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
- const Stmt *S = CE->getCalleeContext()->getCallSite();
- if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
- reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(), Ex,
- N->getLocationContext());
- }
-
- auto C = PathDiagnosticCallPiece::construct(N, *CE, SM);
- LCM[&C->path] = CE->getCalleeContext();
-
- EB.addEdge(C->callReturn, /*AlwaysAdd=*/true, /*IsPostJump=*/true);
- EB.flushLocations();
-
- auto *P = C.get();
- PD.getActivePath().push_front(std::move(C));
- PD.pushActivePath(&P->path);
- CallStack.push_back(StackDiagPair(P, N));
- break;
- }
-
- // Pop the call hierarchy if we are done walking the contents
- // of a function call.
- if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
- // Add an edge to the start of the function.
- const Decl *D = CE->getCalleeContext()->getDecl();
- PathDiagnosticLocation pos =
- PathDiagnosticLocation::createBegin(D, SM);
- EB.addEdge(pos);
-
- // Flush all locations, and pop the active path.
- bool VisitedEntireCall = PD.isWithinCall();
- EB.flushLocations();
- PD.popActivePath();
- PDB.LC = N->getLocationContext();
-
- // Either we just added a bunch of stuff to the top-level path, or
- // we have a previous CallExitEnd. If the former, it means that the
- // path terminated within a function call. We must then take the
- // current contents of the active path and place it within
- // a new PathDiagnosticCallPiece.
- PathDiagnosticCallPiece *C;
- if (VisitedEntireCall) {
- C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get());
- } else {
- const Decl *Caller = CE->getLocationContext()->getDecl();
- C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
- LCM[&C->path] = CE->getCalleeContext();
- }
-
- C->setCallee(*CE, SM);
- EB.addContext(C->getLocation());
-
- if (!CallStack.empty()) {
- assert(CallStack.back().first == C);
- CallStack.pop_back();
- }
- break;
- }
-
- // Note that is important that we update the LocationContext
- // after looking at CallExits. CallExit basically adds an
- // edge in the *caller*, so we don't want to update the LocationContext
- // too soon.
- PDB.LC = N->getLocationContext();
-
- // Block edges.
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
- // Does this represent entering a call? If so, look at propagating
- // interesting symbols across call boundaries.
- if (NextNode) {
- const LocationContext *CallerCtx = NextNode->getLocationContext();
- const LocationContext *CalleeCtx = PDB.LC;
- if (CallerCtx != CalleeCtx) {
- reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(),
- CalleeCtx, CallerCtx);
- }
- }
-
- // Are we jumping to the head of a loop? Add a special diagnostic.
- if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
- PathDiagnosticLocation L(Loop, SM, PDB.LC);
- const CompoundStmt *CS = nullptr;
-
- if (const ForStmt *FS = dyn_cast<ForStmt>(Loop))
- CS = dyn_cast<CompoundStmt>(FS->getBody());
- else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop))
- CS = dyn_cast<CompoundStmt>(WS->getBody());
-
- auto p = std::make_shared<PathDiagnosticEventPiece>(
- L, "Looping back to the head of the loop");
- p->setPrunable(true);
-
- EB.addEdge(p->getLocation(), true);
- PD.getActivePath().push_front(std::move(p));
-
- if (CS) {
- PathDiagnosticLocation BL =
- PathDiagnosticLocation::createEndBrace(CS, SM);
- EB.addEdge(BL);
- }
- }
-
- const CFGBlock *BSrc = BE->getSrc();
- ParentMap &PM = PDB.getParentMap();
-
- if (const Stmt *Term = BSrc->getTerminator()) {
- // Are we jumping past the loop body without ever executing the
- // loop (because the condition was false)?
- if (isLoopJumpPastBody(Term, &*BE) &&
- !isInLoopBody(PM,
- getStmtBeforeCond(PM,
- BSrc->getTerminatorCondition(),
- N),
- Term)) {
- PathDiagnosticLocation L(Term, SM, PDB.LC);
- auto PE = std::make_shared<PathDiagnosticEventPiece>(
- L, "Loop body executed 0 times");
- PE->setPrunable(true);
-
- EB.addEdge(PE->getLocation(), true);
- PD.getActivePath().push_front(std::move(PE));
- }
-
- // In any case, add the terminator as the current statement
- // context for control edges.
- EB.addContext(Term);
- }
-
- break;
- }
-
- if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
- Optional<CFGElement> First = BE->getFirstElement();
- if (Optional<CFGStmt> S = First ? First->getAs<CFGStmt>() : None) {
- const Stmt *stmt = S->getStmt();
- if (IsControlFlowExpr(stmt)) {
- // Add the proper context for '&&', '||', and '?'.
- EB.addContext(stmt);
- }
- else
- EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt());
- }
-
- break;
- }
-
-
- } while (0);
-
- if (!NextNode)
- continue;
-
- // Add pieces from custom visitors.
- BugReport *R = PDB.getBugReport();
- llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet;
- for (auto &V : visitors) {
- if (auto p = V->VisitNode(N, NextNode, PDB, *R)) {
- if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get())
- continue;
-
- const PathDiagnosticLocation &Loc = p->getLocation();
- EB.addEdge(Loc, true);
- updateStackPiecesWithMessage(*p, CallStack);
- PD.getActivePath().push_front(std::move(p));
-
- if (const Stmt *S = Loc.asStmt())
- EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
- }
- }
- }
-
- return PDB.getBugReport()->isValid();
-}
-
-/// \brief Adds a sanitized control-flow diagnostic edge to a path.
+/// Adds a sanitized control-flow diagnostic edge to a path.
static void addEdgeToPath(PathPieces &path,
PathDiagnosticLocation &PrevLoc,
PathDiagnosticLocation NewLoc,
@@ -1639,8 +995,7 @@ static void addEdgeToPath(PathPieces &path,
/// which returns the element for ObjCForCollectionStmts.
static const Stmt *getTerminatorCondition(const CFGBlock *B) {
const Stmt *S = B->getTerminatorCondition();
- if (const ObjCForCollectionStmt *FS =
- dyn_cast_or_null<ObjCForCollectionStmt>(S))
+ if (const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
return FS->getElement();
return S;
}
@@ -1652,269 +1007,256 @@ static const char StrLoopRangeEmpty[] =
static const char StrLoopCollectionEmpty[] =
"Loop body skipped when collection is empty";
-static bool GenerateAlternateExtensivePathDiagnostic(
- PathDiagnostic &PD, PathDiagnosticBuilder &PDB, const ExplodedNode *N,
- LocationContextMap &LCM,
- ArrayRef<std::unique_ptr<BugReporterVisitor>> visitors) {
-
- BugReport *report = PDB.getBugReport();
+static std::unique_ptr<FilesToLineNumsMap>
+findExecutedLines(SourceManager &SM, const ExplodedNode *N);
+
+/// Generate diagnostics for the node \p N,
+/// and write it into \p PD.
+/// \p AddPathEdges Whether diagnostic consumer can generate path arrows
+/// showing both row and column.
+static void generatePathDiagnosticsForNode(const ExplodedNode *N,
+ PathDiagnostic &PD,
+ PathDiagnosticLocation &PrevLoc,
+ PathDiagnosticBuilder &PDB,
+ LocationContextMap &LCM,
+ StackDiagVector &CallStack,
+ InterestingExprs &IE,
+ bool AddPathEdges) {
+ ProgramPoint P = N->getLocation();
const SourceManager& SM = PDB.getSourceManager();
- StackDiagVector CallStack;
- InterestingExprs IE;
- PathDiagnosticLocation PrevLoc = PD.getLocation();
+ // Have we encountered an entrance to a call? It may be
+ // the case that we have not encountered a matching
+ // call exit before this point. This means that the path
+ // terminated within the call itself.
+ if (auto CE = P.getAs<CallEnter>()) {
+
+ if (AddPathEdges) {
+ // Add an edge to the start of the function.
+ const StackFrameContext *CalleeLC = CE->getCalleeContext();
+ const Decl *D = CalleeLC->getDecl();
+ // Add the edge only when the callee has body. We jump to the beginning
+ // of the *declaration*, however we expect it to be followed by the
+ // body. This isn't the case for autosynthesized property accessors in
+ // Objective-C. No need for a similar extra check for CallExit points
+ // because the exit edge comes from a statement (i.e. return),
+ // not from declaration.
+ if (D->hasBody())
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createBegin(D, SM), CalleeLC);
+ }
- const ExplodedNode *NextNode = N->getFirstPred();
- while (NextNode) {
- N = NextNode;
- NextNode = N->getFirstPred();
- ProgramPoint P = N->getLocation();
-
- do {
- // Have we encountered an entrance to a call? It may be
- // the case that we have not encountered a matching
- // call exit before this point. This means that the path
- // terminated within the call itself.
- if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
- // Add an edge to the start of the function.
- const StackFrameContext *CalleeLC = CE->getCalleeContext();
- const Decl *D = CalleeLC->getDecl();
- // Add the edge only when the callee has body. We jump to the beginning
- // of the *declaration*, however we expect it to be followed by the
- // body. This isn't the case for autosynthesized property accessors in
- // Objective-C. No need for a similar extra check for CallExit points
- // because the exit edge comes from a statement (i.e. return),
- // not from declaration.
- if (D->hasBody())
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PathDiagnosticLocation::createBegin(D, SM), CalleeLC);
+ // Did we visit an entire call?
+ bool VisitedEntireCall = PD.isWithinCall();
+ PD.popActivePath();
- // Did we visit an entire call?
- bool VisitedEntireCall = PD.isWithinCall();
- PD.popActivePath();
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get());
+ } else {
+ const Decl *Caller = CE->getLocationContext()->getDecl();
+ C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
+
+ if (AddPathEdges) {
+ // Since we just transferred the path over to the call piece,
+ // reset the mapping from active to location context.
+ assert(PD.getActivePath().size() == 1 &&
+ PD.getActivePath().front().get() == C);
+ LCM[&PD.getActivePath()] = nullptr;
+ }
- PathDiagnosticCallPiece *C;
- if (VisitedEntireCall) {
- PathDiagnosticPiece *P = PD.getActivePath().front().get();
- C = cast<PathDiagnosticCallPiece>(P);
- } else {
- const Decl *Caller = CE->getLocationContext()->getDecl();
- C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
-
- // Since we just transferred the path over to the call piece,
- // reset the mapping from active to location context.
- assert(PD.getActivePath().size() == 1 &&
- PD.getActivePath().front().get() == C);
- LCM[&PD.getActivePath()] = nullptr;
-
- // Record the location context mapping for the path within
- // the call.
- assert(LCM[&C->path] == nullptr ||
- LCM[&C->path] == CE->getCalleeContext());
- LCM[&C->path] = CE->getCalleeContext();
-
- // If this is the first item in the active path, record
- // the new mapping from active path to location context.
- const LocationContext *&NewLC = LCM[&PD.getActivePath()];
- if (!NewLC)
- NewLC = N->getLocationContext();
-
- PDB.LC = NewLC;
- }
- C->setCallee(*CE, SM);
+ // Record the location context mapping for the path within
+ // the call.
+ assert(LCM[&C->path] == nullptr ||
+ LCM[&C->path] == CE->getCalleeContext());
+ LCM[&C->path] = CE->getCalleeContext();
- // Update the previous location in the active path.
- PrevLoc = C->getLocation();
+ // If this is the first item in the active path, record
+ // the new mapping from active path to location context.
+ const LocationContext *&NewLC = LCM[&PD.getActivePath()];
+ if (!NewLC)
+ NewLC = N->getLocationContext();
- if (!CallStack.empty()) {
- assert(CallStack.back().first == C);
- CallStack.pop_back();
- }
- break;
- }
+ PDB.LC = NewLC;
+ }
+ C->setCallee(*CE, SM);
- // Query the location context here and the previous location
- // as processing CallEnter may change the active path.
- PDB.LC = N->getLocationContext();
+ // Update the previous location in the active path.
+ PrevLoc = C->getLocation();
- // Record the mapping from the active path to the location
- // context.
- assert(!LCM[&PD.getActivePath()] ||
- LCM[&PD.getActivePath()] == PDB.LC);
- LCM[&PD.getActivePath()] = PDB.LC;
-
- // Have we encountered an exit from a function call?
- if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
- const Stmt *S = CE->getCalleeContext()->getCallSite();
- // Propagate the interesting symbols accordingly.
- if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
- reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(), Ex,
- N->getLocationContext());
- }
+ if (!CallStack.empty()) {
+ assert(CallStack.back().first == C);
+ CallStack.pop_back();
+ }
+ return;
+ }
- // We are descending into a call (backwards). Construct
- // a new call piece to contain the path pieces for that call.
- auto C = PathDiagnosticCallPiece::construct(N, *CE, SM);
- // Record the location context for this call piece.
- LCM[&C->path] = CE->getCalleeContext();
+ if (AddPathEdges) {
+ // Query the location context here and the previous location
+ // as processing CallEnter may change the active path.
+ PDB.LC = N->getLocationContext();
- // Add the edge to the return site.
- addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn, PDB.LC);
- auto *P = C.get();
- PD.getActivePath().push_front(std::move(C));
- PrevLoc.invalidate();
+ // Record the mapping from the active path to the location
+ // context.
+ assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC);
+ LCM[&PD.getActivePath()] = PDB.LC;
+ }
- // Make the contents of the call the active path for now.
- PD.pushActivePath(&P->path);
- CallStack.push_back(StackDiagPair(P, N));
- break;
- }
+ // Have we encountered an exit from a function call?
+ if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
- if (Optional<PostStmt> PS = P.getAs<PostStmt>()) {
- // For expressions, make sure we propagate the
- // interesting symbols correctly.
- if (const Expr *Ex = PS->getStmtAs<Expr>())
- reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(), Ex,
- N->getLocationContext());
-
- // Add an edge. If this is an ObjCForCollectionStmt do
- // not add an edge here as it appears in the CFG both
- // as a terminator and as a terminator condition.
- if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
- PathDiagnosticLocation L =
- PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC);
- addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC);
- }
- break;
+ // We are descending into a call (backwards). Construct
+ // a new call piece to contain the path pieces for that call.
+ auto C = PathDiagnosticCallPiece::construct(N, *CE, SM);
+ // Record the mapping from call piece to LocationContext.
+ LCM[&C->path] = CE->getCalleeContext();
+
+ if (AddPathEdges) {
+ const Stmt *S = CE->getCalleeContext()->getCallSite();
+ // Propagate the interesting symbols accordingly.
+ if (const auto *Ex = dyn_cast_or_null<Expr>(S)) {
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(), Ex,
+ N->getLocationContext());
}
+ // Add the edge to the return site.
+ addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn, PDB.LC);
+ PrevLoc.invalidate();
+ }
- // Block edges.
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
- // Does this represent entering a call? If so, look at propagating
- // interesting symbols across call boundaries.
- if (NextNode) {
- const LocationContext *CallerCtx = NextNode->getLocationContext();
- const LocationContext *CalleeCtx = PDB.LC;
- if (CallerCtx != CalleeCtx) {
- reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
- N->getState().get(),
- CalleeCtx, CallerCtx);
- }
- }
+ auto *P = C.get();
+ PD.getActivePath().push_front(std::move(C));
- // Are we jumping to the head of a loop? Add a special diagnostic.
- if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
- PathDiagnosticLocation L(Loop, SM, PDB.LC);
- const Stmt *Body = nullptr;
-
- if (const ForStmt *FS = dyn_cast<ForStmt>(Loop))
- Body = FS->getBody();
- else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop))
- Body = WS->getBody();
- else if (const ObjCForCollectionStmt *OFS =
- dyn_cast<ObjCForCollectionStmt>(Loop)) {
- Body = OFS->getBody();
- } else if (const CXXForRangeStmt *FRS =
- dyn_cast<CXXForRangeStmt>(Loop)) {
- Body = FRS->getBody();
- }
- // do-while statements are explicitly excluded here
+ // Make the contents of the call the active path for now.
+ PD.pushActivePath(&P->path);
+ CallStack.push_back(StackDiagPair(P, N));
+ return;
+ }
- auto p = std::make_shared<PathDiagnosticEventPiece>(
- L, "Looping back to the head "
- "of the loop");
- p->setPrunable(true);
+ if (auto PS = P.getAs<PostStmt>()) {
+ if (!AddPathEdges)
+ return;
- addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC);
- PD.getActivePath().push_front(std::move(p));
+ // For expressions, make sure we propagate the
+ // interesting symbols correctly.
+ if (const Expr *Ex = PS->getStmtAs<Expr>())
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(), Ex,
+ N->getLocationContext());
+
+ // Add an edge. If this is an ObjCForCollectionStmt do
+ // not add an edge here as it appears in the CFG both
+ // as a terminator and as a terminator condition.
+ if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC);
+ addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC);
+ }
- if (const CompoundStmt *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PathDiagnosticLocation::createEndBrace(CS, SM),
- PDB.LC);
- }
- }
+ } else if (auto BE = P.getAs<BlockEdge>()) {
- const CFGBlock *BSrc = BE->getSrc();
- ParentMap &PM = PDB.getParentMap();
-
- if (const Stmt *Term = BSrc->getTerminator()) {
- // Are we jumping past the loop body without ever executing the
- // loop (because the condition was false)?
- if (isLoop(Term)) {
- const Stmt *TermCond = getTerminatorCondition(BSrc);
- bool IsInLoopBody =
- isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term);
-
- const char *str = nullptr;
-
- if (isJumpToFalseBranch(&*BE)) {
- if (!IsInLoopBody) {
- if (isa<ObjCForCollectionStmt>(Term)) {
- str = StrLoopCollectionEmpty;
- } else if (isa<CXXForRangeStmt>(Term)) {
- str = StrLoopRangeEmpty;
- } else {
- str = StrLoopBodyZero;
- }
- }
- } else {
- str = StrEnteringLoop;
- }
+ if (!AddPathEdges) {
+ generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD);
+ return;
+ }
- if (str) {
- PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC);
- auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
- PE->setPrunable(true);
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PE->getLocation(), PDB.LC);
- PD.getActivePath().push_front(std::move(PE));
- }
- } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) ||
- isa<GotoStmt>(Term)) {
- PathDiagnosticLocation L(Term, SM, PDB.LC);
- addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC);
- }
- }
- break;
+ // Does this represent entering a call? If so, look at propagating
+ // interesting symbols across call boundaries.
+ if (const ExplodedNode *NextNode = N->getFirstPred()) {
+ const LocationContext *CallerCtx = NextNode->getLocationContext();
+ const LocationContext *CalleeCtx = PDB.LC;
+ if (CallerCtx != CalleeCtx && AddPathEdges) {
+ reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(),
+ CalleeCtx, CallerCtx);
}
- } while (0);
+ }
- if (!NextNode)
- continue;
+ // Are we jumping to the head of a loop? Add a special diagnostic.
+ if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
+ PathDiagnosticLocation L(Loop, SM, PDB.LC);
+ const Stmt *Body = nullptr;
+
+ if (const auto *FS = dyn_cast<ForStmt>(Loop))
+ Body = FS->getBody();
+ else if (const auto *WS = dyn_cast<WhileStmt>(Loop))
+ Body = WS->getBody();
+ else if (const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) {
+ Body = OFS->getBody();
+ } else if (const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) {
+ Body = FRS->getBody();
+ }
+ // do-while statements are explicitly excluded here
- // Add pieces from custom visitors.
- llvm::FoldingSet<PathDiagnosticPiece> DeduplicationSet;
- for (auto &V : visitors) {
- if (auto p = V->VisitNode(N, NextNode, PDB, *report)) {
- if (DeduplicationSet.GetOrInsertNode(p.get()) != p.get())
- continue;
+ auto p = std::make_shared<PathDiagnosticEventPiece>(
+ L, "Looping back to the head "
+ "of the loop");
+ p->setPrunable(true);
- addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC);
- updateStackPiecesWithMessage(*p, CallStack);
- PD.getActivePath().push_front(std::move(p));
+ addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation(), PDB.LC);
+ PD.getActivePath().push_front(std::move(p));
+
+ if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createEndBrace(CS, SM),
+ PDB.LC);
}
}
- }
- // Add an edge to the start of the function.
- // We'll prune it out later, but it helps make diagnostics more uniform.
- const StackFrameContext *CalleeLC = PDB.LC->getCurrentStackFrame();
- const Decl *D = CalleeLC->getDecl();
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PathDiagnosticLocation::createBegin(D, SM),
- CalleeLC);
+ const CFGBlock *BSrc = BE->getSrc();
+ ParentMap &PM = PDB.getParentMap();
+
+ if (const Stmt *Term = BSrc->getTerminator()) {
+ // Are we jumping past the loop body without ever executing the
+ // loop (because the condition was false)?
+ if (isLoop(Term)) {
+ const Stmt *TermCond = getTerminatorCondition(BSrc);
+ bool IsInLoopBody =
+ isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term);
+
+ const char *str = nullptr;
+
+ if (isJumpToFalseBranch(&*BE)) {
+ if (!IsInLoopBody) {
+ if (isa<ObjCForCollectionStmt>(Term)) {
+ str = StrLoopCollectionEmpty;
+ } else if (isa<CXXForRangeStmt>(Term)) {
+ str = StrLoopRangeEmpty;
+ } else {
+ str = StrLoopBodyZero;
+ }
+ }
+ } else {
+ str = StrEnteringLoop;
+ }
- return report->isValid();
+ if (str) {
+ PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC);
+ auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
+ PE->setPrunable(true);
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PE->getLocation(), PDB.LC);
+ PD.getActivePath().push_front(std::move(PE));
+ }
+ } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) ||
+ isa<GotoStmt>(Term)) {
+ PathDiagnosticLocation L(Term, SM, PDB.LC);
+ addEdgeToPath(PD.getActivePath(), PrevLoc, L, PDB.LC);
+ }
+ }
+ }
}
-static const Stmt *getLocStmt(PathDiagnosticLocation L) {
- if (!L.isValid())
- return nullptr;
- return L.asStmt();
+static std::unique_ptr<PathDiagnostic>
+generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) {
+ BugType &BT = R->getBugType();
+ return llvm::make_unique<PathDiagnostic>(
+ R->getBugType().getCheckName(), R->getDeclWithIssue(),
+ R->getBugType().getName(), R->getDescription(),
+ R->getShortDescription(/*Fallback=*/false), BT.getCategory(),
+ R->getUniqueingLocation(), R->getUniqueingDecl(),
+ findExecutedLines(SM, R->getErrorNode()));
}
static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
@@ -1941,7 +1283,7 @@ static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
switch (S->getStmtClass()) {
case Stmt::BinaryOperatorClass: {
- const BinaryOperator *BO = cast<BinaryOperator>(S);
+ const auto *BO = cast<BinaryOperator>(S);
if (!BO->isLogicalOp())
return false;
return BO->getLHS() == Cond || BO->getRHS() == Cond;
@@ -1963,7 +1305,7 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
case Stmt::BinaryConditionalOperatorClass:
return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
case Stmt::ConditionalOperatorClass: {
- const ConditionalOperator *CO = cast<ConditionalOperator>(S);
+ const auto *CO = cast<ConditionalOperator>(S);
return CO->getCond() == Cond ||
CO->getLHS() == Cond ||
CO->getRHS() == Cond;
@@ -1971,7 +1313,7 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
case Stmt::ObjCForCollectionStmtClass:
return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
case Stmt::CXXForRangeStmtClass: {
- const CXXForRangeStmt *FRS = cast<CXXForRangeStmt>(S);
+ const auto *FRS = cast<CXXForRangeStmt>(S);
return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
}
default:
@@ -1980,16 +1322,15 @@ static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
}
static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL) {
- if (const ForStmt *FS = dyn_cast<ForStmt>(FL))
+ if (const auto *FS = dyn_cast<ForStmt>(FL))
return FS->getInc() == S || FS->getInit() == S;
- if (const CXXForRangeStmt *FRS = dyn_cast<CXXForRangeStmt>(FL))
+ if (const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
return FRS->getInc() == S || FRS->getRangeStmt() == S ||
FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
return false;
}
-typedef llvm::DenseSet<const PathDiagnosticCallPiece *>
- OptimizedCallsSet;
+using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>;
/// Adds synthetic edges from top-level statements to their subexpressions.
///
@@ -2001,8 +1342,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
PathPieces::iterator Prev = pieces.end();
for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
Prev = I, ++I) {
- PathDiagnosticControlFlowPiece *Piece =
- dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+ auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
if (!Piece)
continue;
@@ -2023,7 +1363,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
// This is important for nested logical expressions (||, &&, ?:) where we
// want to show all the levels of context.
while (true) {
- const Stmt *Dst = getLocStmt(Piece->getEndLocation());
+ const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
// We are looking at an edge. Is the destination within a larger
// expression?
@@ -2046,9 +1386,11 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
if (PrevPiece) {
- if (const Stmt *PrevSrc = getLocStmt(PrevPiece->getStartLocation())) {
+ if (const Stmt *PrevSrc =
+ PrevPiece->getStartLocation().getStmtOrNull()) {
const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM);
- if (PrevSrcParent == getStmtParent(getLocStmt(DstContext), PM)) {
+ if (PrevSrcParent ==
+ getStmtParent(DstContext.getStmtOrNull(), PM)) {
PrevPiece->setEndLocation(DstContext);
break;
}
@@ -2067,7 +1409,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
}
}
-/// \brief Move edges from a branch condition to a branch target
+/// Move edges from a branch condition to a branch target
/// when the condition is simple.
///
/// This restructures some of the work of addContextEdges. That function
@@ -2077,17 +1419,15 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
/// the branch to the branch condition, and (3) an edge from the branch
/// condition to the branch target. We keep (1), but may wish to remove (2)
/// and move the source of (3) to the branch if the branch condition is simple.
-///
static void simplifySimpleBranches(PathPieces &pieces) {
for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
-
- auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
if (!PieceI)
continue;
- const Stmt *s1Start = getLocStmt(PieceI->getStartLocation());
- const Stmt *s1End = getLocStmt(PieceI->getEndLocation());
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
if (!s1Start || !s1End)
continue;
@@ -2102,7 +1442,7 @@ static void simplifySimpleBranches(PathPieces &pieces) {
if (NextI == E)
break;
- auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
+ const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
if (EV) {
StringRef S = EV->getString();
if (S == StrEnteringLoop || S == StrLoopBodyZero ||
@@ -2120,8 +1460,8 @@ static void simplifySimpleBranches(PathPieces &pieces) {
if (!PieceNextI)
continue;
- const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation());
- const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation());
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
if (!s2Start || !s2End || s1End != s2Start)
continue;
@@ -2152,7 +1492,7 @@ static void simplifySimpleBranches(PathPieces &pieces) {
static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
SourceRange Range) {
SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
- SM.getExpansionRange(Range.getEnd()).second);
+ SM.getExpansionRange(Range.getEnd()).getEnd());
FileID FID = SM.getFileID(ExpansionRange.getBegin());
if (FID != SM.getFileID(ExpansionRange.getEnd()))
@@ -2204,22 +1544,21 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM,
ParentMap &PM) {
for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
// Pattern match the current piece and its successor.
- PathDiagnosticControlFlowPiece *PieceI =
- dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
if (!PieceI) {
++I;
continue;
}
- const Stmt *s1Start = getLocStmt(PieceI->getStartLocation());
- const Stmt *s1End = getLocStmt(PieceI->getEndLocation());
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
PathPieces::iterator NextI = I; ++NextI;
if (NextI == E)
break;
- PathDiagnosticControlFlowPiece *PieceNextI =
+ const auto *PieceNextI =
dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
if (!PieceNextI) {
@@ -2236,8 +1575,8 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM,
}
}
- const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation());
- const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation());
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
const size_t MAX_SHORT_LINE_LENGTH = 80;
@@ -2256,10 +1595,8 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM,
}
}
-/// \brief Return true if X is contained by Y.
-static bool lexicalContains(ParentMap &PM,
- const Stmt *X,
- const Stmt *Y) {
+/// Return true if X is contained by Y.
+static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) {
while (X) {
if (X == Y)
return true;
@@ -2269,24 +1606,21 @@ static bool lexicalContains(ParentMap &PM,
}
// Remove short edges on the same line less than 3 columns in difference.
-static void removePunyEdges(PathPieces &path,
- SourceManager &SM,
+static void removePunyEdges(PathPieces &path, SourceManager &SM,
ParentMap &PM) {
-
bool erased = false;
for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
erased ? I : ++I) {
-
erased = false;
- auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
if (!PieceI)
continue;
- const Stmt *start = getLocStmt(PieceI->getStartLocation());
- const Stmt *end = getLocStmt(PieceI->getEndLocation());
+ const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
if (!start || !end)
continue;
@@ -2327,7 +1661,7 @@ static void removePunyEdges(PathPieces &path,
static void removeIdenticalEvents(PathPieces &path) {
for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
- auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
+ const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
if (!PieceI)
continue;
@@ -2336,7 +1670,7 @@ static void removeIdenticalEvents(PathPieces &path) {
if (NextI == E)
return;
- auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
+ const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
if (!PieceNextI)
continue;
@@ -2377,8 +1711,8 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
continue;
}
- const Stmt *s1Start = getLocStmt(PieceI->getStartLocation());
- const Stmt *s1End = getLocStmt(PieceI->getEndLocation());
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
const Stmt *level1 = getStmtParent(s1Start, PM);
const Stmt *level2 = getStmtParent(s1End, PM);
@@ -2386,15 +1720,15 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
if (NextI == E)
break;
- auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
+ const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
if (!PieceNextI) {
++I;
continue;
}
- const Stmt *s2Start = getLocStmt(PieceNextI->getStartLocation());
- const Stmt *s2End = getLocStmt(PieceNextI->getEndLocation());
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
const Stmt *level3 = getStmtParent(s2Start, PM);
const Stmt *level4 = getStmtParent(s2End, PM);
@@ -2412,7 +1746,6 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
//
// NOTE: this will be limited later in cases where we add barriers
// to prevent this optimization.
- //
if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
PieceI->setEndLocation(PieceNextI->getEndLocation());
path.erase(NextI);
@@ -2427,7 +1760,6 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
//
// NOTE: this will be limited later in cases where we add barriers
// to prevent this optimization.
- //
if (s1End && s1End == s2Start && level2) {
bool removeEdge = false;
// Remove edges into the increment or initialization of a
@@ -2493,8 +1825,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
//
// (X -> element)
if (s1End == s2Start) {
- const ObjCForCollectionStmt *FS =
- dyn_cast_or_null<ObjCForCollectionStmt>(level3);
+ const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
s2End == FS->getElement()) {
PieceI->setEndLocation(PieceNextI->getEndLocation());
@@ -2532,8 +1863,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
/// statement had an invalid source location), this function does nothing.
// FIXME: We should just generate invalid edges anyway and have the optimizer
// deal with them.
-static void dropFunctionEntryEdge(PathPieces &Path,
- LocationContextMap &LCM,
+static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM,
SourceManager &SM) {
const auto *FirstEdge =
dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
@@ -2548,11 +1878,134 @@ static void dropFunctionEntryEdge(PathPieces &Path,
Path.pop_front();
}
+using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *,
+ std::vector<std::shared_ptr<PathDiagnosticPiece>>>;
+
+/// This function is responsible for generating diagnostic pieces that are
+/// *not* provided by bug report visitors.
+/// These diagnostics may differ depending on the consumer's settings,
+/// and are therefore constructed separately for each consumer.
+///
+/// There are two path diagnostics generation modes: with adding edges (used
+/// for plists) and without (used for HTML and text).
+/// When edges are added (\p ActiveScheme is Extensive),
+/// the path is modified to insert artificially generated
+/// edges.
+/// Otherwise, more detailed diagnostics is emitted for block edges, explaining
+/// the transitions in words.
+static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer(
+ PathDiagnosticConsumer::PathGenerationScheme ActiveScheme,
+ PathDiagnosticBuilder &PDB,
+ const ExplodedNode *ErrorNode,
+ const VisitorsDiagnosticsTy &VisitorsDiagnostics) {
+
+ bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None);
+ bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive);
+ SourceManager &SM = PDB.getSourceManager();
+ BugReport *R = PDB.getBugReport();
+ AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions();
+ StackDiagVector CallStack;
+ InterestingExprs IE;
+ LocationContextMap LCM;
+ std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM);
+
+ if (GenerateDiagnostics) {
+ auto EndNotes = VisitorsDiagnostics.find(ErrorNode);
+ std::shared_ptr<PathDiagnosticPiece> LastPiece;
+ if (EndNotes != VisitorsDiagnostics.end()) {
+ assert(!EndNotes->second.empty());
+ LastPiece = EndNotes->second[0];
+ } else {
+ LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R);
+ }
+ PD->setEndOfPath(LastPiece);
+ }
+
+ PathDiagnosticLocation PrevLoc = PD->getLocation();
+ const ExplodedNode *NextNode = ErrorNode->getFirstPred();
+ while (NextNode) {
+ if (GenerateDiagnostics)
+ generatePathDiagnosticsForNode(
+ NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges);
+
+ auto VisitorNotes = VisitorsDiagnostics.find(NextNode);
+ NextNode = NextNode->getFirstPred();
+ if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end())
+ continue;
+
+ // This is a workaround due to inability to put shared PathDiagnosticPiece
+ // into a FoldingSet.
+ std::set<llvm::FoldingSetNodeID> DeduplicationSet;
+
+ // Add pieces from custom visitors.
+ for (const auto &Note : VisitorNotes->second) {
+ llvm::FoldingSetNodeID ID;
+ Note->Profile(ID);
+ auto P = DeduplicationSet.insert(ID);
+ if (!P.second)
+ continue;
+
+ if (AddPathEdges)
+ addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation(),
+ PDB.LC);
+ updateStackPiecesWithMessage(*Note, CallStack);
+ PD->getActivePath().push_front(Note);
+ }
+ }
+
+ if (AddPathEdges) {
+ // Add an edge to the start of the function.
+ // We'll prune it out later, but it helps make diagnostics more uniform.
+ const StackFrameContext *CalleeLC = PDB.LC->getStackFrame();
+ const Decl *D = CalleeLC->getDecl();
+ addEdgeToPath(PD->getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createBegin(D, SM), CalleeLC);
+ }
+
+ if (!AddPathEdges && GenerateDiagnostics)
+ CompactPathDiagnostic(PD->getMutablePieces(), SM);
+
+ // Finally, prune the diagnostic path of uninteresting stuff.
+ if (!PD->path.empty()) {
+ if (R->shouldPrunePath() && Opts.shouldPrunePaths()) {
+ bool stillHasNotes =
+ removeUnneededCalls(PD->getMutablePieces(), R, LCM);
+ assert(stillHasNotes);
+ (void)stillHasNotes;
+ }
+
+ // Redirect all call pieces to have valid locations.
+ adjustCallLocations(PD->getMutablePieces());
+ removePiecesWithInvalidLocations(PD->getMutablePieces());
+
+ if (AddPathEdges) {
+
+ // Reduce the number of edges from a very conservative set
+ // to an aesthetically pleasing subset that conveys the
+ // necessary information.
+ OptimizedCallsSet OCS;
+ while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {}
+
+ // Drop the very first function-entry edge. It's not really necessary
+ // for top-level functions.
+ dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM);
+ }
+
+ // Remove messages that are basically the same, and edges that may not
+ // make sense.
+ // We have to do this after edge optimization in the Extensive mode.
+ removeRedundantMsgs(PD->getMutablePieces());
+ removeEdgesToDefaultInitializers(PD->getMutablePieces());
+ }
+ return PD;
+}
+
//===----------------------------------------------------------------------===//
// Methods for BugType and subclasses.
//===----------------------------------------------------------------------===//
-void BugType::anchor() { }
+
+void BugType::anchor() {}
void BugType::FlushReports(BugReporter &BR) {}
@@ -2570,14 +2023,17 @@ void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) {
llvm::FoldingSetNodeID ID;
visitor->Profile(ID);
- void *InsertPos;
- if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos))
+ void *InsertPos = nullptr;
+ if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
return;
+ }
- CallbacksSet.InsertNode(visitor.get(), InsertPos);
Callbacks.push_back(std::move(visitor));
- ++ConfigurationChangeToken;
+}
+
+void BugReport::clearVisitors() {
+ Callbacks.clear();
}
BugReport::~BugReport() {
@@ -2595,7 +2051,7 @@ const Decl *BugReport::getDeclWithIssue() const {
return nullptr;
const LocationContext *LC = N->getLocationContext();
- return LC->getCurrentStackFrame()->getDecl();
+ return LC->getStackFrame()->getDecl();
}
void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
@@ -2623,11 +2079,9 @@ void BugReport::markInteresting(SymbolRef sym) {
if (!sym)
return;
- // If the symbol wasn't already in our set, note a configuration change.
- if (getInterestingSymbols().insert(sym).second)
- ++ConfigurationChangeToken;
+ getInterestingSymbols().insert(sym);
- if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym))
+ if (const auto *meta = dyn_cast<SymbolMetadata>(sym))
getInterestingRegions().insert(meta->getRegion());
}
@@ -2635,12 +2089,10 @@ void BugReport::markInteresting(const MemRegion *R) {
if (!R)
return;
- // If the base region wasn't already in our set, note a configuration change.
R = R->getBaseRegion();
- if (getInterestingRegions().insert(R).second)
- ++ConfigurationChangeToken;
+ getInterestingRegions().insert(R);
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R))
getInterestingSymbols().insert(SR->getSymbol());
}
@@ -2674,7 +2126,7 @@ bool BugReport::isInteresting(const MemRegion *R) {
bool b = getInterestingRegions().count(R);
if (b)
return true;
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R))
return getInterestingSymbols().count(SR->getSymbol());
return false;
}
@@ -2734,7 +2186,7 @@ llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() {
// If no custom ranges, add the range of the statement corresponding to
// the error node.
if (Ranges.empty()) {
- if (const Expr *E = dyn_cast_or_null<Expr>(getStmt()))
+ if (const auto *E = dyn_cast_or_null<Expr>(getStmt()))
addRange(E->getSourceRange());
else
return llvm::make_range(ranges_iterator(), ranges_iterator());
@@ -2762,9 +2214,11 @@ PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
// Methods for BugReporter and subclasses.
//===----------------------------------------------------------------------===//
-BugReportEquivClass::~BugReportEquivClass() { }
-GRBugReporter::~GRBugReporter() { }
-BugReporterData::~BugReporterData() {}
+BugReportEquivClass::~BugReportEquivClass() = default;
+
+GRBugReporter::~GRBugReporter() = default;
+
+BugReporterData::~BugReporterData() = default;
ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); }
@@ -2775,11 +2229,8 @@ BugReporter::~BugReporter() {
FlushReports();
// Free the bug reports we are tracking.
- typedef std::vector<BugReportEquivClass *> ContTy;
- for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end();
- I != E; ++I) {
- delete *I;
- }
+ for (const auto I : EQClassesVector)
+ delete I;
}
void BugReporter::FlushReports() {
@@ -2791,18 +2242,13 @@ void BugReporter::FlushReports() {
// FIXME: Only NSErrorChecker needs BugType's FlushReports.
// Turn NSErrorChecker into a proper checker and remove this.
SmallVector<const BugType *, 16> bugTypes(BugTypes.begin(), BugTypes.end());
- for (SmallVectorImpl<const BugType *>::iterator
- I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
- const_cast<BugType*>(*I)->FlushReports(*this);
+ for (const auto I : bugTypes)
+ const_cast<BugType*>(I)->FlushReports(*this);
// We need to flush reports in deterministic order to ensure the order
// of the reports is consistent between runs.
- typedef std::vector<BugReportEquivClass *> ContVecTy;
- for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end();
- EI != EE; ++EI){
- BugReportEquivClass& EQ = **EI;
- FlushReport(EQ);
- }
+ for (const auto EQ : EQClassesVector)
+ FlushReport(*EQ);
// BugReporter owns and deletes only BugTypes created implicitly through
// EmitBasicReport.
@@ -2819,6 +2265,7 @@ void BugReporter::FlushReports() {
//===----------------------------------------------------------------------===//
namespace {
+
/// A wrapper around a report graph, which contains only a single path, and its
/// node maps.
class ReportGraph {
@@ -2833,10 +2280,12 @@ public:
class TrimmedGraph {
InterExplodedGraphMap InverseMap;
- typedef llvm::DenseMap<const ExplodedNode *, unsigned> PriorityMapTy;
+ using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
+
PriorityMapTy PriorityMap;
- typedef std::pair<const ExplodedNode *, size_t> NodeIndexPair;
+ using NodeIndexPair = std::pair<const ExplodedNode *, size_t>;
+
SmallVector<NodeIndexPair, 32> ReportNodes;
std::unique_ptr<ExplodedGraph> G;
@@ -2874,7 +2323,8 @@ public:
bool popNextReportGraph(ReportGraph &GraphWrapper);
};
-}
+
+} // namespace
TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
ArrayRef<const ExplodedNode *> Nodes) {
@@ -2930,8 +2380,8 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
}
// Sort the error paths from longest to shortest.
- std::sort(ReportNodes.begin(), ReportNodes.end(),
- PriorityCompare<true>(PriorityMap));
+ llvm::sort(ReportNodes.begin(), ReportNodes.end(),
+ PriorityCompare<true>(PriorityMap));
}
bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
@@ -2987,23 +2437,21 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
return true;
}
-
/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
/// and collapses PathDiagosticPieces that are expanded by macros.
static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
- typedef std::vector<
- std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>
- MacroStackTy;
+ using MacroStackTy =
+ std::vector<
+ std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
- typedef std::vector<std::shared_ptr<PathDiagnosticPiece>> PiecesTy;
+ using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>;
MacroStackTy MacroStack;
PiecesTy Pieces;
for (PathPieces::const_iterator I = path.begin(), E = path.end();
- I!=E; ++I) {
-
- auto &piece = *I;
+ I != E; ++I) {
+ const auto &piece = *I;
// Recursively compact calls.
if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
@@ -3082,43 +2530,69 @@ static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
path.insert(path.end(), Pieces.begin(), Pieces.end());
}
-bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
- PathDiagnosticConsumer &PC,
- ArrayRef<BugReport *> &bugReports) {
- assert(!bugReports.empty());
+/// Generate notes from all visitors.
+/// Notes associated with {@code ErrorNode} are generated using
+/// {@code getEndPath}, and the rest are generated with {@code VisitNode}.
+static std::unique_ptr<VisitorsDiagnosticsTy>
+generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode,
+ BugReporterContext &BRC) {
+ auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>();
+ BugReport::VisitorList visitors;
- bool HasValid = false;
- bool HasInvalid = false;
- SmallVector<const ExplodedNode *, 32> errorNodes;
- for (ArrayRef<BugReport*>::iterator I = bugReports.begin(),
- E = bugReports.end(); I != E; ++I) {
- if ((*I)->isValid()) {
- HasValid = true;
- errorNodes.push_back((*I)->getErrorNode());
- } else {
- // Keep the errorNodes list in sync with the bugReports list.
- HasInvalid = true;
- errorNodes.push_back(nullptr);
+ // Run visitors on all nodes starting from the node *before* the last one.
+ // The last node is reserved for notes generated with {@code getEndPath}.
+ const ExplodedNode *NextNode = ErrorNode->getFirstPred();
+ while (NextNode) {
+
+ // At each iteration, move all visitors from report to visitor list.
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end();
+ I != E; ++I) {
+ visitors.push_back(std::move(*I));
}
- }
+ R->clearVisitors();
- // If all the reports have been marked invalid by a previous path generation,
- // we're done.
- if (!HasValid)
- return false;
+ const ExplodedNode *Pred = NextNode->getFirstPred();
+ if (!Pred) {
+ std::shared_ptr<PathDiagnosticPiece> LastPiece;
+ for (auto &V : visitors) {
+ V->finalizeVisitor(BRC, ErrorNode, *R);
- typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme;
- PathGenerationScheme ActiveScheme = PC.getGenerationScheme();
+ if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) {
+ assert(!LastPiece &&
+ "There can only be one final piece in a diagnostic.");
+ LastPiece = std::move(Piece);
+ (*Notes)[ErrorNode].push_back(LastPiece);
+ }
+ }
+ break;
+ }
- if (ActiveScheme == PathDiagnosticConsumer::Extensive) {
- AnalyzerOptions &options = getAnalyzerOptions();
- if (options.getBooleanOption("path-diagnostics-alternate", true)) {
- ActiveScheme = PathDiagnosticConsumer::AlternateExtensive;
+ for (auto &V : visitors) {
+ auto P = V->VisitNode(NextNode, Pred, BRC, *R);
+ if (P)
+ (*Notes)[NextNode].push_back(std::move(P));
}
+
+ if (!R->isValid())
+ break;
+
+ NextNode = Pred;
}
- TrimmedGraph TrimG(&getGraph(), errorNodes);
- ReportGraph ErrorGraph;
+ return Notes;
+}
+
+/// Find a non-invalidated report for a given equivalence class,
+/// and return together with a cache of visitors notes.
+/// If none found, return a nullptr paired with an empty cache.
+static
+std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport(
+ TrimmedGraph &TrimG,
+ ReportGraph &ErrorGraph,
+ ArrayRef<BugReport *> &bugReports,
+ AnalyzerOptions &Opts,
+ GRBugReporter &Reporter) {
while (TrimG.popNextReportGraph(ErrorGraph)) {
// Find the BugReport with the original location.
@@ -3126,125 +2600,85 @@ bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
BugReport *R = bugReports[ErrorGraph.Index];
assert(R && "No original report found for sliced graph.");
assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
+ const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
- // Start building the path diagnostic...
- PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC);
- const ExplodedNode *N = ErrorGraph.ErrorNode;
+ // Register refutation visitors first, if they mark the bug invalid no
+ // further analysis is required
+ R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
// Register additional node visitors.
R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
- R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>());
- BugReport::VisitorList visitors;
- unsigned origReportConfigToken, finalReportConfigToken;
- LocationContextMap LCM;
-
- // While generating diagnostics, it's possible the visitors will decide
- // new symbols and regions are interesting, or add other visitors based on
- // the information they find. If they do, we need to regenerate the path
- // based on our new report configuration.
- do {
- // Get a clean copy of all the visitors.
- for (BugReport::visitor_iterator I = R->visitor_begin(),
- E = R->visitor_end(); I != E; ++I)
- visitors.push_back((*I)->clone());
-
- // Clear out the active path from any previous work.
- PD.resetPath();
- origReportConfigToken = R->getConfigurationChangeToken();
-
- // Generate the very last diagnostic piece - the piece is visible before
- // the trace is expanded.
- std::unique_ptr<PathDiagnosticPiece> LastPiece;
- for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end();
- I != E; ++I) {
- if (std::unique_ptr<PathDiagnosticPiece> Piece =
- (*I)->getEndPath(PDB, N, *R)) {
- assert (!LastPiece &&
- "There can only be one final piece in a diagnostic.");
- LastPiece = std::move(Piece);
- }
- }
+ BugReporterContext BRC(Reporter, ErrorGraph.BackMap);
- if (ActiveScheme != PathDiagnosticConsumer::None) {
- if (!LastPiece)
- LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R);
- assert(LastPiece);
- PD.setEndOfPath(std::move(LastPiece));
- }
+ // Run all visitors on a given graph, once.
+ std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
+ generateVisitorsDiagnostics(R, ErrorNode, BRC);
- // Make sure we get a clean location context map so we don't
- // hold onto old mappings.
- LCM.clear();
+ if (R->isValid()) {
+ if (Opts.shouldCrosscheckWithZ3()) {
+ // If crosscheck is enabled, remove all visitors, add the refutation
+ // visitor and check again
+ R->clearVisitors();
+ R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>());
- switch (ActiveScheme) {
- case PathDiagnosticConsumer::AlternateExtensive:
- GenerateAlternateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors);
- break;
- case PathDiagnosticConsumer::Extensive:
- GenerateExtensivePathDiagnostic(PD, PDB, N, LCM, visitors);
- break;
- case PathDiagnosticConsumer::Minimal:
- GenerateMinimalPathDiagnostic(PD, PDB, N, LCM, visitors);
- break;
- case PathDiagnosticConsumer::None:
- GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors);
- break;
+ // We don't overrite the notes inserted by other visitors because the
+ // refutation manager does not add any new note to the path
+ generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC);
}
- // Clean up the visitors we used.
- visitors.clear();
-
- // Did anything change while generating this path?
- finalReportConfigToken = R->getConfigurationChangeToken();
- } while (finalReportConfigToken != origReportConfigToken);
-
- if (!R->isValid())
- continue;
-
- // Finally, prune the diagnostic path of uninteresting stuff.
- if (!PD.path.empty()) {
- if (R->shouldPrunePath() && getAnalyzerOptions().shouldPrunePaths()) {
- bool stillHasNotes = removeUnneededCalls(PD.getMutablePieces(), R, LCM);
- assert(stillHasNotes);
- (void)stillHasNotes;
- }
+ // Check if the bug is still valid
+ if (R->isValid())
+ return std::make_pair(R, std::move(visitorNotes));
+ }
+ }
- // Redirect all call pieces to have valid locations.
- adjustCallLocations(PD.getMutablePieces());
- removePiecesWithInvalidLocations(PD.getMutablePieces());
+ return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>());
+}
- if (ActiveScheme == PathDiagnosticConsumer::AlternateExtensive) {
- SourceManager &SM = getSourceManager();
+std::unique_ptr<DiagnosticForConsumerMapTy>
+GRBugReporter::generatePathDiagnostics(
+ ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> &bugReports) {
+ assert(!bugReports.empty());
- // Reduce the number of edges from a very conservative set
- // to an aesthetically pleasing subset that conveys the
- // necessary information.
- OptimizedCallsSet OCS;
- while (optimizeEdges(PD.getMutablePieces(), SM, OCS, LCM)) {}
+ auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>();
+ bool HasValid = false;
+ SmallVector<const ExplodedNode *, 32> errorNodes;
+ for (const auto I : bugReports) {
+ if (I->isValid()) {
+ HasValid = true;
+ errorNodes.push_back(I->getErrorNode());
+ } else {
+ // Keep the errorNodes list in sync with the bugReports list.
+ errorNodes.push_back(nullptr);
+ }
+ }
- // Drop the very first function-entry edge. It's not really necessary
- // for top-level functions.
- dropFunctionEntryEdge(PD.getMutablePieces(), LCM, SM);
- }
+ // If all the reports have been marked invalid by a previous path generation,
+ // we're done.
+ if (!HasValid)
+ return Out;
- // Remove messages that are basically the same, and edges that may not
- // make sense.
- // We have to do this after edge optimization in the Extensive mode.
- removeRedundantMsgs(PD.getMutablePieces());
- removeEdgesToDefaultInitializers(PD.getMutablePieces());
+ TrimmedGraph TrimG(&getGraph(), errorNodes);
+ ReportGraph ErrorGraph;
+ auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports,
+ getAnalyzerOptions(), *this);
+ BugReport *R = ReportInfo.first;
+
+ if (R && R->isValid()) {
+ const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
+ for (PathDiagnosticConsumer *PC : consumers) {
+ PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC);
+ std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer(
+ PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second);
+ (*Out)[PC] = std::move(PD);
}
-
- // We found a report and didn't suppress it.
- return true;
}
- // We suppressed all the reports in this equivalence class.
- assert(!HasInvalid && "Inconsistent suppression");
- (void)HasInvalid;
- return false;
+ return Out;
}
void BugReporter::Register(BugType *BT) {
@@ -3294,20 +2728,21 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
EQ->AddReport(std::move(R));
}
-
//===----------------------------------------------------------------------===//
// Emitting reports in equivalence classes.
//===----------------------------------------------------------------------===//
namespace {
+
struct FRIEC_WLItem {
const ExplodedNode *N;
ExplodedNode::const_succ_iterator I, E;
FRIEC_WLItem(const ExplodedNode *n)
- : N(n), I(N->succ_begin()), E(N->succ_end()) {}
+ : N(n), I(N->succ_begin()), E(N->succ_end()) {}
};
-}
+
+} // namespace
static const CFGBlock *findBlockForNode(const ExplodedNode *N) {
ProgramPoint P = N->getLocation();
@@ -3397,7 +2832,6 @@ static bool isInevitablySinking(const ExplodedNode *N) {
static BugReport *
FindReportInEquivalenceClass(BugReportEquivClass& EQ,
SmallVectorImpl<BugReport*> &bugReports) {
-
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
assert(I != E);
BugType& BT = I->getBugType();
@@ -3407,10 +2841,10 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// to 'Nodes'. Any of the reports will serve as a "representative" report.
if (!BT.isSuppressOnSink()) {
BugReport *R = &*I;
- for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) {
- const ExplodedNode *N = I->getErrorNode();
+ for (auto &I : EQ) {
+ const ExplodedNode *N = I.getErrorNode();
if (N) {
- R = &*I;
+ R = &I;
bugReports.push_back(R);
}
}
@@ -3451,8 +2885,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// At this point we know that 'N' is not a sink and it has at least one
// successor. Use a DFS worklist to find a non-sink end-of-path node.
- typedef FRIEC_WLItem WLItem;
- typedef SmallVector<WLItem, 10> DFSWorkList;
+ using WLItem = FRIEC_WLItem;
+ using DFSWorkList = SmallVector<WLItem, 10>;
+
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
DFSWorkList WL;
@@ -3502,90 +2937,166 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
SmallVector<BugReport*, 10> bugReports;
- BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports);
- if (exampleReport) {
- for (PathDiagnosticConsumer *PDC : getPathDiagnosticConsumers()) {
- FlushReport(exampleReport, *PDC, bugReports);
+ BugReport *report = FindReportInEquivalenceClass(EQ, bugReports);
+ if (!report)
+ return;
+
+ ArrayRef<PathDiagnosticConsumer*> Consumers = getPathDiagnosticConsumers();
+ std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
+ generateDiagnosticForConsumerMap(report, Consumers, bugReports);
+
+ for (auto &P : *Diagnostics) {
+ PathDiagnosticConsumer *Consumer = P.first;
+ std::unique_ptr<PathDiagnostic> &PD = P.second;
+
+ // If the path is empty, generate a single step path with the location
+ // of the issue.
+ if (PD->path.empty()) {
+ PathDiagnosticLocation L = report->getLocation(getSourceManager());
+ auto piece = llvm::make_unique<PathDiagnosticEventPiece>(
+ L, report->getDescription());
+ for (SourceRange Range : report->getRanges())
+ piece->addRange(Range);
+ PD->setEndOfPath(std::move(piece));
}
- }
-}
-void BugReporter::FlushReport(BugReport *exampleReport,
- PathDiagnosticConsumer &PD,
- ArrayRef<BugReport*> bugReports) {
+ PathPieces &Pieces = PD->getMutablePieces();
+ if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) {
+ // For path diagnostic consumers that don't support extra notes,
+ // we may optionally convert those to path notes.
+ for (auto I = report->getNotes().rbegin(),
+ E = report->getNotes().rend(); I != E; ++I) {
+ PathDiagnosticNotePiece *Piece = I->get();
+ auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
+ Piece->getLocation(), Piece->getString());
+ for (const auto &R: Piece->getRanges())
+ ConvertedPiece->addRange(R);
+
+ Pieces.push_front(std::move(ConvertedPiece));
+ }
+ } else {
+ for (auto I = report->getNotes().rbegin(),
+ E = report->getNotes().rend(); I != E; ++I)
+ Pieces.push_front(*I);
+ }
- // FIXME: Make sure we use the 'R' for the path that was actually used.
- // Probably doesn't make a difference in practice.
- BugType& BT = exampleReport->getBugType();
+ // Get the meta data.
+ const BugReport::ExtraTextList &Meta = report->getExtraText();
+ for (const auto &i : Meta)
+ PD->addMeta(i);
- std::unique_ptr<PathDiagnostic> D(new PathDiagnostic(
- exampleReport->getBugType().getCheckName(),
- exampleReport->getDeclWithIssue(), exampleReport->getBugType().getName(),
- exampleReport->getDescription(),
- exampleReport->getShortDescription(/*Fallback=*/false), BT.getCategory(),
- exampleReport->getUniqueingLocation(),
- exampleReport->getUniqueingDecl()));
+ Consumer->HandlePathDiagnostic(std::move(PD));
+ }
+}
- if (exampleReport->isPathSensitive()) {
- // Generate the full path diagnostic, using the generation scheme
- // specified by the PathDiagnosticConsumer. Note that we have to generate
- // path diagnostics even for consumers which do not support paths, because
- // the BugReporterVisitors may mark this bug as a false positive.
- assert(!bugReports.empty());
+/// Insert all lines participating in the function signature \p Signature
+/// into \p ExecutedLines.
+static void populateExecutedLinesWithFunctionSignature(
+ const Decl *Signature, SourceManager &SM,
+ std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) {
+ SourceRange SignatureSourceRange;
+ const Stmt* Body = Signature->getBody();
+ if (const auto FD = dyn_cast<FunctionDecl>(Signature)) {
+ SignatureSourceRange = FD->getSourceRange();
+ } else if (const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
+ SignatureSourceRange = OD->getSourceRange();
+ } else {
+ return;
+ }
+ SourceLocation Start = SignatureSourceRange.getBegin();
+ SourceLocation End = Body ? Body->getSourceRange().getBegin()
+ : SignatureSourceRange.getEnd();
+ unsigned StartLine = SM.getExpansionLineNumber(Start);
+ unsigned EndLine = SM.getExpansionLineNumber(End);
- MaxBugClassSize.updateMax(bugReports.size());
+ FileID FID = SM.getFileID(SM.getExpansionLoc(Start));
+ for (unsigned Line = StartLine; Line <= EndLine; Line++)
+ ExecutedLines->operator[](FID.getHashValue()).insert(Line);
+}
- if (!generatePathDiagnostic(*D.get(), PD, bugReports))
- return;
+static void populateExecutedLinesWithStmt(
+ const Stmt *S, SourceManager &SM,
+ std::unique_ptr<FilesToLineNumsMap> &ExecutedLines) {
+ SourceLocation Loc = S->getSourceRange().getBegin();
+ SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
+ FileID FID = SM.getFileID(ExpansionLoc);
+ unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc);
+ ExecutedLines->operator[](FID.getHashValue()).insert(LineNo);
+}
- MaxValidBugClassSize.updateMax(bugReports.size());
+/// \return all executed lines including function signatures on the path
+/// starting from \p N.
+static std::unique_ptr<FilesToLineNumsMap>
+findExecutedLines(SourceManager &SM, const ExplodedNode *N) {
+ auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>();
- // Examine the report and see if the last piece is in a header. Reset the
- // report location to the last piece in the main source file.
- AnalyzerOptions &Opts = getAnalyzerOptions();
- if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
- D->resetDiagnosticLocationToMainFile();
- }
-
- // If the path is empty, generate a single step path with the location
- // of the issue.
- if (D->path.empty()) {
- PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager());
- auto piece = llvm::make_unique<PathDiagnosticEventPiece>(
- L, exampleReport->getDescription());
- for (SourceRange Range : exampleReport->getRanges())
- piece->addRange(Range);
- D->setEndOfPath(std::move(piece));
- }
-
- PathPieces &Pieces = D->getMutablePieces();
- if (getAnalyzerOptions().shouldDisplayNotesAsEvents()) {
- // For path diagnostic consumers that don't support extra notes,
- // we may optionally convert those to path notes.
- for (auto I = exampleReport->getNotes().rbegin(),
- E = exampleReport->getNotes().rend(); I != E; ++I) {
- PathDiagnosticNotePiece *Piece = I->get();
- auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
- Piece->getLocation(), Piece->getString());
- for (const auto &R: Piece->getRanges())
- ConvertedPiece->addRange(R);
+ while (N) {
+ if (N->getFirstPred() == nullptr) {
+ // First node: show signature of the entrance point.
+ const Decl *D = N->getLocationContext()->getDecl();
+ populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines);
+ } else if (auto CE = N->getLocationAs<CallEnter>()) {
+ // Inlined function: show signature.
+ const Decl* D = CE->getCalleeContext()->getDecl();
+ populateExecutedLinesWithFunctionSignature(D, SM, ExecutedLines);
+ } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+ populateExecutedLinesWithStmt(S, SM, ExecutedLines);
+
+ // Show extra context for some parent kinds.
+ const Stmt *P = N->getParentMap().getParent(S);
+
+ // The path exploration can die before the node with the associated
+ // return statement is generated, but we do want to show the whole
+ // return.
+ if (const auto *RS = dyn_cast_or_null<ReturnStmt>(P)) {
+ populateExecutedLinesWithStmt(RS, SM, ExecutedLines);
+ P = N->getParentMap().getParent(RS);
+ }
- Pieces.push_front(std::move(ConvertedPiece));
+ if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P)))
+ populateExecutedLinesWithStmt(P, SM, ExecutedLines);
}
- } else {
- for (auto I = exampleReport->getNotes().rbegin(),
- E = exampleReport->getNotes().rend(); I != E; ++I)
- Pieces.push_front(*I);
+
+ N = N->getFirstPred();
}
+ return ExecutedLines;
+}
+
+std::unique_ptr<DiagnosticForConsumerMapTy>
+BugReporter::generateDiagnosticForConsumerMap(
+ BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> bugReports) {
- // Get the meta data.
- const BugReport::ExtraTextList &Meta = exampleReport->getExtraText();
- for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
- e = Meta.end(); i != e; ++i) {
- D->addMeta(*i);
+ if (!report->isPathSensitive()) {
+ auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>();
+ for (auto *Consumer : consumers)
+ (*Out)[Consumer] = generateEmptyDiagnosticForReport(report,
+ getSourceManager());
+ return Out;
}
- PD.HandlePathDiagnostic(std::move(D));
+ // Generate the full path sensitive diagnostic, using the generation scheme
+ // specified by the PathDiagnosticConsumer. Note that we have to generate
+ // path diagnostics even for consumers which do not support paths, because
+ // the BugReporterVisitors may mark this bug as a false positive.
+ assert(!bugReports.empty());
+ MaxBugClassSize.updateMax(bugReports.size());
+ std::unique_ptr<DiagnosticForConsumerMapTy> Out =
+ generatePathDiagnostics(consumers, bugReports);
+
+ if (Out->empty())
+ return Out;
+
+ MaxValidBugClassSize.updateMax(bugReports.size());
+
+ // Examine the report and see if the last piece is in a header. Reset the
+ // report location to the last piece in the main source file.
+ AnalyzerOptions &Opts = getAnalyzerOptions();
+ for (auto const &P : *Out)
+ if (Opts.shouldReportIssuesInMainSourceFile() && !Opts.AnalyzeAll)
+ P.second->resetDiagnosticLocationToMainFile();
+
+ return Out;
}
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
@@ -3596,12 +3107,12 @@ void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str,
Loc, Ranges);
}
+
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
CheckName CheckName,
StringRef name, StringRef category,
StringRef str, PathDiagnosticLocation Loc,
ArrayRef<SourceRange> Ranges) {
-
// 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(CheckName, name, category);
auto R = llvm::make_unique<BugReport>(*BT, str, Loc);
@@ -3622,84 +3133,3 @@ BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name,
BT = new BugType(CheckName, name, category);
return BT;
}
-
-LLVM_DUMP_METHOD void PathPieces::dump() const {
- unsigned index = 0;
- for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {
- llvm::errs() << "[" << index++ << "] ";
- (*I)->dump();
- llvm::errs() << "\n";
- }
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const {
- llvm::errs() << "CALL\n--------------\n";
-
- if (const Stmt *SLoc = getLocStmt(getLocation()))
- SLoc->dump();
- else if (const NamedDecl *ND = dyn_cast<NamedDecl>(getCallee()))
- llvm::errs() << *ND << "\n";
- else
- getLocation().dump();
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const {
- llvm::errs() << "EVENT\n--------------\n";
- llvm::errs() << getString() << "\n";
- llvm::errs() << " ---- at ----\n";
- getLocation().dump();
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const {
- llvm::errs() << "CONTROL\n--------------\n";
- getStartLocation().dump();
- llvm::errs() << " ---- to ----\n";
- getEndLocation().dump();
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {
- llvm::errs() << "MACRO\n--------------\n";
- // FIXME: Print which macro is being invoked.
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
- llvm::errs() << "NOTE\n--------------\n";
- llvm::errs() << getString() << "\n";
- llvm::errs() << " ---- at ----\n";
- getLocation().dump();
-}
-
-LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
- if (!isValid()) {
- llvm::errs() << "<INVALID>\n";
- return;
- }
-
- switch (K) {
- case RangeK:
- // FIXME: actually print the range.
- llvm::errs() << "<range>\n";
- break;
- case SingleLocK:
- asLocation().dump();
- llvm::errs() << "\n";
- break;
- case StmtK:
- if (S)
- S->dump();
- else
- llvm::errs() << "<NULL STMT>\n";
- break;
- case DeclK:
- if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D))
- llvm::errs() << *ND << "\n";
- else if (isa<BlockDecl>(D))
- // FIXME: Make this nicer.
- llvm::errs() << "<block>\n";
- else if (D)
- llvm::errs() << "<unknown decl>\n";
- else
- llvm::errs() << "<NULL DECL>\n";
- break;
- }
-}
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 972f4c5f3da2..c87bc685d8b9 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1,4 +1,4 @@
-// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--//
+//===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,37 +11,83 @@
// enhance the diagnostics reported for a bug.
//
//===----------------------------------------------------------------------===//
+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <deque>
+#include <memory>
+#include <string>
+#include <utility>
using namespace clang;
using namespace ento;
-using llvm::FoldingSetNodeID;
-
//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//
bool bugreporter::isDeclRefExprToReference(const Expr *E) {
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl()->getType()->isReferenceType();
- }
return false;
}
+static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) {
+ if (B->isAdditiveOp() && B->getType()->isPointerType()) {
+ if (B->getLHS()->getType()->isPointerType()) {
+ return B->getLHS();
+ } else if (B->getRHS()->getType()->isPointerType()) {
+ return B->getRHS();
+ }
+ }
+ return nullptr;
+}
+
/// Given that expression S represents a pointer that would be dereferenced,
/// try to find a sub-expression from which the pointer came from.
/// This is used for tracking down origins of a null or undefined value:
@@ -55,33 +101,27 @@ bool bugreporter::isDeclRefExprToReference(const Expr *E) {
/// x->y.z ==> x (lvalue)
/// foo()->y.z ==> foo() (rvalue)
const Expr *bugreporter::getDerefExpr(const Stmt *S) {
- const Expr *E = dyn_cast<Expr>(S);
+ const auto *E = dyn_cast<Expr>(S);
if (!E)
return nullptr;
while (true) {
- if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
+ if (const auto *CE = dyn_cast<CastExpr>(E)) {
if (CE->getCastKind() == CK_LValueToRValue) {
// This cast represents the load we're looking for.
break;
}
E = CE->getSubExpr();
- } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E)) {
+ } else if (const auto *B = dyn_cast<BinaryOperator>(E)) {
// Pointer arithmetic: '*(x + 2)' -> 'x') etc.
- if (B->getType()->isPointerType()) {
- if (B->getLHS()->getType()->isPointerType()) {
- E = B->getLHS();
- } else if (B->getRHS()->getType()->isPointerType()) {
- E = B->getRHS();
- } else {
- break;
- }
+ if (const Expr *Inner = peelOffPointerArithmetic(B)) {
+ E = Inner;
} else {
// Probably more arithmetic can be pattern-matched here,
// but for now give up.
break;
}
- } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(E)) {
+ } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf ||
(U->isIncrementDecrementOp() && U->getType()->isPointerType())) {
// Operators '*' and '&' don't actually mean anything.
@@ -94,14 +134,16 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
}
}
// Pattern match for a few useful cases: a[0], p->f, *p etc.
- else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
E = ME->getBase();
- } else if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
+ } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
E = IvarRef->getBase();
- } else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) {
+ } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(E)) {
E = AE->getBase();
- } else if (const ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
+ } else if (const auto *PE = dyn_cast<ParenExpr>(E)) {
E = PE->getSubExpr();
+ } else if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) {
+ E = EWC->getSubExpr();
} else {
// Other arbitrary stuff.
break;
@@ -111,7 +153,7 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
// Special case: remove the final lvalue-to-rvalue cast, but do not recurse
// deeper into the sub-expression. This way we return the lvalue from which
// our pointer rvalue was loaded.
- if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E))
+ if (const auto *CE = dyn_cast<ImplicitCastExpr>(E))
if (CE->getCastKind() == CK_LValueToRValue)
E = CE->getSubExpr();
@@ -120,14 +162,14 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) {
const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
- if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S))
+ if (const auto *BE = dyn_cast<BinaryOperator>(S))
return BE->getRHS();
return nullptr;
}
const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
- if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
+ if (const auto *RS = dyn_cast<ReturnStmt>(S))
return RS->getRetValue();
return nullptr;
}
@@ -136,13 +178,18 @@ const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) {
// Definitions for bug reporter visitors.
//===----------------------------------------------------------------------===//
-std::unique_ptr<PathDiagnosticPiece>
+std::shared_ptr<PathDiagnosticPiece>
BugReporterVisitor::getEndPath(BugReporterContext &BRC,
const ExplodedNode *EndPathNode, BugReport &BR) {
return nullptr;
}
-std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
+void
+BugReporterVisitor::finalizeVisitor(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ BugReport &BR) {}
+
+std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
PathDiagnosticLocation L =
PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
@@ -151,16 +198,465 @@ std::unique_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
// Only add the statement itself as a range if we didn't specify any
// special ranges for this report.
- auto P = llvm::make_unique<PathDiagnosticEventPiece>(
+ auto P = std::make_shared<PathDiagnosticEventPiece>(
L, BR.getDescription(), Ranges.begin() == Ranges.end());
for (SourceRange Range : Ranges)
P->addRange(Range);
- return std::move(P);
+ return P;
+}
+
+/// \return name of the macro inside the location \p Loc.
+static StringRef getMacroName(SourceLocation Loc,
+ BugReporterContext &BRC) {
+ return Lexer::getImmediateMacroName(
+ Loc,
+ BRC.getSourceManager(),
+ BRC.getASTContext().getLangOpts());
+}
+
+/// \return Whether given spelling location corresponds to an expansion
+/// of a function-like macro.
+static bool isFunctionMacroExpansion(SourceLocation Loc,
+ const SourceManager &SM) {
+ if (!Loc.isMacroID())
+ return false;
+ while (SM.isMacroArgExpansion(Loc))
+ Loc = SM.getImmediateExpansionRange(Loc).getBegin();
+ std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc);
+ SrcMgr::SLocEntry SE = SM.getSLocEntry(TLInfo.first);
+ const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion();
+ return EInfo.isFunctionMacroExpansion();
+}
+
+/// \return Whether \c RegionOfInterest was modified at \p N,
+/// where \p ReturnState is a state associated with the return
+/// from the current frame.
+static bool wasRegionOfInterestModifiedAt(
+ const SubRegion *RegionOfInterest,
+ const ExplodedNode *N,
+ SVal ValueAfter) {
+ ProgramStateRef State = N->getState();
+ ProgramStateManager &Mgr = N->getState()->getStateManager();
+
+ if (!N->getLocationAs<PostStore>()
+ && !N->getLocationAs<PostInitializer>()
+ && !N->getLocationAs<PostStmt>())
+ return false;
+
+ // Writing into region of interest.
+ if (auto PS = N->getLocationAs<PostStmt>())
+ if (auto *BO = PS->getStmtAs<BinaryOperator>())
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
+ N->getSVal(BO->getLHS()).getAsRegion()))
+ return true;
+
+ // SVal after the state is possibly different.
+ SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
+ if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() &&
+ (!ValueAtN.isUndef() || !ValueAfter.isUndef()))
+ return true;
+
+ return false;
}
namespace {
+
+/// Put a diagnostic on return statement of all inlined functions
+/// for which the region of interest \p RegionOfInterest was passed into,
+/// but not written inside, and it has caused an undefined read or a null
+/// pointer dereference outside.
+class NoStoreFuncVisitor final : public BugReporterVisitor {
+ const SubRegion *RegionOfInterest;
+ static constexpr const char *DiagnosticsMsg =
+ "Returning without writing to '";
+
+ /// Frames writing into \c RegionOfInterest.
+ /// This visitor generates a note only if a function does not write into
+ /// a region of interest. This information is not immediately available
+ /// by looking at the node associated with the exit from the function
+ /// (usually the return statement). To avoid recomputing the same information
+ /// many times (going up the path for each node and checking whether the
+ /// region was written into) we instead lazily compute the
+ /// stack frames along the path which write into the region of interest.
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
+
+public:
+ NoStoreFuncVisitor(const SubRegion *R) : RegionOfInterest(R) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc)
+ return nullptr;
+
+ CallEventRef<> Call =
+ BRC.getStateManager().getCallEventManager().getCaller(SCtx, State);
+ const PrintingPolicy &PP = BRC.getASTContext().getPrintingPolicy();
+ const SourceManager &SM = BRC.getSourceManager();
+
+ // Region of interest corresponds to an IVar, exiting a method
+ // which could have written into that IVar, but did not.
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call))
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest))
+ if (potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
+ IvarR->getDecl()) &&
+ !isRegionOfInterestModifiedInFrame(N))
+ return notModifiedMemberDiagnostics(
+ Ctx, SM, PP, *CallExitLoc, Call,
+ MC->getReceiverSVal().getAsRegion());
+
+ if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ const MemRegion *ThisRegion = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisRegion)
+ && !CCall->getDecl()->isImplicit()
+ && !isRegionOfInterestModifiedInFrame(N))
+ return notModifiedMemberDiagnostics(Ctx, SM, PP, *CallExitLoc,
+ CCall, ThisRegion);
+ }
+
+ ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
+ for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
+ const ParmVarDecl *PVD = parameters[I];
+ SVal S = Call->getArgSVal(I);
+ unsigned IndirectionLevel = 1;
+ QualType T = PVD->getType();
+ while (const MemRegion *R = S.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(R)
+ && !isPointerToConst(PVD->getType())) {
+
+ if (isRegionOfInterestModifiedInFrame(N))
+ return nullptr;
+
+ return notModifiedParameterDiagnostics(
+ Ctx, SM, PP, *CallExitLoc, Call, PVD, R, IndirectionLevel);
+ }
+ QualType PT = T->getPointeeType();
+ if (PT.isNull() || PT->isVoidType()) break;
+ S = State->getSVal(R, PT);
+ T = PT;
+ IndirectionLevel++;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+
+ /// \return Whether the method declaration \p Parent
+ /// syntactically has a binary operation writing into the ivar \p Ivar.
+ bool potentiallyWritesIntoIvar(const Decl *Parent,
+ const ObjCIvarDecl *Ivar) {
+ using namespace ast_matchers;
+ if (!Parent || !Parent->getBody())
+ return false;
+ StatementMatcher WriteIntoIvarM = binaryOperator(
+ hasOperatorName("="), hasLHS(ignoringParenImpCasts(objcIvarRefExpr(
+ hasDeclaration(equalsNode(Ivar))))));
+ StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
+ auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());
+ return !Matches.empty();
+ }
+
+ /// Check and lazily calculate whether the region of interest is
+ /// modified in the stack frame to which \p N belongs.
+ /// The calculation is cached in FramesModifyingRegion.
+ bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) {
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ if (!FramesModifyingCalculated.count(SCtx))
+ findModifyingFrames(N);
+ return FramesModifyingRegion.count(SCtx);
+ }
+
+
+ /// Write to \c FramesModifyingRegion all stack frames along
+ /// the path in the current stack frame which modify \c RegionOfInterest.
+ void findModifyingFrames(const ExplodedNode *N) {
+ assert(N->getLocationAs<CallExitBegin>());
+ ProgramStateRef LastReturnState = N->getState();
+ SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *OriginalSCtx = Ctx->getStackFrame();
+
+ do {
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (CallExitLoc) {
+ LastReturnState = State;
+ ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
+ }
+
+ FramesModifyingCalculated.insert(
+ N->getLocationContext()->getStackFrame());
+
+ if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) {
+ const StackFrameContext *SCtx = N->getStackFrame();
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifyingRegion.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+ SCtx = SCtx->getParent()->getStackFrame();
+ }
+ }
+
+ // Stop calculation at the call to the current function.
+ if (auto CE = N->getLocationAs<CallEnter>())
+ if (CE->getCalleeContext() == OriginalSCtx)
+ break;
+
+ N = N->getFirstPred();
+ } while (N);
+ }
+
+ /// Get parameters associated with runtime definition in order
+ /// to get the correct parameter name.
+ ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
+ // Use runtime definition, if available.
+ RuntimeDefinition RD = Call->getRuntimeDefinition();
+ if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))
+ return FD->parameters();
+
+ return Call->parameters();
+ }
+
+ /// \return whether \p Ty points to a const type, or is a const reference.
+ bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+ }
+
+ /// \return Diagnostics piece for the member field not modified
+ /// in a given function.
+ std::shared_ptr<PathDiagnosticPiece> notModifiedMemberDiagnostics(
+ const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ CallEventRef<> Call,
+ const MemRegion *ArgRegion) {
+ const char *TopRegionName = isa<ObjCMethodCall>(Call) ? "self" : "this";
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << DiagnosticsMsg;
+ bool out = prettyPrintRegionName(TopRegionName, "->", /*IsReference=*/true,
+ /*IndirectionLevel=*/1, ArgRegion, os, PP);
+
+ // Return nothing if we have failed to pretty-print.
+ if (!out)
+ return nullptr;
+
+ os << "'";
+ PathDiagnosticLocation L =
+ getPathDiagnosticLocation(CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// \return Diagnostics piece for the parameter \p PVD not modified
+ /// in a given function.
+ /// \p IndirectionLevel How many times \c ArgRegion has to be dereferenced
+ /// before we get to the super region of \c RegionOfInterest
+ std::shared_ptr<PathDiagnosticPiece>
+ notModifiedParameterDiagnostics(const LocationContext *Ctx,
+ const SourceManager &SM,
+ const PrintingPolicy &PP,
+ CallExitBegin &CallExitLoc,
+ CallEventRef<> Call,
+ const ParmVarDecl *PVD,
+ const MemRegion *ArgRegion,
+ unsigned IndirectionLevel) {
+ PathDiagnosticLocation L = getPathDiagnosticLocation(
+ CallExitLoc.getReturnStmt(), SM, Ctx, Call);
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << DiagnosticsMsg;
+ bool IsReference = PVD->getType()->isReferenceType();
+ const char *Sep = IsReference && IndirectionLevel == 1 ? "." : "->";
+ bool Success = prettyPrintRegionName(
+ PVD->getQualifiedNameAsString().c_str(),
+ Sep, IsReference, IndirectionLevel, ArgRegion, os, PP);
+
+ // Print the parameter name if the pretty-printing has failed.
+ if (!Success)
+ PVD->printQualifiedName(os);
+ os << "'";
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// \return a path diagnostic location for the optionally
+ /// present return statement \p RS.
+ PathDiagnosticLocation getPathDiagnosticLocation(const ReturnStmt *RS,
+ const SourceManager &SM,
+ const LocationContext *Ctx,
+ CallEventRef<> Call) {
+ if (RS)
+ return PathDiagnosticLocation::createBegin(RS, SM, Ctx);
+ return PathDiagnosticLocation(
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
+ }
+
+ /// Pretty-print region \p ArgRegion starting from parent to \p os.
+ /// \return whether printing has succeeded
+ bool prettyPrintRegionName(StringRef TopRegionName,
+ StringRef Sep,
+ bool IsReference,
+ int IndirectionLevel,
+ const MemRegion *ArgRegion,
+ llvm::raw_svector_ostream &os,
+ const PrintingPolicy &PP) {
+ SmallVector<const MemRegion *, 5> Subregions;
+ const MemRegion *R = RegionOfInterest;
+ while (R != ArgRegion) {
+ if (!(isa<FieldRegion>(R) || isa<CXXBaseObjectRegion>(R) ||
+ isa<ObjCIvarRegion>(R)))
+ return false; // Pattern-matching failed.
+ Subregions.push_back(R);
+ R = cast<SubRegion>(R)->getSuperRegion();
+ }
+ bool IndirectReference = !Subregions.empty();
+
+ if (IndirectReference)
+ IndirectionLevel--; // Due to "->" symbol.
+
+ if (IsReference)
+ IndirectionLevel--; // Due to reference semantics.
+
+ bool ShouldSurround = IndirectReference && IndirectionLevel > 0;
+
+ if (ShouldSurround)
+ os << "(";
+ for (int i = 0; i < IndirectionLevel; i++)
+ os << "*";
+ os << TopRegionName;
+ if (ShouldSurround)
+ os << ")";
+
+ for (auto I = Subregions.rbegin(), E = Subregions.rend(); I != E; ++I) {
+ if (const auto *FR = dyn_cast<FieldRegion>(*I)) {
+ os << Sep;
+ FR->getDecl()->getDeclName().print(os, PP);
+ Sep = ".";
+ } else if (const auto *IR = dyn_cast<ObjCIvarRegion>(*I)) {
+ os << "->";
+ IR->getDecl()->getDeclName().print(os, PP);
+ Sep = ".";
+ } else if (isa<CXXBaseObjectRegion>(*I)) {
+ continue; // Just keep going up to the base region.
+ } else {
+ llvm_unreachable("Previous check has missed an unexpected region");
+ }
+ }
+ return true;
+ }
+};
+
+/// Suppress null-pointer-dereference bugs where dereferenced null was returned
+/// the macro.
+class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor {
+ const SubRegion *RegionOfInterest;
+ const SVal ValueAtDereference;
+
+ // Do not invalidate the reports where the value was modified
+ // after it got assigned to from the macro.
+ bool WasModified = false;
+
+public:
+ MacroNullReturnSuppressionVisitor(const SubRegion *R,
+ const SVal V) : RegionOfInterest(R),
+ ValueAtDereference(V) {}
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ if (WasModified)
+ return nullptr;
+
+ auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
+ if (!BugPoint)
+ return nullptr;
+
+ const SourceManager &SMgr = BRC.getSourceManager();
+ if (auto Loc = matchAssignment(N, BRC)) {
+ if (isFunctionMacroExpansion(*Loc, SMgr)) {
+ std::string MacroName = getMacroName(*Loc, BRC);
+ SourceLocation BugLoc = BugPoint->getStmt()->getLocStart();
+ if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName)
+ BR.markInvalid(getTag(), MacroName.c_str());
+ }
+ }
+
+ if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtDereference))
+ WasModified = true;
+
+ return nullptr;
+ }
+
+ static void addMacroVisitorIfNecessary(
+ const ExplodedNode *N, const MemRegion *R,
+ bool EnableNullFPSuppression, BugReport &BR,
+ const SVal V) {
+ AnalyzerOptions &Options = N->getState()->getStateManager()
+ .getOwningEngine()->getAnalysisManager().options;
+ if (EnableNullFPSuppression && Options.shouldSuppressNullReturnPaths()
+ && V.getAs<Loc>())
+ BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>(
+ R->getAs<SubRegion>(), V));
+ }
+
+ void* getTag() const {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ }
+
+private:
+ /// \return Source location of right hand side of an assignment
+ /// into \c RegionOfInterest, empty optional if none found.
+ Optional<SourceLocation> matchAssignment(const ExplodedNode *N,
+ BugReporterContext &BRC) {
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ ProgramStateRef State = N->getState();
+ auto *LCtx = N->getLocationContext();
+ if (!S)
+ return None;
+
+ if (const auto *DS = dyn_cast<DeclStmt>(S)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
+ if (const Expr *RHS = VD->getInit())
+ if (RegionOfInterest->isSubRegionOf(
+ State->getLValue(VD, LCtx).getAsRegion()))
+ return RHS->getLocStart();
+ } else if (const auto *BO = dyn_cast<BinaryOperator>(S)) {
+ const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion();
+ const Expr *RHS = BO->getRHS();
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) {
+ return RHS->getLocStart();
+ }
+ }
+ return None;
+ }
+};
+
/// Emits an extra note at the return statement of an interesting stack frame.
///
/// The returned value is marked as an interesting value, and if it's null,
@@ -168,19 +664,20 @@ namespace {
///
/// This visitor is intended to be used when another visitor discovers that an
/// interesting value comes from an inlined function call.
-class ReturnVisitor : public BugReporterVisitorImpl<ReturnVisitor> {
+class ReturnVisitor : public BugReporterVisitor {
const StackFrameContext *StackFrame;
enum {
Initial,
MaybeUnsuppress,
Satisfied
- } Mode;
+ } Mode = Initial;
bool EnableNullFPSuppression;
+ bool ShouldInvalidate = true;
public:
ReturnVisitor(const StackFrameContext *Frame, bool Suppressed)
- : StackFrame(Frame), Mode(Initial), EnableNullFPSuppression(Suppressed) {}
+ : StackFrame(Frame), EnableNullFPSuppression(Suppressed) {}
static void *getTag() {
static int Tag = 0;
@@ -235,7 +732,7 @@ public:
// Check the return value.
ProgramStateRef State = Node->getState();
- SVal RetVal = State->getSVal(S, Node->getLocationContext());
+ SVal RetVal = Node->getSVal(S);
// Handle cases where a reference is returned and then immediately used.
if (cast<Expr>(S)->isGLValue())
@@ -274,7 +771,7 @@ public:
if (!SP)
return nullptr;
- const ReturnStmt *Ret = dyn_cast<ReturnStmt>(SP->getStmt());
+ const auto *Ret = dyn_cast<ReturnStmt>(SP->getStmt());
if (!Ret)
return nullptr;
@@ -329,8 +826,7 @@ public:
// If we have counter-suppression enabled, make sure we keep visiting
// future nodes. We want to emit a path note as well, in case
// the report is resurrected as valid later on.
- ExprEngine &Eng = BRC.getBugReporter().getEngine();
- AnalyzerOptions &Options = Eng.getAnalysisManager().options;
+ AnalyzerOptions &Options = BRC.getAnalyzerOptions();
if (EnableNullFPSuppression && hasCounterSuppression(Options))
Mode = MaybeUnsuppress;
@@ -352,8 +848,8 @@ public:
}
} else {
// FIXME: We should have a more generalized location printing mechanism.
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetE))
- if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(DR->getDecl()))
+ if (const auto *DR = dyn_cast<DeclRefExpr>(RetE))
+ if (const auto *DD = dyn_cast<DeclaratorDecl>(DR->getDecl()))
Out << " (loaded from '" << *DD << "')";
}
@@ -368,8 +864,7 @@ public:
visitNodeMaybeUnsuppress(const ExplodedNode *N, const ExplodedNode *PrevN,
BugReporterContext &BRC, BugReport &BR) {
#ifndef NDEBUG
- ExprEngine &Eng = BRC.getBugReporter().getEngine();
- AnalyzerOptions &Options = Eng.getAnalysisManager().options;
+ AnalyzerOptions &Options = BRC.getAnalyzerOptions();
assert(hasCounterSuppression(Options));
#endif
@@ -406,7 +901,7 @@ public:
if (bugreporter::trackNullOrUndefValue(N, ArgE, BR, /*IsArg=*/true,
EnableNullFPSuppression))
- BR.removeInvalidation(ReturnVisitor::getTag(), StackFrame);
+ ShouldInvalidate = false;
// If we /can't/ track the null pointer, we should err on the side of
// false negatives, and continue towards marking this report invalid.
@@ -432,18 +927,16 @@ public:
llvm_unreachable("Invalid visit mode!");
}
- std::unique_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
- const ExplodedNode *N,
- BugReport &BR) override {
- if (EnableNullFPSuppression)
+ void finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *N,
+ BugReport &BR) override {
+ if (EnableNullFPSuppression && ShouldInvalidate)
BR.markInvalid(ReturnVisitor::getTag(), StackFrame);
- return nullptr;
}
};
-} // end anonymous namespace
+} // namespace
-void FindLastStoreBRVisitor ::Profile(llvm::FoldingSetNodeID &ID) const {
+void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
ID.AddPointer(&tag);
ID.AddPointer(R);
@@ -466,7 +959,7 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
return false;
const MemSpaceRegion *VarSpace = VR->getMemorySpace();
- const StackSpaceRegion *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
+ const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
if (!FrameSpace) {
// If we ever directly evaluate global DeclStmts, this assertion will be
// invalid, but this still seems preferable to silently accepting an
@@ -477,14 +970,131 @@ static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
assert(VR->getDecl()->hasLocalStorage());
const LocationContext *LCtx = N->getLocationContext();
- return FrameSpace->getStackFrame() == LCtx->getCurrentStackFrame();
+ return FrameSpace->getStackFrame() == LCtx->getStackFrame();
+}
+
+/// Show diagnostics for initializing or declaring a region \p R with a bad value.
+static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream &os,
+ const MemRegion *R, SVal V, const DeclStmt *DS) {
+ if (R->canPrintPretty()) {
+ R->printPretty(os);
+ os << " ";
+ }
+
+ if (V.getAs<loc::ConcreteInt>()) {
+ bool b = false;
+ if (R->isBoundable()) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
+ os << action << "nil";
+ b = true;
+ }
+ }
+ }
+ if (!b)
+ os << action << "a null pointer value";
+
+ } else if (auto CVal = V.getAs<nonloc::ConcreteInt>()) {
+ os << action << CVal->getValue();
+ } else if (DS) {
+ if (V.isUndef()) {
+ if (isa<VarRegion>(R)) {
+ const auto *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (VD->getInit()) {
+ os << (R->canPrintPretty() ? "initialized" : "Initializing")
+ << " to a garbage value";
+ } else {
+ os << (R->canPrintPretty() ? "declared" : "Declaring")
+ << " without an initial value";
+ }
+ }
+ } else {
+ os << (R->canPrintPretty() ? "initialized" : "Initialized")
+ << " here";
+ }
+ }
+}
+
+/// Display diagnostics for passing bad region as a parameter.
+static void showBRParamDiagnostics(llvm::raw_svector_ostream& os,
+ const VarRegion *VR,
+ SVal V) {
+ const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+
+ os << "Passing ";
+
+ if (V.getAs<loc::ConcreteInt>()) {
+ if (Param->getType()->isObjCObjectPointerType())
+ os << "nil object reference";
+ else
+ os << "null pointer value";
+ } else if (V.isUndef()) {
+ os << "uninitialized value";
+ } else if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
+ os << "the value " << CI->getValue();
+ } else {
+ os << "value";
+ }
+
+ // Printed parameter indexes are 1-based, not 0-based.
+ unsigned Idx = Param->getFunctionScopeIndex() + 1;
+ os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
+ if (VR->canPrintPretty()) {
+ os << " ";
+ VR->printPretty(os);
+ }
+}
+
+/// Show default diagnostics for storing bad region.
+static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os,
+ const MemRegion *R,
+ SVal V) {
+ if (V.getAs<loc::ConcreteInt>()) {
+ bool b = false;
+ if (R->isBoundable()) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
+ os << "nil object reference stored";
+ b = true;
+ }
+ }
+ }
+ if (!b) {
+ if (R->canPrintPretty())
+ os << "Null pointer value stored";
+ else
+ os << "Storing null pointer value";
+ }
+
+ } else if (V.isUndef()) {
+ if (R->canPrintPretty())
+ os << "Uninitialized value stored";
+ else
+ os << "Storing uninitialized value";
+
+ } else if (auto CV = V.getAs<nonloc::ConcreteInt>()) {
+ if (R->canPrintPretty())
+ os << "The value " << CV->getValue() << " is assigned";
+ else
+ os << "Assigning " << CV->getValue();
+
+ } else {
+ if (R->canPrintPretty())
+ os << "Value assigned";
+ else
+ os << "Assigning value";
+ }
+
+ if (R->canPrintPretty()) {
+ os << " to ";
+ R->printPretty(os);
+ }
}
std::shared_ptr<PathDiagnosticPiece>
FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
const ExplodedNode *Pred,
BugReporterContext &BRC, BugReport &BR) {
-
if (Satisfied)
return nullptr;
@@ -493,7 +1103,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
bool IsParam = false;
// First see if we reached the declaration of the region.
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ if (const auto *VR = dyn_cast<VarRegion>(R)) {
if (isInitializationOfVar(Pred, VR)) {
StoreSite = Pred;
InitE = VR->getDecl()->getInit();
@@ -539,8 +1149,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
// 'this' should never be NULL, but this visitor isn't just for NULL and
// UndefinedVal.)
if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
- const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl());
+ if (const auto *VR = dyn_cast<VarRegion>(R)) {
+ const auto *Param = cast<ParmVarDecl>(VR->getDecl());
ProgramStateManager &StateMgr = BRC.getStateManager();
CallEventManager &CallMgr = StateMgr.getCallEventManager();
@@ -554,7 +1164,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
// If this is a CXXTempObjectRegion, the Expr responsible for its creation
// is wrapped inside of it.
- if (const CXXTempObjectRegion *TmpR = dyn_cast<CXXTempObjectRegion>(R))
+ if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(R))
InitE = TmpR->getExpr();
}
@@ -584,8 +1194,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
const Stmt *S = PS->getStmt();
const char *action = nullptr;
- const DeclStmt *DS = dyn_cast<DeclStmt>(S);
- const VarRegion *VR = dyn_cast<VarRegion>(R);
+ const auto *DS = dyn_cast<DeclStmt>(S);
+ const auto *VR = dyn_cast<VarRegion>(R);
if (DS) {
action = R->canPrintPretty() ? "initialized to " :
@@ -596,8 +1206,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (VR) {
// See if we can get the BlockVarRegion.
ProgramStateRef State = StoreSite->getState();
- SVal V = State->getSVal(S, PS->getLocationContext());
- if (const BlockDataRegion *BDR =
+ SVal V = StoreSite->getSVal(S);
+ if (const auto *BDR =
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
if (Optional<KnownSVal> KV =
@@ -608,122 +1218,16 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
}
}
}
+ if (action)
+ showBRDiagnostics(action, os, R, V, DS);
- if (action) {
- if (R->canPrintPretty()) {
- R->printPretty(os);
- os << " ";
- }
-
- if (V.getAs<loc::ConcreteInt>()) {
- bool b = false;
- if (R->isBoundable()) {
- if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
- if (TR->getValueType()->isObjCObjectPointerType()) {
- os << action << "nil";
- b = true;
- }
- }
- }
-
- if (!b)
- os << action << "a null pointer value";
- } else if (Optional<nonloc::ConcreteInt> CVal =
- V.getAs<nonloc::ConcreteInt>()) {
- os << action << CVal->getValue();
- }
- else if (DS) {
- if (V.isUndef()) {
- if (isa<VarRegion>(R)) {
- const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
- if (VD->getInit()) {
- os << (R->canPrintPretty() ? "initialized" : "Initializing")
- << " to a garbage value";
- } else {
- os << (R->canPrintPretty() ? "declared" : "Declaring")
- << " without an initial value";
- }
- }
- }
- else {
- os << (R->canPrintPretty() ? "initialized" : "Initialized")
- << " here";
- }
- }
- }
} else if (StoreSite->getLocation().getAs<CallEnter>()) {
- if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
- const ParmVarDecl *Param = cast<ParmVarDecl>(VR->getDecl());
-
- os << "Passing ";
-
- if (V.getAs<loc::ConcreteInt>()) {
- if (Param->getType()->isObjCObjectPointerType())
- os << "nil object reference";
- else
- os << "null pointer value";
- } else if (V.isUndef()) {
- os << "uninitialized value";
- } else if (Optional<nonloc::ConcreteInt> CI =
- V.getAs<nonloc::ConcreteInt>()) {
- os << "the value " << CI->getValue();
- } else {
- os << "value";
- }
-
- // Printed parameter indexes are 1-based, not 0-based.
- unsigned Idx = Param->getFunctionScopeIndex() + 1;
- os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
- if (R->canPrintPretty()) {
- os << " ";
- R->printPretty(os);
- }
- }
+ if (const auto *VR = dyn_cast<VarRegion>(R))
+ showBRParamDiagnostics(os, VR, V);
}
- if (os.str().empty()) {
- if (V.getAs<loc::ConcreteInt>()) {
- bool b = false;
- if (R->isBoundable()) {
- if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
- if (TR->getValueType()->isObjCObjectPointerType()) {
- os << "nil object reference stored";
- b = true;
- }
- }
- }
- if (!b) {
- if (R->canPrintPretty())
- os << "Null pointer value stored";
- else
- os << "Storing null pointer value";
- }
-
- } else if (V.isUndef()) {
- if (R->canPrintPretty())
- os << "Uninitialized value stored";
- else
- os << "Storing uninitialized value";
-
- } else if (Optional<nonloc::ConcreteInt> CV =
- V.getAs<nonloc::ConcreteInt>()) {
- if (R->canPrintPretty())
- os << "The value " << CV->getValue() << " is assigned";
- else
- os << "Assigning " << CV->getValue();
-
- } else {
- if (R->canPrintPretty())
- os << "Value assigned";
- else
- os << "Assigning value";
- }
-
- if (R->canPrintPretty()) {
- os << " to ";
- R->printPretty(os);
- }
- }
+ if (os.str().empty())
+ showBRDefaultDiagnostics(os, R, V);
// Construct a new PathDiagnosticPiece.
ProgramPoint P = StoreSite->getLocation();
@@ -778,7 +1282,6 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
// Check if in the previous state it was feasible for this constraint
// to *not* be true.
if (isUnderconstrained(PrevN)) {
-
IsSatisfied = true;
// As a sanity check, make sure that the negation of the constraint
@@ -816,20 +1319,20 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
SuppressInlineDefensiveChecksVisitor::
SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
- : V(Value), IsSatisfied(false), IsTrackingTurnedOn(false) {
-
- // Check if the visitor is disabled.
- SubEngine *Eng = N->getState()->getStateManager().getOwningEngine();
- assert(Eng && "Cannot file a bug report without an owning engine");
- AnalyzerOptions &Options = Eng->getAnalysisManager().options;
- if (!Options.shouldSuppressInlinedDefensiveChecks())
- IsSatisfied = true;
+ : V(Value) {
+ // Check if the visitor is disabled.
+ SubEngine *Eng = N->getState()->getStateManager().getOwningEngine();
+ assert(Eng && "Cannot file a bug report without an owning engine");
+ AnalyzerOptions &Options = Eng->getAnalysisManager().options;
+ if (!Options.shouldSuppressInlinedDefensiveChecks())
+ IsSatisfied = true;
- assert(N->getState()->isNull(V).isConstrainedTrue() &&
- "The visitor only tracks the cases where V is constrained to 0");
+ assert(N->getState()->isNull(V).isConstrainedTrue() &&
+ "The visitor only tracks the cases where V is constrained to 0");
}
-void SuppressInlineDefensiveChecksVisitor::Profile(FoldingSetNodeID &ID) const {
+void SuppressInlineDefensiveChecksVisitor::Profile(
+ llvm::FoldingSetNodeID &ID) const {
static int id = 0;
ID.AddPointer(&id);
ID.Add(V);
@@ -878,10 +1381,6 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
if (!BugPoint)
return nullptr;
- SourceLocation BugLoc = BugPoint->getStmt()->getLocStart();
- if (BugLoc.isMacroID())
- return nullptr;
-
ProgramPoint CurPoint = Succ->getLocation();
const Stmt *CurTerminatorStmt = nullptr;
if (auto BE = CurPoint.getAs<BlockEdge>()) {
@@ -902,14 +1401,14 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
SourceLocation TerminatorLoc = CurTerminatorStmt->getLocStart();
if (TerminatorLoc.isMacroID()) {
- const SourceManager &SMgr = BRC.getSourceManager();
- std::pair<FileID, unsigned> TLInfo = SMgr.getDecomposedLoc(TerminatorLoc);
- SrcMgr::SLocEntry SE = SMgr.getSLocEntry(TLInfo.first);
- const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion();
- if (EInfo.isFunctionMacroExpansion()) {
+ SourceLocation BugLoc = BugPoint->getStmt()->getLocStart();
+
+ // Suppress reports unless we are in that same macro.
+ if (!BugLoc.isMacroID() ||
+ getMacroName(BugLoc, BRC) != getMacroName(TerminatorLoc, BRC)) {
BR.markInvalid("Suppress Macro IDC", CurLC);
- return nullptr;
}
+ return nullptr;
}
}
return nullptr;
@@ -917,8 +1416,8 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
static const MemRegion *getLocationRegionIfReference(const Expr *E,
const ExplodedNode *N) {
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(E)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
if (!VD->getType()->isReferenceType())
return nullptr;
ProgramStateManager &StateMgr = N->getState()->getStateManager();
@@ -939,12 +1438,12 @@ static const MemRegion *getLocationRegionIfReference(const Expr *E,
static const Expr *peelOffOuterExpr(const Expr *Ex,
const ExplodedNode *N) {
Ex = Ex->IgnoreParenCasts();
- if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(Ex))
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(Ex))
return peelOffOuterExpr(EWC->getSubExpr(), N);
- if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(Ex))
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex))
return peelOffOuterExpr(OVE->getSourceExpr(), N);
- if (auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) {
- auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm());
+ if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) {
+ const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm());
if (PropRef && PropRef->isMessagingGetter()) {
const Expr *GetterMessageSend =
POE->getSemanticExpr(POE->getNumSemanticExprs() - 1);
@@ -954,7 +1453,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex,
}
// Peel off the ternary operator.
- if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(Ex)) {
+ if (const auto *CO = dyn_cast<ConditionalOperator>(Ex)) {
// Find a node where the branching occurred and find out which branch
// we took (true/false) by looking at the ExplodedGraph.
const ExplodedNode *NI = N;
@@ -975,6 +1474,68 @@ static const Expr *peelOffOuterExpr(const Expr *Ex,
NI = NI->getFirstPred();
} while (NI);
}
+
+ if (auto *BO = dyn_cast<BinaryOperator>(Ex))
+ if (const Expr *SubEx = peelOffPointerArithmetic(BO))
+ return peelOffOuterExpr(SubEx, N);
+
+ return Ex;
+}
+
+/// Walk through nodes until we get one that matches the statement exactly.
+/// Alternately, if we hit a known lvalue for the statement, we know we've
+/// gone too far (though we can likely track the lvalue better anyway).
+static const ExplodedNode* findNodeForStatement(const ExplodedNode *N,
+ const Stmt *S,
+ const Expr *Inner) {
+ do {
+ const ProgramPoint &pp = N->getLocation();
+ if (auto ps = pp.getAs<StmtPoint>()) {
+ if (ps->getStmt() == S || ps->getStmt() == Inner)
+ break;
+ } else if (auto CEE = pp.getAs<CallExitEnd>()) {
+ if (CEE->getCalleeContext()->getCallSite() == S ||
+ CEE->getCalleeContext()->getCallSite() == Inner)
+ break;
+ }
+ N = N->getFirstPred();
+ } while (N);
+ return N;
+}
+
+/// Find the ExplodedNode where the lvalue (the value of 'Ex')
+/// was computed.
+static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
+ const Expr *Inner) {
+ while (N) {
+ if (auto P = N->getLocation().getAs<PostStmt>()) {
+ if (P->getStmt() == Inner)
+ break;
+ }
+ N = N->getFirstPred();
+ }
+ assert(N && "Unable to find the lvalue node.");
+ return N;
+}
+
+/// Performing operator `&' on an lvalue expression is essentially a no-op.
+/// Then, if we are taking addresses of fields or elements, these are also
+/// unlikely to matter.
+static const Expr* peelOfOuterAddrOf(const Expr* Ex) {
+ Ex = Ex->IgnoreParenCasts();
+
+ // FIXME: There's a hack in our Store implementation that always computes
+ // field offsets around null pointers as if they are always equal to 0.
+ // The idea here is to report accesses to fields as null dereferences
+ // even though the pointer value that's being dereferenced is actually
+ // the offset of the field rather than exactly 0.
+ // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
+ // This code interacts heavily with this hack; otherwise the value
+ // would not be null at all for most fields, so we'd be unable to track it.
+ if (const auto *Op = dyn_cast<UnaryOperator>(Ex))
+ if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue())
+ if (const Expr *DerefEx = bugreporter::getDerefExpr(Op->getSubExpr()))
+ return DerefEx;
return Ex;
}
@@ -985,52 +1546,23 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
if (!S || !N)
return false;
- if (const Expr *Ex = dyn_cast<Expr>(S))
+ if (const auto *Ex = dyn_cast<Expr>(S))
S = peelOffOuterExpr(Ex, N);
const Expr *Inner = nullptr;
- if (const Expr *Ex = dyn_cast<Expr>(S)) {
+ if (const auto *Ex = dyn_cast<Expr>(S)) {
+ Ex = peelOfOuterAddrOf(Ex);
Ex = Ex->IgnoreParenCasts();
- // Performing operator `&' on an lvalue expression is essentially a no-op.
- // Then, if we are taking addresses of fields or elements, these are also
- // unlikely to matter.
- // FIXME: There's a hack in our Store implementation that always computes
- // field offsets around null pointers as if they are always equal to 0.
- // The idea here is to report accesses to fields as null dereferences
- // even though the pointer value that's being dereferenced is actually
- // the offset of the field rather than exactly 0.
- // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
- // This code interacts heavily with this hack; otherwise the value
- // would not be null at all for most fields, so we'd be unable to track it.
- if (const auto *Op = dyn_cast<UnaryOperator>(Ex))
- if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue())
- if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr()))
- Ex = DerefEx;
-
- if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)))
+ if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex)
+ || CallEvent::isCallStmt(Ex)))
Inner = Ex;
}
if (IsArg && !Inner) {
assert(N->getLocation().getAs<CallEnter>() && "Tracking arg but not at call");
} else {
- // Walk through nodes until we get one that matches the statement exactly.
- // Alternately, if we hit a known lvalue for the statement, we know we've
- // gone too far (though we can likely track the lvalue better anyway).
- do {
- const ProgramPoint &pp = N->getLocation();
- if (Optional<StmtPoint> ps = pp.getAs<StmtPoint>()) {
- if (ps->getStmt() == S || ps->getStmt() == Inner)
- break;
- } else if (Optional<CallExitEnd> CEE = pp.getAs<CallExitEnd>()) {
- if (CEE->getCalleeContext()->getCallSite() == S ||
- CEE->getCalleeContext()->getCallSite() == Inner)
- break;
- }
- N = N->getFirstPred();
- } while (N);
-
+ N = findNodeForStatement(N, S, Inner);
if (!N)
return false;
}
@@ -1041,51 +1573,43 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
// At this point in the path, the receiver should be live since we are at the
// message send expr. If it is nil, start tracking it.
if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(S, N))
- trackNullOrUndefValue(N, Receiver, report, false, EnableNullFPSuppression);
-
+ trackNullOrUndefValue(N, Receiver, report, /* IsArg=*/ false,
+ EnableNullFPSuppression);
// See if the expression we're interested refers to a variable.
// If so, we can track both its contents and constraints on its value.
if (Inner && ExplodedGraph::isInterestingLValueExpr(Inner)) {
- const MemRegion *R = nullptr;
-
- // Find the ExplodedNode where the lvalue (the value of 'Ex')
- // was computed. We need this for getting the location value.
- const ExplodedNode *LVNode = N;
- while (LVNode) {
- if (Optional<PostStmt> P = LVNode->getLocation().getAs<PostStmt>()) {
- if (P->getStmt() == Inner)
- break;
- }
- LVNode = LVNode->getFirstPred();
- }
- assert(LVNode && "Unable to find the lvalue node.");
+ const ExplodedNode *LVNode = findNodeForExpression(N, Inner);
ProgramStateRef LVState = LVNode->getState();
- SVal LVal = LVState->getSVal(Inner, LVNode->getLocationContext());
-
- if (LVState->isNull(LVal).isConstrainedTrue()) {
- // In case of C++ references, we want to differentiate between a null
- // reference and reference to null pointer.
- // If the LVal is null, check if we are dealing with null reference.
- // For those, we want to track the location of the reference.
- if (const MemRegion *RR = getLocationRegionIfReference(Inner, N))
- R = RR;
- } else {
- R = LVState->getSVal(Inner, LVNode->getLocationContext()).getAsRegion();
-
- // If this is a C++ reference to a null pointer, we are tracking the
- // pointer. In addition, we should find the store at which the reference
- // got initialized.
- if (const MemRegion *RR = getLocationRegionIfReference(Inner, N)) {
- if (Optional<KnownSVal> KV = LVal.getAs<KnownSVal>())
- report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ SVal LVal = LVNode->getSVal(Inner);
+
+ const MemRegion *RR = getLocationRegionIfReference(Inner, N);
+ bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue();
+
+ // If this is a C++ reference to a null pointer, we are tracking the
+ // pointer. In addition, we should find the store at which the reference
+ // got initialized.
+ if (RR && !LVIsNull) {
+ if (auto KV = LVal.getAs<KnownSVal>())
+ report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
*KV, RR, EnableNullFPSuppression));
- }
}
+ // In case of C++ references, we want to differentiate between a null
+ // reference and reference to null pointer.
+ // If the LVal is null, check if we are dealing with null reference.
+ // For those, we want to track the location of the reference.
+ const MemRegion *R = (RR && LVIsNull) ? RR :
+ LVNode->getSVal(Inner).getAsRegion();
+
if (R) {
// Mark both the variable region and its contents as interesting.
SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
+ report.addVisitor(
+ llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R)));
+
+ MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
+ N, R, EnableNullFPSuppression, report, V);
report.markInteresting(R);
report.markInteresting(V);
@@ -1094,21 +1618,21 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
// If the contents are symbolic, find out when they became null.
if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true))
report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
- V.castAs<DefinedSVal>(), false));
+ V.castAs<DefinedSVal>(), false));
// Add visitor, which will suppress inline defensive checks.
- if (Optional<DefinedSVal> DV = V.getAs<DefinedSVal>()) {
+ if (auto DV = V.getAs<DefinedSVal>()) {
if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() &&
EnableNullFPSuppression) {
report.addVisitor(
llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV,
- LVNode));
+ LVNode));
}
}
- if (Optional<KnownSVal> KV = V.getAs<KnownSVal>())
+ if (auto KV = V.getAs<KnownSVal>())
report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, R, EnableNullFPSuppression));
+ *KV, R, EnableNullFPSuppression));
return true;
}
}
@@ -1119,7 +1643,7 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
// If the value came from an inlined function call, we should at least make
// sure that function isn't pruned in our output.
- if (const Expr *E = dyn_cast<Expr>(S))
+ if (const auto *E = dyn_cast<Expr>(S))
S = E->IgnoreParenCasts();
ReturnVisitor::addVisitorIfNecessary(N, S, report, EnableNullFPSuppression);
@@ -1128,40 +1652,40 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
// base value that was dereferenced.
// assert(!V.isUnknownOrUndef());
// Is it a symbolic value?
- if (Optional<loc::MemRegionVal> L = V.getAs<loc::MemRegionVal>()) {
+ if (auto L = V.getAs<loc::MemRegionVal>()) {
+ report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion()));
+
// At this point we are dealing with the region's LValue.
// However, if the rvalue is a symbolic region, we should track it as well.
// Try to use the correct type when looking up the value.
SVal RVal;
- if (const Expr *E = dyn_cast<Expr>(S))
+ if (const auto *E = dyn_cast<Expr>(S))
RVal = state->getRawSVal(L.getValue(), E->getType());
else
RVal = state->getSVal(L->getRegion());
- report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion()));
- if (Optional<KnownSVal> KV = RVal.getAs<KnownSVal>())
+ if (auto KV = RVal.getAs<KnownSVal>())
report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, L->getRegion(), EnableNullFPSuppression));
+ *KV, L->getRegion(), EnableNullFPSuppression));
const MemRegion *RegionRVal = RVal.getAsRegion();
if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) {
report.markInteresting(RegionRVal);
report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
- loc::MemRegionVal(RegionRVal), false));
+ loc::MemRegionVal(RegionRVal), false));
}
}
-
return true;
}
const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
const ExplodedNode *N) {
- const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S);
+ const auto *ME = dyn_cast<ObjCMessageExpr>(S);
if (!ME)
return nullptr;
if (const Expr *Receiver = ME->getInstanceReceiver()) {
ProgramStateRef state = N->getState();
- SVal V = state->getSVal(Receiver, N->getLocationContext());
+ SVal V = N->getSVal(Receiver);
if (state->isNull(V).isConstrainedTrue())
return Receiver;
}
@@ -1184,7 +1708,7 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
llvm::SmallString<256> Buf;
llvm::raw_svector_ostream OS(Buf);
- if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
+ if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
OS << "'";
ME->getSelector().print(OS);
OS << "' not called";
@@ -1217,16 +1741,15 @@ void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
const Stmt *Head = WorkList.front();
WorkList.pop_front();
- ProgramStateRef state = N->getState();
- ProgramStateManager &StateMgr = state->getStateManager();
+ ProgramStateManager &StateMgr = N->getState()->getStateManager();
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Head)) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
const VarRegion *R =
StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
// What did we load?
- SVal V = state->getSVal(S, N->getLocationContext());
+ SVal V = N->getSVal(S);
if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) {
// Register a new visitor with the BugReport.
@@ -1267,7 +1790,6 @@ std::shared_ptr<PathDiagnosticPiece>
ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
const ExplodedNode *Prev,
BugReporterContext &BRC, BugReport &BR) {
-
ProgramPoint progPoint = N->getLocation();
ProgramStateRef CurrentState = N->getState();
ProgramStateRef PrevState = Prev->getState();
@@ -1289,11 +1811,8 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
}
if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) {
- // FIXME: Assuming that BugReporter is a GRBugReporter is a layering
- // violation.
const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags =
- cast<GRBugReporter>(BRC.getBugReporter()).
- getEngine().geteagerlyAssumeBinOpBifurcationTags();
+ ExprEngine::geteagerlyAssumeBinOpBifurcationTags();
const ProgramPointTag *tag = PS->getTag();
if (tag == tags.first)
@@ -1389,7 +1908,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue,
return P;
break;
case Stmt::UnaryOperatorClass: {
- const UnaryOperator *UO = cast<UnaryOperator>(CondTmp);
+ const auto *UO = cast<UnaryOperator>(CondTmp);
if (UO->getOpcode() == UO_LNot) {
tookTrueTmp = !tookTrueTmp;
CondTmp = UO->getSubExpr();
@@ -1432,7 +1951,6 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
isa<CXXBoolLiteralExpr>(Ex) ||
isa<IntegerLiteral>(Ex) ||
isa<FloatingLiteral>(Ex))) {
-
StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart,
BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd,
@@ -1463,7 +1981,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
}
}
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Ex)) {
const bool quotes = isa<VarDecl>(DR->getDecl());
if (quotes) {
Out << '\'';
@@ -1487,7 +2005,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
return quotes;
}
- if (const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(Ex)) {
+ if (const auto *IL = dyn_cast<IntegerLiteral>(Ex)) {
QualType OriginalTy = OriginalExpr->getType();
if (OriginalTy->isPointerType()) {
if (IL->getValue() == 0) {
@@ -1513,7 +2031,6 @@ std::shared_ptr<PathDiagnosticPiece>
ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
const bool tookTrue, BugReporterContext &BRC,
BugReport &R, const ExplodedNode *N) {
-
bool shouldInvert = false;
Optional<bool> shouldPrune;
@@ -1618,8 +2135,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx);
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) {
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
const ProgramState *state = N->getState().get();
if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
if (report.isInteresting(R))
@@ -1635,8 +2152,7 @@ std::shared_ptr<PathDiagnosticPiece>
ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
const bool tookTrue, BugReporterContext &BRC,
BugReport &report, const ExplodedNode *N) {
-
- const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
if (!VD)
return nullptr;
@@ -1684,14 +2200,11 @@ bool ConditionBRVisitor::isPieceMessageGeneric(
Piece->getString() == GenericFalseMessage;
}
-std::unique_ptr<PathDiagnosticPiece>
-LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
- const ExplodedNode *N,
- BugReport &BR) {
+void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
+ BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) {
// Here we suppress false positives coming from system headers. This list is
// based on known issues.
- ExprEngine &Eng = BRC.getBugReporter().getEngine();
- AnalyzerOptions &Options = Eng.getAnalysisManager().options;
+ AnalyzerOptions &Options = BRC.getAnalyzerOptions();
const Decl *D = N->getLocationContext()->getDecl();
if (AnalysisDeclContext::isInStdNamespace(D)) {
@@ -1701,8 +2214,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// TR1, Boost, or llvm/ADT.
if (Options.shouldSuppressFromCXXStandardLibrary()) {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
-
+ return;
} else {
// If the complete 'std' suppression is not enabled, suppress reports
// from the 'std' namespace that are known to produce false positives.
@@ -1710,27 +2222,27 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// The analyzer issues a false use-after-free when std::list::pop_front
// or std::list::pop_back are called multiple times because we cannot
// reason about the internal invariants of the data structure.
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
const CXXRecordDecl *CD = MD->getParent();
if (CD->getName() == "list") {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
+ return;
}
}
// The analyzer issues a false positive when the constructor of
// std::__independent_bits_engine from algorithms is used.
- if (const CXXConstructorDecl *MD = dyn_cast<CXXConstructorDecl>(D)) {
+ if (const auto *MD = dyn_cast<CXXConstructorDecl>(D)) {
const CXXRecordDecl *CD = MD->getParent();
if (CD->getName() == "__independent_bits_engine") {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
+ return;
}
}
for (const LocationContext *LCtx = N->getLocationContext(); LCtx;
LCtx = LCtx->getParent()) {
- const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+ const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
if (!MD)
continue;
@@ -1743,7 +2255,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// data structure.
if (CD->getName() == "basic_string") {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
+ return;
}
// The analyzer issues a false positive on
@@ -1751,7 +2263,7 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
// because it does not reason properly about temporary destructors.
if (CD->getName() == "shared_ptr") {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
+ return;
}
}
}
@@ -1765,18 +2277,15 @@ LikelyFalsePositiveSuppressionBRVisitor::getEndPath(BugReporterContext &BRC,
Loc = Loc.getSpellingLoc();
if (SM.getFilename(Loc).endswith("sys/queue.h")) {
BR.markInvalid(getTag(), nullptr);
- return nullptr;
+ return;
}
}
-
- return nullptr;
}
std::shared_ptr<PathDiagnosticPiece>
UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
const ExplodedNode *PrevN,
BugReporterContext &BRC, BugReport &BR) {
-
ProgramStateRef State = N->getState();
ProgramPoint ProgLoc = N->getLocation();
@@ -1789,18 +2298,17 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
CallEventRef<> Call = CEMgr.getCaller(CEnter->getCalleeContext(), State);
unsigned Idx = 0;
- ArrayRef<ParmVarDecl*> parms = Call->parameters();
+ ArrayRef<ParmVarDecl *> parms = Call->parameters();
- for (ArrayRef<ParmVarDecl*>::iterator I = parms.begin(), E = parms.end();
- I != E; ++I, ++Idx) {
+ for (const auto ParamDecl : parms) {
const MemRegion *ArgReg = Call->getArgSVal(Idx).getAsRegion();
+ ++Idx;
// Are we tracking the argument or its subregion?
- if ( !ArgReg || (ArgReg != R && !R->isSubRegionOf(ArgReg->StripCasts())))
+ if ( !ArgReg || !R->isSubRegionOf(ArgReg->StripCasts()))
continue;
// Check the function parameter type.
- const ParmVarDecl *ParamDecl = *I;
assert(ParamDecl && "Formal parameter has no decl?");
QualType T = ParamDecl->getType();
@@ -1832,7 +2340,7 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Satisfied)
return nullptr;
- auto Edge = Succ->getLocation().getAs<BlockEdge>();
+ const auto Edge = Succ->getLocation().getAs<BlockEdge>();
if (!Edge.hasValue())
return nullptr;
@@ -1859,7 +2367,7 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ,
const auto Param =
State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx));
const auto This =
- State->getSVal(SVB.getCXXThis(Met, LCtx->getCurrentStackFrame()));
+ State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame()));
auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager());
@@ -1877,3 +2385,82 @@ CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ,
return std::move(Piece);
}
+
+std::shared_ptr<PathDiagnosticPiece>
+TaintBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN,
+ BugReporterContext &BRC, BugReport &BR) {
+
+ // Find the ExplodedNode where the taint was first introduced
+ if (!N->getState()->isTainted(V) || PrevN->getState()->isTainted(V))
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const LocationContext *NCtx = N->getLocationContext();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx);
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here");
+}
+
+FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
+ : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
+
+void FalsePositiveRefutationBRVisitor::finalizeVisitor(
+ BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
+ // Collect new constraints
+ VisitNode(EndPathNode, nullptr, BRC, BR);
+
+ // Create a refutation manager
+ std::unique_ptr<SMTSolver> RefutationSolver = CreateZ3Solver();
+ ASTContext &Ctx = BRC.getASTContext();
+
+ // Add constraints to the solver
+ for (const auto &I : Constraints) {
+ SymbolRef Sym = I.first;
+
+ SMTExprRef Constraints = RefutationSolver->fromBoolean(false);
+ for (const auto &Range : I.second) {
+ Constraints = RefutationSolver->mkOr(
+ Constraints,
+ RefutationSolver->getRangeExpr(Ctx, Sym, Range.From(), Range.To(),
+ /*InRange=*/true));
+ }
+ RefutationSolver->addConstraint(Constraints);
+ }
+
+ // And check for satisfiability
+ if (RefutationSolver->check().isConstrainedFalse())
+ BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N,
+ const ExplodedNode *PrevN,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ // Collect new constraints
+ const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CF =
+ N->getState()->get_context<ConstraintRange>();
+
+ // Add constraints if we don't have them yet
+ for (auto const &C : NewCs) {
+ const SymbolRef &Sym = C.first;
+ if (!Constraints.contains(Sym)) {
+ Constraints = CF.add(Constraints, Sym, C.second);
+ }
+ }
+
+ return nullptr;
+}
+
+void FalsePositiveRefutationBRVisitor::Profile(
+ llvm::FoldingSetNodeID &ID) const {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+}
diff --git a/lib/StaticAnalyzer/Core/CMakeLists.txt b/lib/StaticAnalyzer/Core/CMakeLists.txt
index 5ac4f942f373..de994b598e59 100644
--- a/lib/StaticAnalyzer/Core/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Core/CMakeLists.txt
@@ -13,7 +13,6 @@ add_clang_library(clangStaticAnalyzerCore
AnalyzerOptions.cpp
BasicValueFactory.cpp
BlockCounter.cpp
- IssueHash.cpp
BugReporter.cpp
BugReporterVisitors.cpp
CallEvent.cpp
@@ -35,6 +34,7 @@ add_clang_library(clangStaticAnalyzerCore
ExprEngineObjC.cpp
FunctionSummary.cpp
HTMLDiagnostics.cpp
+ IssueHash.cpp
LoopUnrolling.cpp
LoopWidening.cpp
MemRegion.cpp
@@ -48,9 +48,11 @@ add_clang_library(clangStaticAnalyzerCore
SVals.cpp
SimpleConstraintManager.cpp
SimpleSValBuilder.cpp
+ SMTConstraintManager.cpp
Store.cpp
SubEngine.cpp
SymbolManager.cpp
+ WorkList.cpp
Z3ConstraintManager.cpp
LINK_LIBS
@@ -58,6 +60,7 @@ add_clang_library(clangStaticAnalyzerCore
clangASTMatchers
clangAnalysis
clangBasic
+ clangCrossTU
clangLex
clangRewrite
${Z3_LINK_FILES}
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 776369be9dba..8db7b06f186d 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -1,4 +1,4 @@
-//===- Calls.cpp - Wrapper for all function and method calls ------*- C++ -*--//
+//===- CallEvent.cpp - Wrapper for all function and method calls ----------===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,14 +14,52 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.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/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
-#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <utility>
#define DEBUG_TYPE "static-analyzer-call-event"
@@ -29,11 +67,13 @@ using namespace clang;
using namespace ento;
QualType CallEvent::getResultType() const {
+ ASTContext &Ctx = getState()->getStateManager().getContext();
const Expr *E = getOriginExpr();
- assert(E && "Calls without origin expressions do not have results");
- QualType ResultTy = E->getType();
+ if (!E)
+ return Ctx.VoidTy;
+ assert(E);
- ASTContext &Ctx = getState()->getStateManager().getContext();
+ QualType ResultTy = E->getType();
// A function that returns a reference to 'int' will have a result type
// of simply 'int'. Check the origin expr's value kind to recover the
@@ -78,7 +118,7 @@ static bool isCallback(QualType T) {
}
static bool isVoidPointerToNonConst(QualType T) {
- if (const PointerType *PT = T->getAs<PointerType>()) {
+ if (const auto *PT = T->getAs<PointerType>()) {
QualType PointeeTy = PT->getPointeeType();
if (PointeeTy.isConstQualified())
return false;
@@ -119,14 +159,14 @@ bool CallEvent::hasVoidPointerToNonConstArg() const {
}
bool CallEvent::isGlobalCFunction(StringRef FunctionName) const {
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
if (!FD)
return false;
return CheckerContext::isCLibraryFunction(FD, FunctionName);
}
-/// \brief Returns true if a type is a pointer-to-const or reference-to-const
+/// Returns true if a type is a pointer-to-const or reference-to-const
/// with no further indirection.
static bool isPointerToConst(QualType Ty) {
QualType PointeeTy = Ty->getPointeeType();
@@ -235,7 +275,7 @@ SVal CallEvent::getArgSVal(unsigned Index) const {
SourceRange CallEvent::getArgSourceRange(unsigned Index) const {
const Expr *ArgE = getArgExpr(Index);
if (!ArgE)
- return SourceRange();
+ return {};
return ArgE->getSourceRange();
}
@@ -266,7 +306,6 @@ void CallEvent::dump(raw_ostream &Out) const {
Out << "Unknown call (type " << getKind() << ")";
}
-
bool CallEvent::isCallStmt(const Stmt *S) {
return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
|| isa<CXXConstructExpr>(S)
@@ -275,11 +314,11 @@ bool CallEvent::isCallStmt(const Stmt *S) {
QualType CallEvent::getDeclaredResultType(const Decl *D) {
assert(D);
- if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(D))
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
return FD->getReturnType();
- if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(D))
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
return MD->getReturnType();
- if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) {
+ if (const auto *BD = dyn_cast<BlockDecl>(D)) {
// Blocks are difficult because the return type may not be stored in the
// BlockDecl itself. The AST should probably be enhanced, but for now we
// just do what we can.
@@ -296,7 +335,7 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) {
return Ty;
}
- return QualType();
+ return {};
}
llvm_unreachable("unknown callable kind");
@@ -305,11 +344,11 @@ QualType CallEvent::getDeclaredResultType(const Decl *D) {
bool CallEvent::isVariadic(const Decl *D) {
assert(D);
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
return FD->isVariadic();
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
return MD->isVariadic();
- if (const BlockDecl *BD = dyn_cast<BlockDecl>(D))
+ if (const auto *BD = dyn_cast<BlockDecl>(D))
return BD->isVariadic();
llvm_unreachable("unknown callable kind");
@@ -350,32 +389,53 @@ ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
const FunctionDecl *FD = getDecl();
+ if (!FD)
+ return {};
+
// Note that the AnalysisDeclContext will have the FunctionDecl with
// the definition (if one exists).
- if (FD) {
- AnalysisDeclContext *AD =
- getLocationContext()->getAnalysisDeclContext()->
- getManager()->getContext(FD);
- bool IsAutosynthesized;
- Stmt* Body = AD->getBody(IsAutosynthesized);
- DEBUG({
- if (IsAutosynthesized)
- llvm::dbgs() << "Using autosynthesized body for " << FD->getName()
- << "\n";
- });
- if (Body) {
- const Decl* Decl = AD->getDecl();
- return RuntimeDefinition(Decl);
- }
+ AnalysisDeclContext *AD =
+ getLocationContext()->getAnalysisDeclContext()->
+ getManager()->getContext(FD);
+ bool IsAutosynthesized;
+ Stmt* Body = AD->getBody(IsAutosynthesized);
+ LLVM_DEBUG({
+ if (IsAutosynthesized)
+ llvm::dbgs() << "Using autosynthesized body for " << FD->getName()
+ << "\n";
+ });
+ if (Body) {
+ const Decl* Decl = AD->getDecl();
+ return RuntimeDefinition(Decl);
+ }
+
+ SubEngine *Engine = getState()->getStateManager().getOwningEngine();
+ AnalyzerOptions &Opts = Engine->getAnalysisManager().options;
+
+ // Try to get CTU definition only if CTUDir is provided.
+ if (!Opts.naiveCTUEnabled())
+ return {};
+
+ cross_tu::CrossTranslationUnitContext &CTUCtx =
+ *Engine->getCrossTranslationUnitContext();
+ llvm::Expected<const FunctionDecl *> CTUDeclOrError =
+ CTUCtx.getCrossTUDefinition(FD, Opts.getCTUDir(), Opts.getCTUIndexName());
+
+ if (!CTUDeclOrError) {
+ handleAllErrors(CTUDeclOrError.takeError(),
+ [&](const cross_tu::IndexError &IE) {
+ CTUCtx.emitCrossTUDiagnostics(IE);
+ });
+ return {};
}
- return RuntimeDefinition();
+ return RuntimeDefinition(*CTUDeclOrError);
}
void AnyFunctionCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
- const FunctionDecl *D = cast<FunctionDecl>(CalleeCtx->getDecl());
+ const auto *D = cast<FunctionDecl>(CalleeCtx->getDecl());
SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
D->parameters());
@@ -442,7 +502,6 @@ bool AnyFunctionCall::argumentsMayEscape() const {
return false;
}
-
const FunctionDecl *SimpleFunctionCall::getDecl() const {
const FunctionDecl *D = getOriginExpr()->getDirectCallee();
if (D)
@@ -451,9 +510,8 @@ const FunctionDecl *SimpleFunctionCall::getDecl() const {
return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
}
-
const FunctionDecl *CXXInstanceCall::getDecl() const {
- const CallExpr *CE = cast_or_null<CallExpr>(getOriginExpr());
+ const auto *CE = cast_or_null<CallExpr>(getOriginExpr());
if (!CE)
return AnyFunctionCall::getDecl();
@@ -470,15 +528,20 @@ void CXXInstanceCall::getExtraInvalidatedValues(
Values.push_back(ThisVal);
// Don't invalidate if the method is const and there are no mutable fields.
- if (const CXXMethodDecl *D = cast_or_null<CXXMethodDecl>(getDecl())) {
+ if (const auto *D = cast_or_null<CXXMethodDecl>(getDecl())) {
if (!D->isConst())
return;
// Get the record decl for the class of 'This'. D->getParent() may return a
// base class decl, rather than the class of the instance which needs to be
// checked for mutable fields.
+ // TODO: We might as well look at the dynamic type of the object.
const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts();
- const CXXRecordDecl *ParentRecord = Ex->getType()->getAsCXXRecordDecl();
- if (!ParentRecord || ParentRecord->hasMutableFields())
+ QualType T = Ex->getType();
+ if (T->isPointerType()) // Arrow or implicit-this syntax?
+ T = T->getPointeeType();
+ const CXXRecordDecl *ParentRecord = T->getAsCXXRecordDecl();
+ assert(ParentRecord);
+ if (ParentRecord->hasMutableFields())
return;
// Preserve CXXThis.
const MemRegion *ThisRegion = ThisVal.getAsRegion();
@@ -501,27 +564,26 @@ SVal CXXInstanceCall::getCXXThisVal() const {
return ThisVal;
}
-
RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
// Do we have a decl at all?
const Decl *D = getDecl();
if (!D)
- return RuntimeDefinition();
+ return {};
// If the method is non-virtual, we know we can inline it.
- const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
+ const auto *MD = cast<CXXMethodDecl>(D);
if (!MD->isVirtual())
return AnyFunctionCall::getRuntimeDefinition();
// Do we know the implicit 'this' object being called?
const MemRegion *R = getCXXThisVal().getAsRegion();
if (!R)
- return RuntimeDefinition();
+ return {};
// Do we know anything about the type of 'this'?
DynamicTypeInfo DynType = getDynamicTypeInfo(getState(), R);
if (!DynType.isValid())
- return RuntimeDefinition();
+ return {};
// Is the type a C++ class? (This is mostly a defensive check.)
QualType RegionType = DynType.getType()->getPointeeType();
@@ -529,7 +591,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl();
if (!RD || !RD->hasDefinition())
- return RuntimeDefinition();
+ return {};
// Find the decl for this method in that class.
const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true);
@@ -547,13 +609,13 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
// this is fixed. <rdar://problem/12287087>
//assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo");
- return RuntimeDefinition();
+ return {};
}
// Does the decl that we found have an implementation?
const FunctionDecl *Definition;
if (!Result->hasBody(Definition))
- return RuntimeDefinition();
+ return {};
// We found a definition. If we're not sure that this devirtualization is
// actually what will happen at runtime, make sure to provide the region so
@@ -574,7 +636,7 @@ void CXXInstanceCall::getInitialStackFrameContents(
ProgramStateManager &StateMgr = getState()->getStateManager();
SValBuilder &SVB = StateMgr.getSValBuilder();
- const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
// If we devirtualized to a different member function, we need to make sure
@@ -587,7 +649,15 @@ void CXXInstanceCall::getInitialStackFrameContents(
// FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
bool Failed;
ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed);
- assert(!Failed && "Calling an incorrectly devirtualized method");
+ if (Failed) {
+ // We might have suffered some sort of placement new earlier, so
+ // we're constructing in a completely unexpected storage.
+ // Fall back to a generic pointer cast for this-value.
+ const CXXMethodDecl *StaticMD = cast<CXXMethodDecl>(getDecl());
+ const CXXRecordDecl *StaticClass = StaticMD->getParent();
+ QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass));
+ ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy);
+ }
}
if (!ThisVal.isUnknown())
@@ -595,8 +665,6 @@ void CXXInstanceCall::getInitialStackFrameContents(
}
}
-
-
const Expr *CXXMemberCall::getCXXThisExpr() const {
return getOriginExpr()->getImplicitObjectArgument();
}
@@ -606,19 +674,17 @@ RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const {
// id-expression in the class member access expression is a qualified-id,
// that function is called. Otherwise, its final overrider in the dynamic type
// of the object expression is called.
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee()))
+ if (const auto *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee()))
if (ME->hasQualifier())
return AnyFunctionCall::getRuntimeDefinition();
return CXXInstanceCall::getRuntimeDefinition();
}
-
const Expr *CXXMemberOperatorCall::getCXXThisExpr() const {
return getOriginExpr()->getArg(0);
}
-
const BlockDataRegion *BlockCall::getBlockRegion() const {
const Expr *Callee = getOriginExpr()->getCallee();
const MemRegion *DataReg = getSVal(Callee).getAsRegion();
@@ -663,7 +729,6 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
Params);
}
-
SVal CXXConstructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
@@ -672,8 +737,13 @@ SVal CXXConstructorCall::getCXXThisVal() const {
void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
- if (Data)
- Values.push_back(loc::MemRegionVal(static_cast<const MemRegion *>(Data)));
+ 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);
+ }
}
void CXXConstructorCall::getInitialStackFrameContents(
@@ -684,7 +754,7 @@ void CXXConstructorCall::getInitialStackFrameContents(
SVal ThisVal = getCXXThisVal();
if (!ThisVal.isUnknown()) {
SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
- const CXXMethodDecl *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
}
@@ -785,7 +855,7 @@ SourceRange ObjCMethodCall::getSourceRange() const {
llvm_unreachable("unknown message kind");
}
-typedef llvm::PointerIntPair<const PseudoObjectExpr *, 2> ObjCMessageDataTy;
+using ObjCMessageDataTy = llvm::PointerIntPair<const PseudoObjectExpr *, 2>;
const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
assert(Data && "Lazy lookup not yet performed.");
@@ -799,7 +869,7 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
// This handles the funny case of assigning to the result of a getter.
// This can happen if the getter returns a non-const reference.
- if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(Syntactic))
+ if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic))
Syntactic = BO->getLHS();
return Syntactic;
@@ -807,13 +877,12 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
ObjCMessageKind ObjCMethodCall::getMessageKind() const {
if (!Data) {
-
// Find the parent, ignoring implicit casts.
ParentMap &PM = getLocationContext()->getParentMap();
const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr());
// Check if parent is a PseudoObjectExpr.
- if (const PseudoObjectExpr *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
+ if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE);
ObjCMessageKind K;
@@ -875,15 +944,14 @@ const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const {
bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
Selector Sel) const {
assert(IDecl);
- const SourceManager &SM =
- getState()->getStateManager().getContext().getSourceManager();
-
+ AnalysisManager &AMgr =
+ getState()->getStateManager().getOwningEngine()->getAnalysisManager();
// If the class interface is declared inside the main file, assume it is not
// subcassed.
// TODO: It could actually be subclassed if the subclass is private as well.
// This is probably very rare.
SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
- if (InterfLoc.isValid() && SM.isInMainFile(InterfLoc))
+ if (InterfLoc.isValid() && AMgr.isInCodeFile(InterfLoc))
return false;
// Assume that property accessors are not overridden.
@@ -905,7 +973,7 @@ bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
return false;
// If outside the main file,
- if (D->getLocation().isValid() && !SM.isInMainFile(D->getLocation()))
+ if (D->getLocation().isValid() && !AMgr.isInCodeFile(D->getLocation()))
return true;
if (D->isOverriding()) {
@@ -965,7 +1033,6 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
Selector Sel = E->getSelector();
if (E->isInstanceMessage()) {
-
// Find the receiver type.
const ObjCObjectPointerType *ReceiverT = nullptr;
bool CanBeSubClassed = false;
@@ -980,13 +1047,13 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
} else {
Receiver = getReceiverSVal().getAsRegion();
if (!Receiver)
- return RuntimeDefinition();
+ return {};
DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver);
if (!DTI.isValid()) {
assert(isa<AllocaRegion>(Receiver) &&
"Unhandled untyped region class!");
- return RuntimeDefinition();
+ return {};
}
QualType DynType = DTI.getType();
@@ -1041,11 +1108,9 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
// 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.
- typedef std::pair<const ObjCInterfaceDecl*, Selector>
- PrivateMethodKey;
- typedef llvm::DenseMap<PrivateMethodKey,
- Optional<const ObjCMethodDecl *> >
- PrivateMethodCache;
+ 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)];
@@ -1090,7 +1155,6 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
else
return RuntimeDefinition(MD, nullptr);
}
-
} else {
// This is a class method.
// If we have type info for the receiver class, we are calling via
@@ -1101,7 +1165,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
}
}
- return RuntimeDefinition();
+ return {};
}
bool ObjCMethodCall::argumentsMayEscape() const {
@@ -1118,7 +1182,7 @@ bool ObjCMethodCall::argumentsMayEscape() const {
void ObjCMethodCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
- const ObjCMethodDecl *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl());
+ const auto *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl());
SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
D->parameters());
@@ -1135,12 +1199,12 @@ void ObjCMethodCall::getInitialStackFrameContents(
CallEventRef<>
CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
const LocationContext *LCtx) {
- if (const CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(CE))
+ if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE))
return create<CXXMemberCall>(MCE, State, LCtx);
- if (const CXXOperatorCallExpr *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
if (MD->isInstance())
return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
@@ -1153,12 +1217,11 @@ CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
return create<SimpleFunctionCall>(CE, State, LCtx);
}
-
CallEventRef<>
CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
ProgramStateRef State) {
const LocationContext *ParentCtx = CalleeCtx->getParent();
- const LocationContext *CallerCtx = ParentCtx->getCurrentStackFrame();
+ const LocationContext *CallerCtx = ParentCtx->getStackFrame();
assert(CallerCtx && "This should not be used for top-level stack frames");
const Stmt *CallSite = CalleeCtx->getCallSite();
@@ -1171,7 +1234,7 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
case Stmt::CXXConstructExprClass:
case Stmt::CXXTemporaryObjectExprClass: {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
- const CXXMethodDecl *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
@@ -1192,12 +1255,11 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
// destructors, though this could change in the future.
const CFGBlock *B = CalleeCtx->getCallSiteBlock();
CFGElement E = (*B)[CalleeCtx->getIndex()];
- assert(E.getAs<CFGImplicitDtor>() &&
+ assert((E.getAs<CFGImplicitDtor>() || E.getAs<CFGTemporaryDtor>()) &&
"All other CFG elements should have exprs");
- assert(!E.getAs<CFGTemporaryDtor>() && "We don't handle temporaries yet");
SValBuilder &SVB = State->getStateManager().getSValBuilder();
- const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
+ const auto *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
@@ -1205,7 +1267,7 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>())
Trigger = AutoDtor->getTriggerStmt();
else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
- Trigger = cast<Stmt>(DeleteDtor->getDeleteExpr());
+ Trigger = DeleteDtor->getDeleteExpr();
else
Trigger = Dtor->getBody();
diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 61cbf3854bb2..6cf931abbddd 100644
--- a/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -20,9 +20,8 @@ using namespace clang;
using namespace ento;
const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const {
- ProgramStateRef State = getState();
const Expr *Callee = CE->getCallee();
- SVal L = State->getSVal(Callee, Pred->getLocationContext());
+ SVal L = Pred->getSVal(Callee);
return L.getAsFunctionDecl();
}
diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index ed41914ebd05..b9facffcc8b5 100644
--- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -15,8 +15,12 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
+namespace clang {
+
+namespace ento {
+
// Recursively find any substatements containing macros
-bool clang::ento::containsMacro(const Stmt *S) {
+bool containsMacro(const Stmt *S) {
if (S->getLocStart().isMacroID())
return true;
@@ -31,7 +35,7 @@ bool clang::ento::containsMacro(const Stmt *S) {
}
// Recursively find any substatements containing enum constants
-bool clang::ento::containsEnum(const Stmt *S) {
+bool containsEnum(const Stmt *S) {
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
if (DR && isa<EnumConstantDecl>(DR->getDecl()))
@@ -45,7 +49,7 @@ bool clang::ento::containsEnum(const Stmt *S) {
}
// Recursively find any substatements containing static vars
-bool clang::ento::containsStaticLocal(const Stmt *S) {
+bool containsStaticLocal(const Stmt *S) {
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
if (DR)
@@ -61,7 +65,7 @@ bool clang::ento::containsStaticLocal(const Stmt *S) {
}
// Recursively find any substatements containing __builtin_offsetof
-bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) {
+bool containsBuiltinOffsetOf(const Stmt *S) {
if (isa<OffsetOfExpr>(S))
return true;
@@ -74,7 +78,7 @@ bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) {
// Extract lhs and rhs from assignment statement
std::pair<const clang::VarDecl *, const clang::Expr *>
-clang::ento::parseAssignment(const Stmt *S) {
+parseAssignment(const Stmt *S) {
const VarDecl *VD = nullptr;
const Expr *RHS = nullptr;
@@ -94,3 +98,18 @@ clang::ento::parseAssignment(const Stmt *S) {
return std::make_pair(VD, RHS);
}
+
+Nullability getNullabilityAnnotation(QualType Type) {
+ const auto *AttrType = Type->getAs<AttributedType>();
+ if (!AttrType)
+ return Nullability::Unspecified;
+ if (AttrType->getAttrKind() == AttributedType::attr_nullable)
+ return Nullability::Nullable;
+ else if (AttrType->getAttrKind() == AttributedType::attr_nonnull)
+ return Nullability::Nonnull;
+ return Nullability::Unspecified;
+}
+
+
+} // end namespace ento
+} // end namespace clang
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 49f3edef2a2d..712872a15d8a 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -1,4 +1,4 @@
-//===--- CheckerManager.cpp - Static Analyzer Checker Manager -------------===//
+//===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,10 +13,20 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
+#include <vector>
using namespace clang;
using namespace ento;
@@ -43,9 +53,9 @@ void CheckerManager::finishedCheckerRegistration() {
#ifndef NDEBUG
// Make sure that for every event that has listeners, there is at least
// one dispatcher registered for it.
- for (llvm::DenseMap<EventTag, EventInfo>::iterator
- I = Events.begin(), E = Events.end(); I != E; ++I)
- assert(I->second.HasDispatcher && "No dispatcher registered for an event");
+ for (const auto &Event : Events)
+ assert(Event.second.HasDispatcher &&
+ "No dispatcher registered for an event");
#endif
}
@@ -65,25 +75,22 @@ void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
} else {
// Find the checkers that should run for this Decl and cache them.
checkers = &CachedDeclCheckersMap[DeclKind];
- for (unsigned i = 0, e = DeclCheckers.size(); i != e; ++i) {
- DeclCheckerInfo &info = DeclCheckers[i];
+ for (const auto &info : DeclCheckers)
if (info.IsForDeclFn(D))
checkers->push_back(info.CheckFn);
- }
}
assert(checkers);
- for (CachedDeclCheckers::iterator
- I = checkers->begin(), E = checkers->end(); I != E; ++I)
- (*I)(D, mgr, BR);
+ for (const auto checker : *checkers)
+ checker(D, mgr, BR);
}
void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) {
assert(D && D->hasBody());
- for (unsigned i = 0, e = BodyCheckers.size(); i != e; ++i)
- BodyCheckers[i](D, mgr, BR);
+ for (const auto BodyChecker : BodyCheckers)
+ BodyChecker(D, mgr, BR);
}
//===----------------------------------------------------------------------===//
@@ -118,10 +125,8 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx,
}
NodeBuilder B(*PrevSet, *CurrSet, BldrCtx);
- for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end();
- NI != NE; ++NI) {
- checkCtx.runChecker(*I, B, *NI);
- }
+ for (const auto &NI : *PrevSet)
+ checkCtx.runChecker(*I, B, NI);
// If all the produced transitions are sinks, stop.
if (CurrSet->empty())
@@ -133,21 +138,23 @@ static void expandGraphWithCheckers(CHECK_CTX checkCtx,
}
namespace {
+
struct CheckStmtContext {
- typedef SmallVectorImpl<CheckerManager::CheckStmtFunc> CheckersTy;
+ using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>;
+
bool IsPreVisit;
const CheckersTy &Checkers;
const Stmt *S;
ExprEngine &Eng;
bool WasInlined;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
const Stmt *s, ExprEngine &eng, bool wasInlined = false)
- : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
- WasInlined(wasInlined) {}
+ : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
+ WasInlined(wasInlined) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckStmtFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -160,9 +167,10 @@ namespace {
checkFn(S, C);
}
};
-}
-/// \brief Run checkers for visiting Stmts.
+} // namespace
+
+/// Run checkers for visiting Stmts.
void CheckerManager::runCheckersForStmt(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
@@ -175,8 +183,9 @@ void CheckerManager::runCheckersForStmt(bool isPreVisit,
}
namespace {
+
struct CheckObjCMessageContext {
- typedef std::vector<CheckerManager::CheckObjCMessageFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>;
ObjCMessageVisitKind Kind;
bool WasInlined;
@@ -184,19 +193,18 @@ namespace {
const ObjCMethodCall &Msg;
ExprEngine &Eng;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckObjCMessageContext(ObjCMessageVisitKind visitKind,
const CheckersTy &checkers,
const ObjCMethodCall &msg, ExprEngine &eng,
bool wasInlined)
- : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers),
- Msg(msg), Eng(eng) { }
+ : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg),
+ Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
-
bool IsPreVisit;
switch (Kind) {
@@ -215,9 +223,10 @@ namespace {
checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
}
};
-}
-/// \brief Run checkers for visiting obj-c messages.
+} // namespace
+
+/// Run checkers for visiting obj-c messages.
void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
@@ -242,24 +251,27 @@ CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) {
}
llvm_unreachable("Unknown Kind");
}
+
namespace {
+
// FIXME: This has all the same signatures as CheckObjCMessageContext.
// Is there a way we can merge the two?
struct CheckCallContext {
- typedef std::vector<CheckerManager::CheckCallFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckCallFunc>;
+
bool IsPreVisit, WasInlined;
const CheckersTy &Checkers;
const CallEvent &Call;
ExprEngine &Eng;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
const CallEvent &call, ExprEngine &eng,
bool wasInlined)
- : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
- Call(call), Eng(eng) { }
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Call(call), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckCallFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -269,9 +281,10 @@ namespace {
checkFn(*Call.cloneWithState(Pred->getState()), C);
}
};
-}
-/// \brief Run checkers for visiting an abstract call event.
+} // namespace
+
+/// Run checkers for visiting an abstract call event.
void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
@@ -286,8 +299,10 @@ void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
}
namespace {
+
struct CheckLocationContext {
- typedef std::vector<CheckerManager::CheckLocationFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>;
+
const CheckersTy &Checkers;
SVal Loc;
bool IsLoad;
@@ -295,15 +310,15 @@ namespace {
const Stmt *BoundEx;
ExprEngine &Eng;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckLocationContext(const CheckersTy &checkers,
SVal loc, bool isLoad, const Stmt *NodeEx,
const Stmt *BoundEx,
ExprEngine &eng)
- : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx),
- BoundEx(BoundEx), Eng(eng) {}
+ : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx),
+ BoundEx(BoundEx), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckLocationFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -317,9 +332,10 @@ namespace {
checkFn(Loc, IsLoad, BoundEx, C);
}
};
-}
-/// \brief Run checkers for load/store of a location.
+} // namespace
+
+/// Run checkers for load/store of a location.
void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
@@ -333,8 +349,10 @@ void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
}
namespace {
+
struct CheckBindContext {
- typedef std::vector<CheckerManager::CheckBindFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckBindFunc>;
+
const CheckersTy &Checkers;
SVal Loc;
SVal Val;
@@ -342,13 +360,13 @@ namespace {
ExprEngine &Eng;
const ProgramPoint &PP;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckBindContext(const CheckersTy &checkers,
SVal loc, SVal val, const Stmt *s, ExprEngine &eng,
const ProgramPoint &pp)
- : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {}
+ : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckBindFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -358,9 +376,10 @@ namespace {
checkFn(Loc, Val, S, C);
}
};
-}
-/// \brief Run checkers for binding of a value to a location.
+} // namespace
+
+/// Run checkers for binding of a value to a location.
void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SVal location, SVal val,
@@ -373,24 +392,26 @@ void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
BugReporter &BR,
ExprEngine &Eng) {
- for (unsigned i = 0, e = EndAnalysisCheckers.size(); i != e; ++i)
- EndAnalysisCheckers[i](G, BR, Eng);
+ for (const auto EndAnalysisChecker : EndAnalysisCheckers)
+ EndAnalysisChecker(G, BR, Eng);
}
namespace {
+
struct CheckBeginFunctionContext {
- typedef std::vector<CheckerManager::CheckBeginFunctionFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>;
+
const CheckersTy &Checkers;
ExprEngine &Eng;
const ProgramPoint &PP;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng,
const ProgramPoint &PP)
: Checkers(Checkers), Eng(Eng), PP(PP) {}
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
const ProgramPoint &L = PP.withTag(checkFn.Checker);
@@ -399,7 +420,8 @@ struct CheckBeginFunctionContext {
checkFn(C);
}
};
-}
+
+} // namespace
void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
const BlockEdge &L,
@@ -411,42 +433,42 @@ void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
expandGraphWithCheckers(C, Dst, Src);
}
-/// \brief Run checkers for end of path.
+/// Run checkers for end of path.
// Note, We do not chain the checker output (like in expandGraphWithCheckers)
// for this callback since end of path nodes are expected to be final.
void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
ExplodedNodeSet &Dst,
ExplodedNode *Pred,
- ExprEngine &Eng) {
-
+ ExprEngine &Eng,
+ const ReturnStmt *RS) {
// We define the builder outside of the loop bacause if at least one checkers
// creates a sucsessor for Pred, we do not need to generate an
// autotransition for it.
NodeBuilder Bldr(Pred, Dst, BC);
- for (unsigned i = 0, e = EndFunctionCheckers.size(); i != e; ++i) {
- CheckEndFunctionFunc checkFn = EndFunctionCheckers[i];
-
+ for (const auto checkFn : EndFunctionCheckers) {
const ProgramPoint &L = BlockEntrance(BC.Block,
Pred->getLocationContext(),
checkFn.Checker);
CheckerContext C(Bldr, Eng, Pred, L);
- checkFn(C);
+ checkFn(RS, C);
}
}
namespace {
+
struct CheckBranchConditionContext {
- typedef std::vector<CheckerManager::CheckBranchConditionFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>;
+
const CheckersTy &Checkers;
const Stmt *Condition;
ExprEngine &Eng;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckBranchConditionContext(const CheckersTy &checkers,
const Stmt *Cond, ExprEngine &eng)
- : Checkers(checkers), Condition(Cond), Eng(eng) {}
+ : Checkers(checkers), Condition(Cond), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckBranchConditionFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -456,9 +478,10 @@ namespace {
checkFn(Condition, C);
}
};
-}
-/// \brief Run checkers for branch condition.
+} // namespace
+
+/// Run checkers for branch condition.
void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
ExplodedNodeSet &Dst,
ExplodedNode *Pred,
@@ -469,29 +492,69 @@ void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
expandGraphWithCheckers(C, Dst, Src);
}
-/// \brief Run checkers for live symbols.
+namespace {
+
+ struct CheckNewAllocatorContext {
+ using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
+
+ const CheckersTy &Checkers;
+ const CXXNewExpr *NE;
+ SVal Target;
+ 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) {}
+
+ 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());
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(NE, Target, C);
+ }
+ };
+
+} // namespace
+
+void CheckerManager::runCheckersForNewAllocator(
+ const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ ExprEngine &Eng, bool WasInlined) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for live symbols.
void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
SymbolReaper &SymReaper) {
- for (unsigned i = 0, e = LiveSymbolsCheckers.size(); i != e; ++i)
- LiveSymbolsCheckers[i](state, SymReaper);
+ for (const auto LiveSymbolsChecker : LiveSymbolsCheckers)
+ LiveSymbolsChecker(state, SymReaper);
}
namespace {
+
struct CheckDeadSymbolsContext {
- typedef std::vector<CheckerManager::CheckDeadSymbolsFunc> CheckersTy;
+ using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>;
+
const CheckersTy &Checkers;
SymbolReaper &SR;
const Stmt *S;
ExprEngine &Eng;
ProgramPoint::Kind ProgarmPointKind;
- CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
- CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
-
CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
const Stmt *s, ExprEngine &eng,
ProgramPoint::Kind K)
- : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) { }
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
NodeBuilder &Bldr, ExplodedNode *Pred) {
@@ -505,9 +568,10 @@ namespace {
checkFn(SR, C);
}
};
-}
-/// \brief Run checkers for dead symbols.
+} // namespace
+
+/// Run checkers for dead symbols.
void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
SymbolReaper &SymReaper,
@@ -518,7 +582,7 @@ void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
expandGraphWithCheckers(C, Dst, Src);
}
-/// \brief Run checkers for region changes.
+/// Run checkers for region changes.
ProgramStateRef
CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
const InvalidatedSymbols *invalidated,
@@ -526,19 +590,18 @@ CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
ArrayRef<const MemRegion *> Regions,
const LocationContext *LCtx,
const CallEvent *Call) {
- for (unsigned i = 0, e = RegionChangesCheckers.size(); i != e; ++i) {
+ for (const auto RegionChangesChecker : RegionChangesCheckers) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return nullptr;
- state = RegionChangesCheckers[i](state, invalidated,
- ExplicitRegions, Regions,
- LCtx, Call);
+ state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions,
+ LCtx, Call);
}
return state;
}
-/// \brief Run checkers to process symbol escape event.
+/// Run checkers to process symbol escape event.
ProgramStateRef
CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
@@ -549,58 +612,55 @@ CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
(Kind != PSK_DirectEscapeOnCall &&
Kind != PSK_IndirectEscapeOnCall)) &&
"Call must not be NULL when escaping on call");
- for (unsigned i = 0, e = PointerEscapeCheckers.size(); i != e; ++i) {
- // If any checker declares the state infeasible (or if it starts that
- // way), bail out.
- if (!State)
- return nullptr;
- State = PointerEscapeCheckers[i](State, Escaped, Call, Kind, ETraits);
- }
+ for (const auto PointerEscapeChecker : PointerEscapeCheckers) {
+ // If any checker declares the state infeasible (or if it starts that
+ // way), bail out.
+ if (!State)
+ return nullptr;
+ State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits);
+ }
return State;
}
-/// \brief Run checkers for handling assumptions on symbolic values.
+/// Run checkers for handling assumptions on symbolic values.
ProgramStateRef
CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
SVal Cond, bool Assumption) {
- for (unsigned i = 0, e = EvalAssumeCheckers.size(); i != e; ++i) {
+ for (const auto EvalAssumeChecker : EvalAssumeCheckers) {
// If any checker declares the state infeasible (or if it starts that way),
// bail out.
if (!state)
return nullptr;
- state = EvalAssumeCheckers[i](state, Cond, Assumption);
+ state = EvalAssumeChecker(state, Cond, Assumption);
}
return state;
}
-/// \brief Run checkers for evaluating a call.
+/// Run checkers for evaluating a call.
/// Only one checker will evaluate the call.
void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
const ExplodedNodeSet &Src,
const CallEvent &Call,
ExprEngine &Eng) {
const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
- for (ExplodedNodeSet::iterator
- NI = Src.begin(), NE = Src.end(); NI != NE; ++NI) {
- ExplodedNode *Pred = *NI;
+ for (const auto Pred : Src) {
bool anyEvaluated = false;
ExplodedNodeSet checkDst;
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
// Check if any of the EvalCall callbacks can evaluate the call.
- for (std::vector<EvalCallFunc>::iterator
- EI = EvalCallCheckers.begin(), EE = EvalCallCheckers.end();
- EI != EE; ++EI) {
+ for (const auto EvalCallChecker : EvalCallCheckers) {
ProgramPoint::Kind K = ProgramPoint::PostStmtKind;
- const ProgramPoint &L = ProgramPoint::getProgramPoint(CE, K,
- Pred->getLocationContext(), EI->Checker);
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(),
+ EvalCallChecker.Checker);
bool evaluated = false;
{ // CheckerContext generates transitions(populates checkDest) on
// destruction, so introduce the scope to make sure it gets properly
// populated.
CheckerContext C(B, Eng, Pred, L);
- evaluated = (*EI)(CE, C);
+ evaluated = EvalCallChecker(CE, C);
}
assert(!(evaluated && anyEvaluated)
&& "There are more than one checkers evaluating the call");
@@ -621,21 +681,20 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
}
}
-/// \brief Run checkers for the entire Translation Unit.
+/// Run checkers for the entire Translation Unit.
void CheckerManager::runCheckersOnEndOfTranslationUnit(
const TranslationUnitDecl *TU,
AnalysisManager &mgr,
BugReporter &BR) {
- for (unsigned i = 0, e = EndOfTranslationUnitCheckers.size(); i != e; ++i)
- EndOfTranslationUnitCheckers[i](TU, mgr, BR);
+ for (const auto EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
+ EndOfTranslationUnitChecker(TU, mgr, BR);
}
void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
ProgramStateRef State,
const char *NL, const char *Sep) {
- for (llvm::DenseMap<CheckerTag, CheckerRef>::iterator
- I = CheckerTags.begin(), E = CheckerTags.end(); I != E; ++I)
- I->second->printState(Out, State, NL, Sep);
+ for (const auto &CheckerTag : CheckerTags)
+ CheckerTag.second->printState(Out, State, NL, Sep);
}
//===----------------------------------------------------------------------===//
@@ -661,6 +720,7 @@ void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn,
StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true };
StmtCheckers.push_back(info);
}
+
void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn,
HandlesStmtFunc isForStmtFn) {
StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false };
@@ -711,6 +771,10 @@ void CheckerManager::_registerForBranchCondition(
BranchConditionCheckers.push_back(checkfn);
}
+void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
+ NewAllocatorCheckers.push_back(checkfn);
+}
+
void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
LiveSymbolsCheckers.push_back(checkfn);
}
@@ -760,15 +824,13 @@ CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
// Find the checkers that should run for this Stmt and cache them.
CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key];
- for (unsigned i = 0, e = StmtCheckers.size(); i != e; ++i) {
- StmtCheckerInfo &Info = StmtCheckers[i];
+ for (const auto &Info : StmtCheckers)
if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S))
Checkers.push_back(Info.CheckFn);
- }
return Checkers;
}
CheckerManager::~CheckerManager() {
- for (unsigned i = 0, e = CheckerDtors.size(); i != e; ++i)
- CheckerDtors[i]();
+ for (const auto CheckerDtor : CheckerDtors)
+ CheckerDtor();
}
diff --git a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
index c9cb189a5b72..645845ec2181 100644
--- a/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerRegistry.cpp
@@ -1,4 +1,4 @@
-//===--- CheckerRegistry.cpp - Maintains all available checkers -*- C++ -*-===//
+//===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -9,18 +9,26 @@
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/CheckerOptInfo.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstddef>
+#include <tuple>
using namespace clang;
using namespace ento;
static const char PackageSeparator = '.';
-typedef llvm::SetVector<const CheckerRegistry::CheckerInfo *> CheckerInfoSet;
+using CheckerInfoSet = llvm::SetVector<const CheckerRegistry::CheckerInfo *>;
static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
const CheckerRegistry::CheckerInfo &b) {
@@ -50,8 +58,7 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers,
// Use a binary search to find the possible start of the package.
CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.getName(), "");
auto end = checkers.cend();
- CheckerRegistry::CheckerInfoList::const_iterator i =
- std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT);
+ auto i = std::lower_bound(checkers.cbegin(), end, packageInfo, checkerNameLT);
// If we didn't even find a possible package, give up.
if (i == end)
@@ -73,12 +80,11 @@ static void collectCheckers(const CheckerRegistry::CheckerInfoList &checkers,
size = packageSize->getValue();
// Step through all the checkers in the package.
- for (auto checkEnd = i+size; i != checkEnd; ++i) {
+ for (auto checkEnd = i+size; i != checkEnd; ++i)
if (opt.isEnabled())
collected.insert(&*i);
else
collected.remove(&*i);
- }
}
void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
@@ -97,42 +103,38 @@ void CheckerRegistry::addChecker(InitializationFunction fn, StringRef name,
void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
SmallVectorImpl<CheckerOptInfo> &opts) const {
// Sort checkers for efficient collection.
- std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
+ llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
// Collect checkers enabled by the options.
CheckerInfoSet enabledCheckers;
- for (SmallVectorImpl<CheckerOptInfo>::iterator
- i = opts.begin(), e = opts.end(); i != e; ++i) {
- collectCheckers(Checkers, Packages, *i, enabledCheckers);
- }
+ for (auto &i : opts)
+ collectCheckers(Checkers, Packages, i, enabledCheckers);
// Initialize the CheckerManager with all enabled checkers.
- for (CheckerInfoSet::iterator
- i = enabledCheckers.begin(), e = enabledCheckers.end(); i != e; ++i) {
- checkerMgr.setCurrentCheckName(CheckName((*i)->FullName));
- (*i)->Initialize(checkerMgr);
+ for (const auto *i :enabledCheckers) {
+ checkerMgr.setCurrentCheckName(CheckName(i->FullName));
+ i->Initialize(checkerMgr);
}
}
void CheckerRegistry::validateCheckerOptions(const AnalyzerOptions &opts,
DiagnosticsEngine &diags) const {
- for (auto &config : opts.Config) {
+ for (const auto &config : opts.Config) {
size_t pos = config.getKey().find(':');
if (pos == StringRef::npos)
continue;
bool hasChecker = false;
StringRef checkerName = config.getKey().substr(0, pos);
- for (auto &checker : Checkers) {
+ for (const auto &checker : Checkers) {
if (checker.FullName.startswith(checkerName) &&
(checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
hasChecker = true;
break;
}
}
- if (!hasChecker) {
+ if (!hasChecker)
diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
- }
}
}
@@ -141,7 +143,7 @@ void CheckerRegistry::printHelp(raw_ostream &out,
// FIXME: Alphabetical sort puts 'experimental' in the middle.
// Would it be better to name it '~experimental' or something else
// that's ASCIIbetically last?
- std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
+ llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
// FIXME: Print available packages.
@@ -149,28 +151,26 @@ void CheckerRegistry::printHelp(raw_ostream &out,
// Find the maximum option length.
size_t optionFieldWidth = 0;
- for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
- i != e; ++i) {
+ for (const auto &i : Checkers) {
// Limit the amount of padding we are willing to give up for alignment.
// Package.Name Description [Hidden]
- size_t nameLength = i->FullName.size();
+ size_t nameLength = i.FullName.size();
if (nameLength <= maxNameChars)
optionFieldWidth = std::max(optionFieldWidth, nameLength);
}
const size_t initialPad = 2;
- for (CheckerInfoList::const_iterator i = Checkers.begin(), e = Checkers.end();
- i != e; ++i) {
- out.indent(initialPad) << i->FullName;
+ for (const auto &i : Checkers) {
+ out.indent(initialPad) << i.FullName;
- int pad = optionFieldWidth - i->FullName.size();
+ int pad = optionFieldWidth - i.FullName.size();
// Break on long option names.
if (pad < 0) {
out << '\n';
pad = optionFieldWidth + initialPad;
}
- out.indent(pad + 2) << i->Desc;
+ out.indent(pad + 2) << i.Desc;
out << '\n';
}
@@ -178,19 +178,13 @@ void CheckerRegistry::printHelp(raw_ostream &out,
void CheckerRegistry::printList(
raw_ostream &out, SmallVectorImpl<CheckerOptInfo> &opts) const {
- std::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
+ llvm::sort(Checkers.begin(), Checkers.end(), checkerNameLT);
// Collect checkers enabled by the options.
CheckerInfoSet enabledCheckers;
- for (SmallVectorImpl<CheckerOptInfo>::iterator i = opts.begin(),
- e = opts.end();
- i != e; ++i) {
- collectCheckers(Checkers, Packages, *i, enabledCheckers);
- }
+ for (auto &i : opts)
+ collectCheckers(Checkers, Packages, i, enabledCheckers);
- for (CheckerInfoSet::const_iterator i = enabledCheckers.begin(),
- e = enabledCheckers.end();
- i != e; ++i) {
- out << (*i)->FullName << '\n';
- }
+ for (const auto *i : enabledCheckers)
+ out << i->FullName << '\n';
}
diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
index 8de2b0e8d271..ef9c44c51be4 100644
--- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
@@ -1,4 +1,4 @@
-//== ConstraintManager.cpp - Constraints on symbolic values -----*- C++ -*--==//
+//===- ConstraintManager.cpp - Constraints on symbolic values. ------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,12 +11,17 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/AST/Type.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"
using namespace clang;
using namespace ento;
-ConstraintManager::~ConstraintManager() {}
+ConstraintManager::~ConstraintManager() = default;
static DefinedSVal getLocFromSymbol(const ProgramStateRef &State,
SymbolRef Sym) {
@@ -35,5 +40,5 @@ ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State,
return ConditionTruthVal(false);
if (!P.first && P.second)
return ConditionTruthVal(true);
- return ConditionTruthVal();
+ return {};
}
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index e2e9ddf5048e..c17b6aae37e2 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -1,4 +1,4 @@
-//==- CoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-//
+//===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -15,11 +15,27 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
+#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/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"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <utility>
using namespace clang;
using namespace ento;
@@ -34,146 +50,42 @@ STATISTIC(NumPathsExplored,
"The # of paths explored by the analyzer.");
//===----------------------------------------------------------------------===//
-// Worklist classes for exploration of reachable states.
+// Core analysis engine.
//===----------------------------------------------------------------------===//
-WorkList::Visitor::~Visitor() {}
-
-namespace {
-class DFS : public WorkList {
- SmallVector<WorkListUnit,20> Stack;
-public:
- bool hasWork() const override {
- return !Stack.empty();
- }
-
- void enqueue(const WorkListUnit& U) override {
- Stack.push_back(U);
- }
-
- WorkListUnit dequeue() override {
- assert (!Stack.empty());
- const WorkListUnit& U = Stack.back();
- Stack.pop_back(); // This technically "invalidates" U, but we are fine.
- return U;
- }
-
- bool visitItemsInWorkList(Visitor &V) override {
- for (SmallVectorImpl<WorkListUnit>::iterator
- I = Stack.begin(), E = Stack.end(); I != E; ++I) {
- if (V.visit(*I))
- return true;
- }
- return false;
- }
-};
-
-class BFS : public WorkList {
- std::deque<WorkListUnit> Queue;
-public:
- bool hasWork() const override {
- return !Queue.empty();
- }
-
- void enqueue(const WorkListUnit& U) override {
- Queue.push_back(U);
- }
-
- WorkListUnit dequeue() override {
- WorkListUnit U = Queue.front();
- Queue.pop_front();
- return U;
- }
-
- bool visitItemsInWorkList(Visitor &V) override {
- for (std::deque<WorkListUnit>::iterator
- I = Queue.begin(), E = Queue.end(); I != E; ++I) {
- if (V.visit(*I))
- return true;
- }
- return false;
+static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts) {
+ switch (Opts.getExplorationStrategy()) {
+ case AnalyzerOptions::ExplorationStrategyKind::DFS:
+ return WorkList::makeDFS();
+ case AnalyzerOptions::ExplorationStrategyKind::BFS:
+ return WorkList::makeBFS();
+ case AnalyzerOptions::ExplorationStrategyKind::BFSBlockDFSContents:
+ return WorkList::makeBFSBlockDFSContents();
+ case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirst:
+ return WorkList::makeUnexploredFirst();
+ case AnalyzerOptions::ExplorationStrategyKind::UnexploredFirstQueue:
+ return WorkList::makeUnexploredFirstPriorityQueue();
+ default:
+ llvm_unreachable("Unexpected case");
}
-};
-
-} // end anonymous namespace
-
-// Place the dstor for WorkList here because it contains virtual member
-// functions, and we the code for the dstor generated in one compilation unit.
-WorkList::~WorkList() {}
-
-WorkList *WorkList::makeDFS() { return new DFS(); }
-WorkList *WorkList::makeBFS() { return new BFS(); }
-
-namespace {
- class BFSBlockDFSContents : public WorkList {
- std::deque<WorkListUnit> Queue;
- SmallVector<WorkListUnit,20> Stack;
- public:
- bool hasWork() const override {
- return !Queue.empty() || !Stack.empty();
- }
-
- void enqueue(const WorkListUnit& U) override {
- if (U.getNode()->getLocation().getAs<BlockEntrance>())
- Queue.push_front(U);
- else
- Stack.push_back(U);
- }
-
- WorkListUnit dequeue() override {
- // Process all basic blocks to completion.
- if (!Stack.empty()) {
- const WorkListUnit& U = Stack.back();
- Stack.pop_back(); // This technically "invalidates" U, but we are fine.
- return U;
- }
-
- assert(!Queue.empty());
- // Don't use const reference. The subsequent pop_back() might make it
- // unsafe.
- WorkListUnit U = Queue.front();
- Queue.pop_front();
- return U;
- }
- bool visitItemsInWorkList(Visitor &V) override {
- for (SmallVectorImpl<WorkListUnit>::iterator
- I = Stack.begin(), E = Stack.end(); I != E; ++I) {
- if (V.visit(*I))
- return true;
- }
- for (std::deque<WorkListUnit>::iterator
- I = Queue.begin(), E = Queue.end(); I != E; ++I) {
- if (V.visit(*I))
- return true;
- }
- return false;
- }
-
- };
-} // end anonymous namespace
-
-WorkList* WorkList::makeBFSBlockDFSContents() {
- return new BFSBlockDFSContents();
}
-//===----------------------------------------------------------------------===//
-// Core analysis engine.
-//===----------------------------------------------------------------------===//
+CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS,
+ AnalyzerOptions &Opts)
+ : SubEng(subengine), WList(generateWorkList(Opts)),
+ BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
ProgramStateRef InitState) {
-
if (G.num_roots() == 0) { // Initialize the analysis by constructing
// the root if none exists.
const CFGBlock *Entry = &(L->getCFG()->getEntry());
- assert (Entry->empty() &&
- "Entry block must be empty.");
+ assert(Entry->empty() && "Entry block must be empty.");
- assert (Entry->succ_size() == 1 &&
- "Entry block must have 1 successor.");
+ assert(Entry->succ_size() == 1 && "Entry block must have 1 successor.");
// Mark the entry block as visited.
FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(),
@@ -195,7 +107,7 @@ bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
bool IsNew;
ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
- assert (IsNew);
+ assert(IsNew);
G.addRoot(Node);
NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
@@ -251,13 +163,12 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
break;
case ProgramPoint::BlockExitKind:
- assert (false && "BlockExit location never occur in forward analysis.");
+ assert(false && "BlockExit location never occur in forward analysis.");
break;
- case ProgramPoint::CallEnterKind: {
+ case ProgramPoint::CallEnterKind:
HandleCallEnter(Loc.castAs<CallEnter>(), Pred);
break;
- }
case ProgramPoint::CallExitBeginKind:
SubEng.processCallExit(Pred);
@@ -275,7 +186,8 @@ void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
Loc.getAs<PostInitializer>() ||
Loc.getAs<PostImplicitCall>() ||
Loc.getAs<CallExitEnd>() ||
- Loc.getAs<LoopExit>());
+ Loc.getAs<LoopExit>() ||
+ Loc.getAs<PostAllocatorCall>());
HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred);
break;
}
@@ -294,7 +206,6 @@ bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
}
void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
-
const CFGBlock *Blk = L.getDst();
NodeBuilderContext BuilderCtx(*this, Blk, Pred);
@@ -306,18 +217,14 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
// Check if we are entering the EXIT block.
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
-
- assert (L.getLocationContext()->getCFG()->getExit().size() == 0
- && "EXIT block cannot contain Stmts.");
+ assert(L.getLocationContext()->getCFG()->getExit().empty() &&
+ "EXIT block cannot contain Stmts.");
// Get return statement..
const ReturnStmt *RS = nullptr;
if (!L.getSrc()->empty()) {
if (Optional<CFGStmt> LastStmt = L.getSrc()->back().getAs<CFGStmt>()) {
- if ((RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()))) {
- if (!RS->getRetValue())
- RS = nullptr;
- }
+ RS = dyn_cast<ReturnStmt>(LastStmt->getStmt());
}
}
@@ -345,12 +252,11 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
ExplodedNode *Pred) {
-
// Increment the block counter.
const LocationContext *LC = Pred->getLocationContext();
unsigned BlockId = L.getBlock()->getBlockID();
BlockCounter Counter = WList->getBlockCounter();
- Counter = BCounterFactory.IncrementCount(Counter, LC->getCurrentStackFrame(),
+ Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(),
BlockId);
WList->setBlockCounter(Counter);
@@ -364,7 +270,6 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
}
void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
-
if (const Stmt *Term = B->getTerminator()) {
switch (Term->getStmtClass()) {
default:
@@ -397,7 +302,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred);
return;
- case Stmt::CXXTryStmtClass: {
+ case Stmt::CXXTryStmtClass:
// Generate a node for each of the successors.
// Our logic for EH analysis can certainly be improved.
for (CFGBlock::const_succ_iterator it = B->succ_begin(),
@@ -408,7 +313,6 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
}
}
return;
- }
case Stmt::DoStmtClass:
HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
@@ -433,7 +337,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
case Stmt::IndirectGotoStmtClass: {
// Only 1 successor: the indirect goto dispatch block.
- assert (B->succ_size() == 1);
+ assert(B->succ_size() == 1);
IndirectGotoNodeBuilder
builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(),
@@ -443,7 +347,7 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
return;
}
- case Stmt::ObjCForCollectionStmtClass: {
+ case Stmt::ObjCForCollectionStmtClass:
// In the case of ObjCForCollectionStmt, it appears twice in a CFG:
//
// (1) inside a basic block, which represents the binding of the
@@ -456,7 +360,6 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
// contain nil elements.
HandleBranch(Term, Term, B, Pred);
return;
- }
case Stmt::SwitchStmtClass: {
SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(),
@@ -472,8 +375,8 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
}
}
- assert (B->succ_size() == 1 &&
- "Blocks with no terminator should have at most 1 successor.");
+ assert(B->succ_size() == 1 &&
+ "Blocks with no terminator should have at most 1 successor.");
generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()),
Pred->State, Pred);
@@ -518,9 +421,8 @@ void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
enqueue(Dst);
}
-
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
- ExplodedNode *Pred) {
+ ExplodedNode *Pred) {
assert(B);
assert(!B->empty());
@@ -537,14 +439,13 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
void CoreEngine::generateNode(const ProgramPoint &Loc,
ProgramStateRef State,
ExplodedNode *Pred) {
-
bool IsNew;
ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew);
if (Pred)
Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor.
else {
- assert (IsNew);
+ assert(IsNew);
G.addRoot(Node); // 'Node' has no predecessor. Make it a root.
}
@@ -555,7 +456,7 @@ void CoreEngine::generateNode(const ProgramPoint &Loc,
void CoreEngine::enqueueStmtNode(ExplodedNode *N,
const CFGBlock *Block, unsigned Idx) {
assert(Block);
- assert (!N->isSink());
+ assert(!N->isSink());
// Check if this node entered a callee.
if (N->getLocation().getAs<CallEnter>()) {
@@ -605,8 +506,7 @@ void CoreEngine::enqueueStmtNode(ExplodedNode *N,
ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N,
const ReturnStmt *RS) {
// Create a CallExitBegin node and enqueue it.
- const StackFrameContext *LocCtx
- = cast<StackFrameContext>(N->getLocationContext());
+ const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext());
// Use the callee location context.
CallExitBegin Loc(LocCtx, RS);
@@ -617,40 +517,33 @@ ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N,
return isNew ? Node : nullptr;
}
-
void CoreEngine::enqueue(ExplodedNodeSet &Set) {
- for (ExplodedNodeSet::iterator I = Set.begin(),
- E = Set.end(); I != E; ++I) {
- WList->enqueue(*I);
- }
+ for (const auto I : Set)
+ WList->enqueue(I);
}
void CoreEngine::enqueue(ExplodedNodeSet &Set,
const CFGBlock *Block, unsigned Idx) {
- for (ExplodedNodeSet::iterator I = Set.begin(),
- E = Set.end(); I != E; ++I) {
- enqueueStmtNode(*I, Block, Idx);
- }
+ for (const auto I : Set)
+ enqueueStmtNode(I, Block, Idx);
}
void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) {
- for (ExplodedNodeSet::iterator I = Set.begin(), E = Set.end(); I != E; ++I) {
- ExplodedNode *N = *I;
+ for (auto I : Set) {
// If we are in an inlined call, generate CallExitBegin node.
- if (N->getLocationContext()->getParent()) {
- N = generateCallExitBeginNode(N, RS);
- if (N)
- WList->enqueue(N);
+ if (I->getLocationContext()->getParent()) {
+ I = generateCallExitBeginNode(I, RS);
+ if (I)
+ WList->enqueue(I);
} else {
// TODO: We should run remove dead bindings here.
- G.addEndOfPath(N);
+ G.addEndOfPath(I);
NumPathsExplored++;
}
}
}
-
-void NodeBuilder::anchor() { }
+void NodeBuilder::anchor() {}
ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc,
ProgramStateRef State,
@@ -671,16 +564,15 @@ ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc,
return N;
}
-void NodeBuilderWithSinks::anchor() { }
+void NodeBuilderWithSinks::anchor() {}
StmtNodeBuilder::~StmtNodeBuilder() {
if (EnclosingBldr)
- for (ExplodedNodeSet::iterator I = Frontier.begin(),
- E = Frontier.end(); I != E; ++I )
- EnclosingBldr->addNodes(*I);
+ for (const auto I : Frontier)
+ EnclosingBldr->addNodes(I);
}
-void BranchNodeBuilder::anchor() { }
+void BranchNodeBuilder::anchor() {}
ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State,
bool branch,
@@ -714,11 +606,9 @@ IndirectGotoNodeBuilder::generateNode(const iterator &I,
return Succ;
}
-
ExplodedNode*
SwitchNodeBuilder::generateCaseStmtNode(const iterator &I,
ProgramStateRef St) {
-
bool IsNew;
ExplodedNode *Succ =
Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()),
@@ -731,7 +621,6 @@ SwitchNodeBuilder::generateCaseStmtNode(const iterator &I,
return Succ;
}
-
ExplodedNode*
SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St,
bool IsSink) {
diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
index a01ff36a8aae..530933916889 100644
--- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
+++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
@@ -1,4 +1,4 @@
-//==- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------*- C++ -*-//
+//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,6 +14,13 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.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/SymExpr.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
namespace clang {
namespace ento {
@@ -28,15 +35,15 @@ DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State,
return *GDMType;
// Otherwise, fall back to what we know about the region.
- if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg))
+ if (const auto *TR = dyn_cast<TypedRegion>(Reg))
return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false);
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) {
+ if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) {
SymbolRef Sym = SR->getSymbol();
return DynamicTypeInfo(Sym->getType());
}
- return DynamicTypeInfo();
+ return {};
}
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
@@ -47,5 +54,28 @@ ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
return NewState;
}
+void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out,
+ const char *NL, const char *Sep) {
+ bool First = true;
+ for (const auto &I : State->get<DynamicTypeMap>()) {
+ if (First) {
+ Out << NL << "Dynamic types of regions:" << NL;
+ First = false;
+ }
+ const MemRegion *MR = I.first;
+ const DynamicTypeInfo &DTI = I.second;
+ Out << MR << " : ";
+ if (DTI.isValid()) {
+ Out << DTI.getType()->getPointeeType().getAsString();
+ if (DTI.canBeASubClass()) {
+ Out << " (or its subclass)";
+ }
+ } else {
+ Out << "Invalid type info";
+ }
+ Out << NL;
+ }
+}
+
} // namespace ento
} // namespace clang
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index c6acb9d1851c..eccaee292c40 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -1,4 +1,4 @@
-//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==//
+//===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,12 +11,25 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
-#include "clang/AST/ExprObjC.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
#include "clang/Analysis/AnalysisDeclContext.h"
-#include "clang/Analysis/CFG.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.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/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
using namespace clang;
using namespace ento;
@@ -46,16 +59,16 @@ static const Expr *ignoreTransparentExprs(const Expr *E) {
}
static const Stmt *ignoreTransparentExprs(const Stmt *S) {
- if (const Expr *E = dyn_cast<Expr>(S))
+ if (const auto *E = dyn_cast<Expr>(S))
return ignoreTransparentExprs(E);
return S;
}
EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L)
- : std::pair<const Stmt *,
- const StackFrameContext *>(ignoreTransparentExprs(S),
- L ? L->getCurrentStackFrame()
- : nullptr) {}
+ : std::pair<const Stmt *,
+ const StackFrameContext *>(ignoreTransparentExprs(S),
+ L ? L->getStackFrame()
+ : nullptr) {}
SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
const SVal* X = ExprBindings.lookup(E);
@@ -95,7 +108,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
case Stmt::ReturnStmtClass: {
- const ReturnStmt *RS = cast<ReturnStmt>(S);
+ const auto *RS = cast<ReturnStmt>(S);
if (const Expr *RE = RS->getRetValue())
return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
return UndefinedVal();
@@ -121,20 +134,25 @@ Environment EnvironmentManager::bindExpr(Environment Env,
}
namespace {
+
class MarkLiveCallback final : public SymbolVisitor {
SymbolReaper &SymReaper;
+
public:
MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
+
bool VisitSymbol(SymbolRef sym) override {
SymReaper.markLive(sym);
return true;
}
+
bool VisitMemRegion(const MemRegion *R) override {
SymReaper.markLive(R);
return true;
}
};
-} // end anonymous namespace
+
+} // namespace
// removeDeadBindings:
// - Remove subexpression bindings.
@@ -147,7 +165,6 @@ Environment
EnvironmentManager::removeDeadBindings(Environment Env,
SymbolReaper &SymReaper,
ProgramStateRef ST) {
-
// We construct a new Environment object entirely, as this is cheaper than
// individually removing all the subexpression bindings (which will greatly
// outnumber block-level expression bindings).
@@ -156,14 +173,13 @@ EnvironmentManager::removeDeadBindings(Environment Env,
MarkLiveCallback CB(SymReaper);
ScanReachableSymbols RSScaner(ST, CB);
- llvm::ImmutableMapRef<EnvironmentEntry,SVal>
+ llvm::ImmutableMapRef<EnvironmentEntry, SVal>
EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
F.getTreeFactory());
// Iterate over the block-expr bindings.
for (Environment::iterator I = Env.begin(), E = Env.end();
I != E; ++I) {
-
const EnvironmentEntry &BlkExpr = I.getKey();
const SVal &X = I.getData();
@@ -186,28 +202,41 @@ EnvironmentManager::removeDeadBindings(Environment Env,
}
void Environment::print(raw_ostream &Out, const char *NL,
- const char *Sep) const {
- bool isFirst = true;
+ const char *Sep, const LocationContext *WithLC) const {
+ if (ExprBindings.isEmpty())
+ return;
+
+ if (!WithLC) {
+ // Find the freshest location context.
+ llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
+ for (auto I : *this) {
+ const LocationContext *LC = I.first.getLocationContext();
+ if (FoundContexts.count(LC) == 0) {
+ // This context is fresher than all other contexts so far.
+ WithLC = LC;
+ for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
+ FoundContexts.insert(LCI);
+ }
+ }
+ }
- for (Environment::iterator I = begin(), E = end(); I != E; ++I) {
- const EnvironmentEntry &En = I.getKey();
+ assert(WithLC);
- if (isFirst) {
- Out << NL << NL
- << "Expressions:"
- << NL;
- isFirst = false;
- } else {
- Out << NL;
- }
+ LangOptions LO; // FIXME.
+ PrintingPolicy PP(LO);
- const Stmt *S = En.getStmt();
- assert(S != nullptr && "Expected non-null Stmt");
+ Out << NL << NL << "Expressions by stack frame:" << NL;
+ WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
+ for (auto I : ExprBindings) {
+ if (I.first.getLocationContext() != LC)
+ continue;
- Out << " (" << (const void*) En.getLocationContext() << ','
- << (const void*) S << ") ";
- LangOptions LO; // FIXME.
- S->printPretty(Out, nullptr, PrintingPolicy(LO));
- Out << " : " << I.getData();
- }
+ const Stmt *S = I.first.getStmt();
+ assert(S != nullptr && "Expected non-null Stmt");
+
+ Out << "(" << (const void *)LC << ',' << (const void *)S << ") ";
+ S->printPretty(Out, nullptr, PP);
+ Out << " : " << I.second << NL;
+ }
+ });
}
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 3bc8e09333b9..ece103d9d09a 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -1,4 +1,4 @@
-//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=//
+//===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,13 +13,24 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/Stmt.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
-#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include <cassert>
+#include <memory>
using namespace clang;
using namespace ento;
@@ -29,7 +40,7 @@ using namespace ento;
//===----------------------------------------------------------------------===//
// An out of line virtual method to provide a home for the class vtable.
-ExplodedNode::Auditor::~Auditor() {}
+ExplodedNode::Auditor::~Auditor() = default;
#ifndef NDEBUG
static ExplodedNode::Auditor* NodeAuditor = nullptr;
@@ -45,10 +56,9 @@ void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) {
// Cleanup.
//===----------------------------------------------------------------------===//
-ExplodedGraph::ExplodedGraph()
- : NumNodes(0), ReclaimNodeInterval(0) {}
+ExplodedGraph::ExplodedGraph() = default;
-ExplodedGraph::~ExplodedGraph() {}
+ExplodedGraph::~ExplodedGraph() = default;
//===----------------------------------------------------------------------===//
// Node reclamation.
@@ -187,12 +197,9 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
return;
ReclaimCounter = ReclaimNodeInterval;
- for (NodeVector::iterator it = ChangedNodes.begin(), et = ChangedNodes.end();
- it != et; ++it) {
- ExplodedNode *node = *it;
+ for (const auto node : ChangedNodes)
if (shouldCollect(node))
collectNode(node);
- }
ChangedNodes.clear();
}
@@ -210,11 +217,11 @@ void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
// 2. The group is empty, in which case the storage value is null.
// 3. The group contains a single node.
// 4. The group contains more than one node.
-typedef BumpVector<ExplodedNode *> ExplodedNodeVector;
-typedef llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *> GroupStorage;
+using ExplodedNodeVector = BumpVector<ExplodedNode *>;
+using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
- assert (!V->isSink());
+ assert(!V->isSink());
Preds.addNode(V, G);
V->Succs.addNode(this, G);
#ifndef NDEBUG
@@ -346,25 +353,22 @@ std::unique_ptr<ExplodedGraph>
ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
InterExplodedGraphMap *ForwardMap,
InterExplodedGraphMap *InverseMap) const {
-
if (Nodes.empty())
return nullptr;
- typedef llvm::DenseSet<const ExplodedNode*> Pass1Ty;
+ using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
Pass1Ty Pass1;
- typedef InterExplodedGraphMap Pass2Ty;
+ using Pass2Ty = InterExplodedGraphMap;
InterExplodedGraphMap Pass2Scratch;
Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
SmallVector<const ExplodedNode*, 10> WL1, WL2;
// ===- Pass 1 (reverse DFS) -===
- for (ArrayRef<const NodeTy *>::iterator I = Sinks.begin(), E = Sinks.end();
- I != E; ++I) {
- if (*I)
- WL1.push_back(*I);
- }
+ for (const auto Sink : Sinks)
+ if (Sink)
+ WL1.push_back(Sink);
// Process the first worklist until it is empty.
while (!WL1.empty()) {
@@ -445,4 +449,3 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
return G;
}
-
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 3be37e7ae301..188316c096e3 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1,4 +1,4 @@
-//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-=
+//===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===//
//
// The LLVM Compiler Infrastructure
//
@@ -15,31 +15,75 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "PrettyStackTraceLocationContext.h"
-#include "clang/AST/CharUnits.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
-#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
-#include "clang/Basic/Builtins.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ConstructionContext.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.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/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DOTGraphTraits.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/Support/raw_ostream.h"
-
-#ifndef NDEBUG
-#include "llvm/Support/GraphWriter.h"
-#endif
+#include <cassert>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ento;
-using llvm::APSInt;
#define DEBUG_TYPE "ExprEngine"
@@ -54,14 +98,100 @@ STATISTIC(NumMaxBlockCountReachedInInlined,
STATISTIC(NumTimesRetriedWithoutInlining,
"The # of times we re-evaluated a call without inlining");
-typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *>
- CXXBindTemporaryContext;
-// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated.
-// The StackFrameContext assures that nested calls due to inlined recursive
-// functions do not interfere.
-REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet,
- llvm::ImmutableSet<CXXBindTemporaryContext>)
+//===----------------------------------------------------------------------===//
+// Internal program state traits.
+//===----------------------------------------------------------------------===//
+
+// When modeling a C++ constructor, for a variety of reasons we need to track
+// the location of the object for the duration of its ConstructionContext.
+// ObjectsUnderConstruction maps statements within the construction context
+// to the object's location, so that on every such statement the location
+// could have been retrieved.
+
+/// ConstructedObjectKey is used for being able to find the path-sensitive
+/// memory region of a freshly constructed object while modeling the AST node
+/// that syntactically represents the object that is being constructed.
+/// Semantics of such nodes may sometimes require access to the region that's
+/// not otherwise present in the program state, or to the very fact that
+/// the construction context was present and contained references to these
+/// AST nodes.
+class ConstructedObjectKey {
+ typedef std::pair<
+ llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *>,
+ const LocationContext *> ConstructedObjectKeyImpl;
+
+ ConstructedObjectKeyImpl Impl;
+
+ const void *getAnyASTNodePtr() const {
+ if (const Stmt *S = getStmt())
+ return S;
+ else
+ return getCXXCtorInitializer();
+ }
+
+public:
+ ConstructedObjectKey(
+ llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
+ const LocationContext *LC)
+ : Impl(P, LC) {
+ // This is the full list of statements that require additional actions when
+ // encountered. This list may be expanded when new actions are implemented.
+ assert(getCXXCtorInitializer() || isa<DeclStmt>(getStmt()) ||
+ isa<CXXNewExpr>(getStmt()) || isa<CXXBindTemporaryExpr>(getStmt()) ||
+ isa<MaterializeTemporaryExpr>(getStmt()) ||
+ isa<CXXConstructExpr>(getStmt()));
+ }
+
+ const Stmt *getStmt() const {
+ return Impl.first.dyn_cast<const Stmt *>();
+ }
+
+ const CXXCtorInitializer *getCXXCtorInitializer() const {
+ return Impl.first.dyn_cast<const CXXCtorInitializer *>();
+ }
+
+ const LocationContext *getLocationContext() const {
+ return Impl.second;
+ }
+
+ void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) {
+ OS << '(' << getLocationContext() << ',' << getAnyASTNodePtr() << ") ";
+ if (const Stmt *S = getStmt()) {
+ S->printPretty(OS, Helper, PP);
+ } else {
+ const CXXCtorInitializer *I = getCXXCtorInitializer();
+ OS << I->getAnyMember()->getNameAsString();
+ }
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(Impl.first.getOpaqueValue());
+ ID.AddPointer(Impl.second);
+ }
+
+ bool operator==(const ConstructedObjectKey &RHS) const {
+ return Impl == RHS.Impl;
+ }
+
+ bool operator<(const ConstructedObjectKey &RHS) const {
+ return Impl < RHS.Impl;
+ }
+};
+
+typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
+ ObjectsUnderConstructionMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
+ ObjectsUnderConstructionMap)
+
+// Additionally, track a set of destructors that correspond to elided
+// constructors when copy elision occurs.
+typedef std::pair<const CXXBindTemporaryExpr *, const LocationContext *>
+ ElidedDestructorItem;
+typedef llvm::ImmutableSet<ElidedDestructorItem>
+ ElidedDestructorSet;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ElidedDestructors,
+ ElidedDestructorSet)
//===----------------------------------------------------------------------===//
// Engine construction and deletion.
@@ -69,25 +199,21 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet,
static const char* TagProviderName = "ExprEngine";
-ExprEngine::ExprEngine(AnalysisManager &mgr, bool gcEnabled,
+ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
+ AnalysisManager &mgr, bool gcEnabled,
SetOfConstDecls *VisitedCalleesIn,
FunctionSummariesTy *FS,
InliningModes HowToInlineIn)
- : AMgr(mgr),
- AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
- Engine(*this, FS),
- G(Engine.getGraph()),
- StateMgr(getContext(), mgr.getStoreManagerCreator(),
- mgr.getConstraintManagerCreator(), G.getAllocator(),
- this),
- SymMgr(StateMgr.getSymbolManager()),
- svalBuilder(StateMgr.getSValBuilder()),
- currStmtIdx(0), currBldrCtx(nullptr),
- ObjCNoRet(mgr.getASTContext()),
- ObjCGCEnabled(gcEnabled), BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn),
- HowToInline(HowToInlineIn)
-{
+ : CTU(CTU), AMgr(mgr),
+ AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+ Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()),
+ StateMgr(getContext(), mgr.getStoreManagerCreator(),
+ mgr.getConstraintManagerCreator(), G.getAllocator(),
+ this),
+ SymMgr(StateMgr.getSymbolManager()),
+ svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
+ ObjCGCEnabled(gcEnabled), BR(mgr, *this),
+ VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
unsigned TrimInterval = mgr.options.getGraphTrimInterval();
if (TrimInterval != 0) {
// Enable eager node reclaimation when constructing the ExplodedGraph.
@@ -111,8 +237,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
// FIXME: It would be nice if we had a more general mechanism to add
// such preconditions. Some day.
do {
-
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Precondition: the first argument of 'main' is an integer guaranteed
// to be > 0.
const IdentifierInfo *II = FD->getIdentifier();
@@ -121,7 +246,7 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
const ParmVarDecl *PD = FD->getParamDecl(0);
QualType T = PD->getType();
- const BuiltinType *BT = dyn_cast<BuiltinType>(T);
+ const auto *BT = dyn_cast<BuiltinType>(T);
if (!BT || !BT->isInteger())
break;
@@ -145,9 +270,9 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
}
break;
}
- while (0);
+ while (false);
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
// Precondition: 'self' is always non-null upon entry to an Objective-C
// method.
const ImplicitParamDecl *SelfD = MD->getSelfDecl();
@@ -161,12 +286,12 @@ ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
}
}
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
if (!MD->isStatic()) {
// Precondition: 'this' is always non-null upon entry to the
// top-level function. This is our starting assumption for
// analyzing an "open" program.
- const StackFrameContext *SFC = InitLoc->getCurrentStackFrame();
+ const StackFrameContext *SFC = InitLoc->getStackFrame();
if (SFC->getParent() == nullptr) {
loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
SVal V = state->getSVal(L);
@@ -237,17 +362,30 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments(
CommaLHSs, Adjustments);
+ // Take the region for Init, i.e. for the whole object. If we do not remember
+ // the region in which the object originally was constructed, come up with
+ // a new temporary region out of thin air and copy the contents of the object
+ // (which are currently present in the Environment, because Init is an rvalue)
+ // into that region. This is not correct, but it is better than nothing.
const TypedValueRegion *TR = nullptr;
- if (const MaterializeTemporaryExpr *MT =
- dyn_cast<MaterializeTemporaryExpr>(Result)) {
- StorageDuration SD = MT->getStorageDuration();
- // If this object is bound to a reference with static storage duration, we
- // put it in a different region to prevent "address leakage" warnings.
- if (SD == SD_Static || SD == SD_Thread)
- TR = MRMgr.getCXXStaticTempObjectRegion(Init);
- }
- if (!TR)
+ if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {
+ if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
+ State = finishObjectConstruction(State, MT, LC);
+ State = State->BindExpr(Result, LC, *V);
+ return State;
+ } else {
+ StorageDuration SD = MT->getStorageDuration();
+ // If this object is bound to a reference with static storage duration, we
+ // put it in a different region to prevent "address leakage" warnings.
+ if (SD == SD_Static || SD == SD_Thread) {
+ TR = MRMgr.getCXXStaticTempObjectRegion(Init);
+ } else {
+ TR = MRMgr.getCXXTempObjectRegion(Init, LC);
+ }
+ }
+ } else {
TR = MRMgr.getCXXTempObjectRegion(Init, LC);
+ }
SVal Reg = loc::MemRegionVal(TR);
SVal BaseReg = Reg;
@@ -264,7 +402,9 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
break;
case SubobjectAdjustment::MemberPointerAdjustment:
// FIXME: Unimplemented.
- State = State->bindDefault(Reg, UnknownVal(), LC);
+ State = State->invalidateRegions(Reg, InitWithAdjustments,
+ currBldrCtx->blockCount(), LC, true,
+ nullptr, nullptr, nullptr);
return State;
}
}
@@ -283,7 +423,8 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
currBldrCtx->blockCount());
State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
- // Then we'd need to take the value that certainly exists and bind it over.
+ // Then we'd need to take the value that certainly exists and bind it
+ // over.
if (InitValWithAdjustments.isUnknown()) {
// Try to recover some path sensitivity in case we couldn't
// compute the value.
@@ -308,6 +449,79 @@ ExprEngine::createTemporaryRegionIfNeeded(ProgramStateRef State,
return State;
}
+ProgramStateRef ExprEngine::addObjectUnderConstruction(
+ ProgramStateRef State,
+ llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
+ const LocationContext *LC, SVal V) {
+ ConstructedObjectKey Key(P, LC->getStackFrame());
+ // FIXME: Currently the state might already contain the marker due to
+ // incorrect handling of temporaries bound to default parameters.
+ assert(!State->get<ObjectsUnderConstruction>(Key) ||
+ isa<CXXBindTemporaryExpr>(Key.getStmt()));
+ return State->set<ObjectsUnderConstruction>(Key, V);
+}
+
+Optional<SVal> ExprEngine::getObjectUnderConstruction(
+ ProgramStateRef State,
+ llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(P, LC->getStackFrame());
+ return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key));
+}
+
+ProgramStateRef ExprEngine::finishObjectConstruction(
+ ProgramStateRef State,
+ llvm::PointerUnion<const Stmt *, const CXXCtorInitializer *> P,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(P, LC->getStackFrame());
+ assert(State->contains<ObjectsUnderConstruction>(Key));
+ return State->remove<ObjectsUnderConstruction>(Key);
+}
+
+ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ElidedDestructorItem I(BTE, LC);
+ assert(!State->contains<ElidedDestructors>(I));
+ return State->add<ElidedDestructors>(I);
+}
+
+ProgramStateRef
+ExprEngine::cleanupElidedDestructor(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ElidedDestructorItem I(BTE, LC);
+ assert(State->contains<ElidedDestructors>(I));
+ return State->remove<ElidedDestructors>(I);
+}
+
+bool ExprEngine::isDestructorElided(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ElidedDestructorItem I(BTE, LC);
+ return State->contains<ElidedDestructors>(I);
+}
+
+bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,
+ const LocationContext *FromLC,
+ const LocationContext *ToLC) {
+ const LocationContext *LC = FromLC;
+ while (LC != ToLC) {
+ assert(LC && "ToLC must be a parent of FromLC!");
+ for (auto I : State->get<ObjectsUnderConstruction>())
+ if (I.first.getLocationContext() == LC)
+ return false;
+
+ for (auto I: State->get<ElidedDestructors>())
+ if (I.second == LC)
+ return false;
+
+ LC = LC->getParent();
+ }
+ return true;
+}
+
+
//===----------------------------------------------------------------------===//
// Top-level transfer function logic (Dispatcher).
//===----------------------------------------------------------------------===//
@@ -331,8 +545,44 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
LCtx, Call);
}
+static void printObjectsUnderConstructionForContext(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ const char *Sep,
+ const LocationContext *LC) {
+ PrintingPolicy PP =
+ LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+ for (auto I : State->get<ObjectsUnderConstruction>()) {
+ ConstructedObjectKey Key = I.first;
+ SVal Value = I.second;
+ if (Key.getLocationContext() != LC)
+ continue;
+ Key.print(Out, nullptr, PP);
+ Out << " : " << Value << NL;
+ }
+
+ for (auto I : State->get<ElidedDestructors>()) {
+ if (I.second != LC)
+ continue;
+ Out << '(' << I.second << ',' << (const void *)I.first << ") ";
+ I.first->printPretty(Out, nullptr, PP);
+ Out << " : (constructor elided)" << NL;
+ }
+}
+
void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) {
+ const char *NL, const char *Sep,
+ const LocationContext *LCtx) {
+ if (LCtx) {
+ if (!State->get<ObjectsUnderConstruction>().isEmpty()) {
+ Out << Sep << "Objects under construction:" << NL;
+
+ LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
+ printObjectsUnderConstructionForContext(Out, State, NL, Sep, LC);
+ });
+ }
+ }
+
getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
}
@@ -348,10 +598,12 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
switch (E.getKind()) {
case CFGElement::Statement:
- ProcessStmt(const_cast<Stmt*>(E.castAs<CFGStmt>().getStmt()), Pred);
+ case CFGElement::Constructor:
+ case CFGElement::CXXRecordTypedCall:
+ ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred);
return;
case CFGElement::Initializer:
- ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred);
+ ProcessInitializer(E.castAs<CFGInitializer>(), Pred);
return;
case CFGElement::NewAllocator:
ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(),
@@ -368,15 +620,16 @@ void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
return;
case CFGElement::LifetimeEnds:
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
return;
}
}
static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
- const CFGStmt S,
+ const Stmt *S,
const ExplodedNode *Pred,
const LocationContext *LC) {
-
// Are we never purging state values?
if (AMgr.options.AnalysisPurgeOpt == PurgeNone)
return false;
@@ -386,17 +639,17 @@ static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
return true;
// Is this on a non-expression?
- if (!isa<Expr>(S.getStmt()))
+ if (!isa<Expr>(S))
return true;
// Run before processing a call.
- if (CallEvent::isCallStmt(S.getStmt()))
+ if (CallEvent::isCallStmt(S))
return true;
// Is this an expression that is consumed by another expression? If so,
// postpone cleaning out the state.
ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap();
- return !PM.isConsumedExpr(cast<Expr>(S.getStmt()));
+ return !PM.isConsumedExpr(cast<Expr>(S));
}
void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
@@ -426,9 +679,16 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
LC = LC->getParent();
}
- const StackFrameContext *SFC = LC ? LC->getCurrentStackFrame() : nullptr;
+ const StackFrameContext *SFC = LC ? LC->getStackFrame() : nullptr;
SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager());
+ for (auto I : CleanedState->get<ObjectsUnderConstruction>()) {
+ if (SymbolRef Sym = I.second.getAsSymbol())
+ SymReaper.markLive(Sym);
+ if (const MemRegion *MR = I.second.getAsRegion())
+ SymReaper.markLive(MR);
+ }
+
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
// Create a state in which dead bindings are removed from the environment
@@ -457,9 +717,8 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
// environment, the store, and the constraints cleaned up but have the
// user-supplied states as the predecessors.
StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx);
- for (ExplodedNodeSet::const_iterator
- I = CheckedSet.begin(), E = CheckedSet.end(); I != E; ++I) {
- ProgramStateRef CheckerState = (*I)->getState();
+ for (const auto I : CheckedSet) {
+ ProgramStateRef CheckerState = I->getState();
// The constraint manager has not been cleaned up yet, so clean up now.
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
@@ -476,35 +735,34 @@ void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
// generate a transition to that state.
ProgramStateRef CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
- Bldr.generateNode(DiagnosticStmt, *I, CleanedCheckerSt, &cleanupTag, K);
+ Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K);
}
}
}
-void ExprEngine::ProcessStmt(const CFGStmt S,
- ExplodedNode *Pred) {
+void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
- const Stmt *currStmt = S.getStmt();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
currStmt->getLocStart(),
"Error evaluating statement");
// Remove dead bindings and symbols.
ExplodedNodeSet CleanedStates;
- if (shouldRemoveDeadBindings(AMgr, S, Pred, Pred->getLocationContext())){
- removeDead(Pred, CleanedStates, currStmt, Pred->getLocationContext());
+ if (shouldRemoveDeadBindings(AMgr, currStmt, Pred,
+ Pred->getLocationContext())) {
+ removeDead(Pred, CleanedStates, currStmt,
+ Pred->getLocationContext());
} else
CleanedStates.Add(Pred);
// Visit the statement.
ExplodedNodeSet Dst;
- for (ExplodedNodeSet::iterator I = CleanedStates.begin(),
- E = CleanedStates.end(); I != E; ++I) {
+ for (const auto I : CleanedStates) {
ExplodedNodeSet DstI;
// Visit the statement.
- Visit(currStmt, *I, DstI);
+ Visit(currStmt, I, DstI);
Dst.insert(DstI);
}
@@ -530,36 +788,38 @@ void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) {
Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
}
-void ExprEngine::ProcessInitializer(const CFGInitializer Init,
+void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
ExplodedNode *Pred) {
- const CXXCtorInitializer *BMI = Init.getInitializer();
+ const CXXCtorInitializer *BMI = CFGInit.getInitializer();
+ const Expr *Init = BMI->getInit()->IgnoreImplicit();
+ const LocationContext *LC = Pred->getLocationContext();
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
BMI->getSourceLocation(),
"Error evaluating initializer");
// We don't clean up dead bindings here.
- const StackFrameContext *stackFrame =
- cast<StackFrameContext>(Pred->getLocationContext());
- const CXXConstructorDecl *decl =
- cast<CXXConstructorDecl>(stackFrame->getDecl());
+ const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext());
+ const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl());
ProgramStateRef State = Pred->getState();
SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame));
- ExplodedNodeSet Tmp(Pred);
+ ExplodedNodeSet Tmp;
SVal FieldLoc;
// Evaluate the initializer, if necessary
if (BMI->isAnyMemberInitializer()) {
// Constructors build the object directly in the field,
// but non-objects must be copied in from the initializer.
- if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) {
- assert(BMI->getInit()->IgnoreImplicit() == CtorExpr);
- (void)CtorExpr;
+ if (getObjectUnderConstruction(State, BMI, LC)) {
// The field was directly constructed, so there is no need to bind.
+ // But we still need to stop tracking the object under construction.
+ State = finishObjectConstruction(State, BMI, LC);
+ NodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+ PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr);
+ Bldr.generateNode(PS, State, Pred);
} else {
- const Expr *Init = BMI->getInit()->IgnoreImplicit();
const ValueDecl *Field;
if (BMI->isIndirectMemberInitializer()) {
Field = BMI->getIndirectMember();
@@ -593,15 +853,12 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
InitVal = State->getSVal(BMI->getInit(), stackFrame);
}
- assert(Tmp.size() == 1 && "have not generated any new nodes yet");
- assert(*Tmp.begin() == Pred && "have not generated any new nodes yet");
- Tmp.clear();
-
PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
}
} else {
assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
+ Tmp.insert(Pred);
// We already did all the work when visiting the CXXConstructExpr.
}
@@ -610,9 +867,9 @@ void ExprEngine::ProcessInitializer(const CFGInitializer Init,
PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
ExplodedNodeSet Dst;
NodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) {
- ExplodedNode *N = *I;
- Bldr.generateNode(PP, N->getState(), N);
+ for (const auto I : Tmp) {
+ ProgramStateRef State = I->getState();
+ Bldr.generateNode(PP, State, I);
}
// Enqueue the new nodes onto the work list.
@@ -688,8 +945,15 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
varType = cast<TypedValueRegion>(Region)->getValueType();
}
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ EvalCallOptions CallOpts;
+ Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
+ CallOpts.IsArrayCtorOrDtor).getAsRegion();
+
VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false,
- Pred, Dst);
+ Pred, Dst, CallOpts);
}
void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
@@ -699,12 +963,12 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
const LocationContext *LCtx = Pred->getLocationContext();
const CXXDeleteExpr *DE = Dtor.getDeleteExpr();
const Stmt *Arg = DE->getArgument();
+ QualType DTy = DE->getDestroyedType();
SVal ArgVal = State->getSVal(Arg, LCtx);
// If the argument to delete is known to be a null value,
// don't run destructor.
if (State->isNull(ArgVal).isConstrainedTrue()) {
- QualType DTy = DE->getDestroyedType();
QualType BTy = getContext().getBaseElementType(DTy);
const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl();
const CXXDestructorDecl *Dtor = RD->getDestructor();
@@ -715,19 +979,30 @@ void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
return;
}
- VisitCXXDestructor(DE->getDestroyedType(),
- ArgVal.getAsRegion(),
- DE, /*IsBase=*/ false,
- Pred, Dst);
+ EvalCallOptions CallOpts;
+ const MemRegion *ArgR = ArgVal.getAsRegion();
+ if (DE->isArrayForm()) {
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ CallOpts.IsArrayCtorOrDtor = true;
+ // Yes, it may even be a multi-dimensional array.
+ while (const auto *AT = getContext().getAsArrayType(DTy))
+ DTy = AT->getElementType();
+ if (ArgR)
+ ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy);
+ }
+
+ VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
}
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
const LocationContext *LCtx = Pred->getLocationContext();
- const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor,
- LCtx->getCurrentStackFrame());
+ LCtx->getStackFrame());
SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
// Create the base object region.
@@ -737,51 +1012,94 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
Base->isVirtual());
VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(),
- CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst);
+ CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {});
}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
const FieldDecl *Member = D.getFieldDecl();
+ QualType T = Member->getType();
ProgramStateRef State = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
- const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
- LCtx->getCurrentStackFrame());
+ LCtx->getStackFrame());
SVal FieldVal =
State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>());
- VisitCXXDestructor(Member->getType(),
- FieldVal.castAs<loc::MemRegionVal>().getRegion(),
- CurDtor->getBody(), /*IsBase=*/false, Pred, Dst);
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ EvalCallOptions CallOpts;
+ FieldVal = makeZeroElementRegion(State, FieldVal, T,
+ CallOpts.IsArrayCtorOrDtor);
+
+ VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(),
+ CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts);
}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
- ExplodedNodeSet CleanDtorState;
- StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
+ const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr();
ProgramStateRef State = Pred->getState();
- if (State->contains<InitializedTemporariesSet>(
- std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()))) {
+ const LocationContext *LC = Pred->getLocationContext();
+ const MemRegion *MR = nullptr;
+
+ if (Optional<SVal> V =
+ getObjectUnderConstruction(State, D.getBindTemporaryExpr(),
+ Pred->getLocationContext())) {
// FIXME: Currently we insert temporary destructors for default parameters,
- // but we don't insert the constructors.
- State = State->remove<InitializedTemporariesSet>(
- std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()));
+ // but we don't insert the constructors, so the entry in
+ // ObjectsUnderConstruction may be missing.
+ State = finishObjectConstruction(State, D.getBindTemporaryExpr(),
+ Pred->getLocationContext());
+ MR = V->getAsRegion();
}
+
+ // If copy elision has occured, and the constructor corresponding to the
+ // destructor was elided, we need to skip the destructor as well.
+ if (isDestructorElided(State, BTE, LC)) {
+ State = cleanupElidedDestructor(State, BTE, LC);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ PostImplicitCall PP(D.getDestructorDecl(getContext()),
+ D.getBindTemporaryExpr()->getLocStart(),
+ Pred->getLocationContext());
+ Bldr.generateNode(PP, State, Pred);
+ return;
+ }
+
+ ExplodedNodeSet CleanDtorState;
+ StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
- QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
+ QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType();
// FIXME: Currently CleanDtorState can be empty here due to temporaries being
// bound to default parameters.
assert(CleanDtorState.size() <= 1);
ExplodedNode *CleanPred =
CleanDtorState.empty() ? Pred : *CleanDtorState.begin();
- // FIXME: Inlining of temporary destructors is not supported yet anyway, so
- // we just put a NULL region for now. This will need to be changed later.
- VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(),
- /*IsBase=*/false, CleanPred, Dst);
+
+ EvalCallOptions CallOpts;
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ if (!MR) {
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+
+ // If we have no MR, we still need to unwrap the array to avoid destroying
+ // the whole array at once. Regardless, we'd eventually need to model array
+ // destructors properly, element-by-element.
+ while (const ArrayType *AT = getContext().getAsArrayType(T)) {
+ T = AT->getElementType();
+ CallOpts.IsArrayCtorOrDtor = true;
+ }
+ } else {
+ // We'd eventually need to makeZeroElementRegion() trick here,
+ // but for now we don't have the respective construction contexts,
+ // so MR would always be null in this case. Do nothing for now.
+ }
+ VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(),
+ /*IsBase=*/false, CleanPred, Dst, CallOpts);
}
void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
@@ -791,19 +1109,23 @@ void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
const CFGBlock *DstT,
const CFGBlock *DstF) {
BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF);
- if (Pred->getState()->contains<InitializedTemporariesSet>(
- std::make_pair(BTE, Pred->getStackFrame()))) {
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LC = Pred->getLocationContext();
+ if (getObjectUnderConstruction(State, BTE, LC)) {
TempDtorBuilder.markInfeasible(false);
- TempDtorBuilder.generateNode(Pred->getState(), true, Pred);
+ TempDtorBuilder.generateNode(State, true, Pred);
} else {
TempDtorBuilder.markInfeasible(true);
- TempDtorBuilder.generateNode(Pred->getState(), false, Pred);
+ TempDtorBuilder.generateNode(State, false, Pred);
}
}
void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
ExplodedNodeSet &PreVisit,
ExplodedNodeSet &Dst) {
+ // This is a fallback solution in case we didn't have a construction
+ // context when we were constructing the temporary. Otherwise the map should
+ // have been populated there.
if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) {
// In case we don't have temporary destructors in the CFG, do not mark
// the initialization - we would otherwise never clean it up.
@@ -813,34 +1135,39 @@ void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
for (ExplodedNode *Node : PreVisit) {
ProgramStateRef State = Node->getState();
-
- if (!State->contains<InitializedTemporariesSet>(
- std::make_pair(BTE, Node->getStackFrame()))) {
- // FIXME: Currently the state might already contain the marker due to
+ const LocationContext *LC = Node->getLocationContext();
+ if (!getObjectUnderConstruction(State, BTE, LC)) {
+ // FIXME: Currently the state might also already contain the marker due to
// incorrect handling of temporaries bound to default parameters; for
// those, we currently skip the CXXBindTemporaryExpr but rely on adding
// temporary destructor nodes.
- State = State->add<InitializedTemporariesSet>(
- std::make_pair(BTE, Node->getStackFrame()));
+ State = addObjectUnderConstruction(State, BTE, LC, UnknownVal());
}
StmtBldr.generateNode(BTE, Node, State);
}
}
-namespace {
-class CollectReachableSymbolsCallback final : public SymbolVisitor {
- InvalidatedSymbols Symbols;
+ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V,
+ PointerEscapeKind K) const {
+ class CollectReachableSymbolsCallback final : public SymbolVisitor {
+ InvalidatedSymbols Symbols;
-public:
- explicit CollectReachableSymbolsCallback(ProgramStateRef State) {}
- const InvalidatedSymbols &getSymbols() const { return Symbols; }
+ public:
+ explicit CollectReachableSymbolsCallback(ProgramStateRef State) {}
- bool VisitSymbol(SymbolRef Sym) override {
- Symbols.insert(Sym);
- return true;
- }
-};
-} // end anonymous namespace
+ const InvalidatedSymbols &getSymbols() const { return Symbols; }
+
+ bool VisitSymbol(SymbolRef Sym) override {
+ Symbols.insert(Sym);
+ return true;
+ }
+ };
+
+ const CollectReachableSymbolsCallback &Scanner =
+ State->scanReachableSymbols<CollectReachableSymbolsCallback>(V);
+ return getCheckerManager().runCheckersForPointerEscape(
+ State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr);
+}
void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet &DstTop) {
@@ -930,8 +1257,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
- case Stmt::CapturedStmtClass:
- {
+ case Stmt::CapturedStmtClass: {
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
break;
@@ -1026,6 +1352,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::AddrLabelExprClass:
case Stmt::AttributedStmtClass:
case Stmt::IntegerLiteralClass:
+ case Stmt::FixedPointLiteralClass:
case Stmt::CharacterLiteralClass:
case Stmt::ImplicitValueInitExprClass:
case Stmt::CXXScalarValueInitExprClass:
@@ -1060,16 +1387,15 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);
const Expr *ArgE;
- if (const CXXDefaultArgExpr *DefE = dyn_cast<CXXDefaultArgExpr>(S))
+ if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
ArgE = DefE->getExpr();
- else if (const CXXDefaultInitExpr *DefE = dyn_cast<CXXDefaultInitExpr>(S))
+ else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
ArgE = DefE->getExpr();
else
llvm_unreachable("unknown constant wrapper kind");
bool IsTemporary = false;
- if (const MaterializeTemporaryExpr *MTE =
- dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
+ if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
ArgE = MTE->GetTemporaryExpr();
IsTemporary = true;
}
@@ -1079,15 +1405,14 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ConstantVal = UnknownVal();
const LocationContext *LCtx = Pred->getLocationContext();
- for (ExplodedNodeSet::iterator I = PreVisit.begin(), E = PreVisit.end();
- I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
+ for (const auto I : PreVisit) {
+ ProgramStateRef State = I->getState();
State = State->BindExpr(S, LCtx, *ConstantVal);
if (IsTemporary)
State = createTemporaryRegionIfNeeded(State, LCtx,
cast<Expr>(S),
cast<Expr>(S));
- Bldr2.generateNode(S, *I, State);
+ Bldr2.generateNode(S, I, State);
}
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
@@ -1108,12 +1433,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx);
- const Expr *Ex = cast<Expr>(S);
+ const auto *Ex = cast<Expr>(S);
QualType resultType = Ex->getType();
- for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
- it != et; ++it) {
- ExplodedNode *N = *it;
+ for (const auto N : preVisit) {
const LocationContext *LCtx = N->getLocationContext();
SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
resultType,
@@ -1127,17 +1450,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
->getType()->isRecordType()))
for (auto Child : Ex->children()) {
assert(Child);
-
SVal Val = State->getSVal(Child, LCtx);
-
- CollectReachableSymbolsCallback Scanner =
- State->scanReachableSymbols<CollectReachableSymbolsCallback>(
- Val);
- const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols();
-
- State = getCheckerManager().runCheckersForPointerEscape(
- State, EscapedSymbols,
- /*CallEvent*/ nullptr, PSK_EscapeOther, nullptr);
+ State = escapeValue(State, Val, PSK_EscapeOther);
}
Bldr2.generateNode(S, N, State);
@@ -1184,7 +1498,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
case Stmt::BinaryOperatorClass: {
- const BinaryOperator* B = cast<BinaryOperator>(S);
+ const auto *B = cast<BinaryOperator>(S);
if (B->isLogicalOp()) {
Bldr.takeNodes(Pred);
VisitLogicalExpr(B, Pred, Dst);
@@ -1216,12 +1530,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
}
case Stmt::CXXOperatorCallExprClass: {
- const CXXOperatorCallExpr *OCE = cast<CXXOperatorCallExpr>(S);
+ const auto *OCE = cast<CXXOperatorCallExpr>(S);
// For instance method operators, make sure the 'this' argument has a
// valid region.
const Decl *Callee = OCE->getCalleeDecl();
- if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) {
if (MD->isInstance()) {
ProgramStateRef State = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
@@ -1239,34 +1553,38 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
// FALLTHROUGH
LLVM_FALLTHROUGH;
}
+
case Stmt::CallExprClass:
case Stmt::CXXMemberCallExprClass:
- case Stmt::UserDefinedLiteralClass: {
+ case Stmt::UserDefinedLiteralClass:
Bldr.takeNodes(Pred);
VisitCallExpr(cast<CallExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
- case Stmt::CXXCatchStmtClass: {
+ case Stmt::CXXCatchStmtClass:
Bldr.takeNodes(Pred);
VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
case Stmt::CXXTemporaryObjectExprClass:
- case Stmt::CXXConstructExprClass: {
+ case Stmt::CXXConstructExprClass:
Bldr.takeNodes(Pred);
VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
Bldr.addNodes(Dst);
break;
- }
case Stmt::CXXNewExprClass: {
Bldr.takeNodes(Pred);
+
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
ExplodedNodeSet PostVisit;
- VisitCXXNewExpr(cast<CXXNewExpr>(S), Pred, PostVisit);
+ for (const auto i : PreVisit)
+ VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit);
+
getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
Bldr.addNodes(Dst);
break;
@@ -1275,12 +1593,11 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXDeleteExprClass: {
Bldr.takeNodes(Pred);
ExplodedNodeSet PreVisit;
- const CXXDeleteExpr *CDE = cast<CXXDeleteExpr>(S);
+ const auto *CDE = cast<CXXDeleteExpr>(S);
getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
- for (ExplodedNodeSet::iterator i = PreVisit.begin(),
- e = PreVisit.end(); i != e ; ++i)
- VisitCXXDeleteExpr(CDE, *i, Dst);
+ for (const auto i : PreVisit)
+ VisitCXXDeleteExpr(CDE, i, Dst);
Bldr.addNodes(Dst);
break;
@@ -1290,7 +1607,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::ChooseExprClass: { // __builtin_choose_expr
Bldr.takeNodes(Pred);
- const ChooseExpr *C = cast<ChooseExpr>(S);
+ const auto *C = cast<ChooseExpr>(S);
VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
Bldr.addNodes(Dst);
break;
@@ -1311,8 +1628,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass: { // '?' operator
Bldr.takeNodes(Pred);
- const AbstractConditionalOperator *C
- = cast<AbstractConditionalOperator>(S);
+ const auto *C = cast<AbstractConditionalOperator>(S);
VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst);
Bldr.addNodes(Dst);
break;
@@ -1326,7 +1642,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::DeclRefExprClass: {
Bldr.takeNodes(Pred);
- const DeclRefExpr *DE = cast<DeclRefExpr>(S);
+ const auto *DE = cast<DeclRefExpr>(S);
VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst);
Bldr.addNodes(Dst);
break;
@@ -1347,7 +1663,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXFunctionalCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
Bldr.takeNodes(Pred);
- const CastExpr *C = cast<CastExpr>(S);
+ const auto *C = cast<CastExpr>(S);
ExplodedNodeSet dstExpr;
VisitCast(C, C->getSubExpr(), Pred, dstExpr);
@@ -1359,14 +1675,12 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Expr::MaterializeTemporaryExprClass: {
Bldr.takeNodes(Pred);
- const MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(S);
+ const auto *MTE = cast<MaterializeTemporaryExpr>(S);
ExplodedNodeSet dstPrevisit;
getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
ExplodedNodeSet dstExpr;
- for (ExplodedNodeSet::iterator i = dstPrevisit.begin(),
- e = dstPrevisit.end(); i != e ; ++i) {
- CreateCXXTemporaryObject(MTE, *i, dstExpr);
- }
+ for (const auto i : dstPrevisit)
+ CreateCXXTemporaryObject(MTE, i, dstExpr);
getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
Bldr.addNodes(Dst);
break;
@@ -1421,11 +1735,19 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
Bldr.addNodes(Dst);
break;
- case Stmt::OffsetOfExprClass:
+ case Stmt::OffsetOfExprClass: {
Bldr.takeNodes(Pred);
- VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Pred, Dst);
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
+ ExplodedNodeSet PostVisit;
+ for (const auto Node : PreVisit)
+ VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit);
+
+ getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
Bldr.addNodes(Dst);
break;
+ }
case Stmt::UnaryExprOrTypeTraitExprClass:
Bldr.takeNodes(Pred);
@@ -1435,7 +1757,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
case Stmt::StmtExprClass: {
- const StmtExpr *SE = cast<StmtExpr>(S);
+ const auto *SE = cast<StmtExpr>(S);
if (SE->getSubStmt()->body_empty()) {
// Empty statement expression.
@@ -1444,7 +1766,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
break;
}
- if (Expr *LastExpr = dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
+ if (const auto *LastExpr =
+ dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
ProgramStateRef state = Pred->getState();
Bldr.generateNode(SE, Pred,
state->BindExpr(SE, Pred->getLocationContext(),
@@ -1456,7 +1779,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::UnaryOperatorClass: {
Bldr.takeNodes(Pred);
- const UnaryOperator *U = cast<UnaryOperator>(S);
+ const auto *U = cast<UnaryOperator>(S);
if (AMgr.options.eagerlyAssumeBinOpBifurcation && (U->getOpcode() == UO_LNot)) {
ExplodedNodeSet Tmp;
VisitUnaryOperator(U, Pred, Tmp);
@@ -1471,7 +1794,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::PseudoObjectExprClass: {
Bldr.takeNodes(Pred);
ProgramStateRef state = Pred->getState();
- const PseudoObjectExpr *PE = cast<PseudoObjectExpr>(S);
+ const auto *PE = cast<PseudoObjectExpr>(S);
if (const Expr *Result = PE->getResultExpr()) {
SVal V = state->getSVal(Result, Pred->getLocationContext());
Bldr.generateNode(S, Pred,
@@ -1490,8 +1813,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
const LocationContext *CalleeLC) {
- const StackFrameContext *CalleeSF = CalleeLC->getCurrentStackFrame();
- const StackFrameContext *CallerSF = CalleeSF->getParent()->getCurrentStackFrame();
+ const StackFrameContext *CalleeSF = CalleeLC->getStackFrame();
+ const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame();
assert(CalleeSF && CallerSF);
ExplodedNode *BeforeProcessingCall = nullptr;
const Stmt *CE = CalleeSF->getCallSite();
@@ -1503,7 +1826,7 @@ bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
N = N->pred_empty() ? nullptr : *(N->pred_begin());
// Skip the nodes corresponding to the inlined code.
- if (L.getLocationContext()->getCurrentStackFrame() != CallerSF)
+ if (L.getStackFrame() != CallerSF)
continue;
// We reached the caller. Find the node right before we started
// processing the call.
@@ -1602,10 +1925,10 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
// Check if we stopped at the top level function or not.
// Root node should have the location context of the top most function.
const LocationContext *CalleeLC = Pred->getLocation().getLocationContext();
- const LocationContext *CalleeSF = CalleeLC->getCurrentStackFrame();
+ const LocationContext *CalleeSF = CalleeLC->getStackFrame();
const LocationContext *RootLC =
(*G.roots_begin())->getLocation().getLocationContext();
- if (RootLC->getCurrentStackFrame() != CalleeSF) {
+ if (RootLC->getStackFrame() != CalleeSF) {
Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl());
// Re-run the call evaluation without inlining it, by storing the
@@ -1639,14 +1962,14 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
const LocationContext *LCtx,
ASTContext &Ctx) {
- const Expr *Ex = dyn_cast<Expr>(Condition);
+ const auto *Ex = dyn_cast<Expr>(Condition);
if (!Ex)
return UnknownVal();
uint64_t bits = 0;
bool bitsInit = false;
- while (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) {
+ while (const auto *CE = dyn_cast<CastExpr>(Ex)) {
QualType T = CE->getType();
if (!T->isIntegralOrEnumerationType())
@@ -1674,7 +1997,7 @@ static SVal RecoverCastedSymbol(ProgramStateManager& StateMgr,
#ifndef NDEBUG
static const Stmt *getRightmostLeaf(const Stmt *Condition) {
while (Condition) {
- const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition);
+ const auto *BO = dyn_cast<BinaryOperator>(Condition);
if (!BO || !BO->isLogicalOp()) {
return Condition;
}
@@ -1700,10 +2023,10 @@ static const Stmt *getRightmostLeaf(const Stmt *Condition) {
// space.
static const Stmt *ResolveCondition(const Stmt *Condition,
const CFGBlock *B) {
- if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ if (const auto *Ex = dyn_cast<Expr>(Condition))
Condition = Ex->IgnoreParens();
- const BinaryOperator *BO = dyn_cast<BinaryOperator>(Condition);
+ const auto *BO = dyn_cast<BinaryOperator>(Condition);
if (!BO || !BO->isLogicalOp())
return Condition;
@@ -1751,7 +2074,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
return;
}
- if (const Expr *Ex = dyn_cast<Expr>(Condition))
+ if (const auto *Ex = dyn_cast<Expr>(Condition))
Condition = Ex->IgnoreParens();
Condition = ResolveCondition(Condition, BldCtx.getBlock());
@@ -1767,10 +2090,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
return;
BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
- for (NodeBuilder::iterator I = CheckersOutSet.begin(),
- E = CheckersOutSet.end(); E != I; ++I) {
- ExplodedNode *PredI = *I;
-
+ for (const auto PredI : CheckersOutSet) {
if (PredI->isSink())
continue;
@@ -1779,7 +2099,7 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
if (X.isUnknownOrUndef()) {
// Give it a chance to recover from unknown.
- if (const Expr *Ex = dyn_cast<Expr>(Condition)) {
+ if (const auto *Ex = dyn_cast<Expr>(Condition)) {
if (Ex->getType()->isIntegralOrEnumerationType()) {
// Try to recover some path-sensitivity. Right now casts of symbolic
// integers that promote their values are currently not tracked well.
@@ -1836,13 +2156,13 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
void ExprEngine::processStaticInitializer(const DeclStmt *DS,
NodeBuilderContext &BuilderCtx,
ExplodedNode *Pred,
- clang::ento::ExplodedNodeSet &Dst,
+ ExplodedNodeSet &Dst,
const CFGBlock *DstT,
const CFGBlock *DstF) {
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
currBldrCtx = &BuilderCtx;
- const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
+ const auto *VD = cast<VarDecl>(DS->getSingleDecl());
ProgramStateRef state = Pred->getState();
bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF);
@@ -1860,7 +2180,6 @@ void ExprEngine::processStaticInitializer(const DeclStmt *DS,
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a computed goto jump.
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
-
ProgramStateRef state = builder.getState();
SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext());
@@ -1871,7 +2190,7 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
// (3) We have no clue about the label. Dispatch to all targets.
//
- typedef IndirectGotoNodeBuilder::iterator iterator;
+ using iterator = IndirectGotoNodeBuilder::iterator;
if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
const LabelDecl *L = LV->getLabel();
@@ -1897,26 +2216,10 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
// This is really a catch-all. We don't support symbolics yet.
// FIXME: Implement dispatch for symbolic pointers.
- for (iterator I=builder.begin(), E=builder.end(); I != E; ++I)
+ for (iterator I = builder.begin(), E = builder.end(); I != E; ++I)
builder.generateNode(I, state);
}
-#if 0
-static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
- const StackFrameContext* Frame = Pred.getStackFrame();
- const llvm::ImmutableSet<CXXBindTemporaryContext> &Set =
- Pred.getState()->get<InitializedTemporariesSet>();
- return std::find_if(Set.begin(), Set.end(),
- [&](const CXXBindTemporaryContext &Ctx) {
- if (Ctx.second == Frame) {
- Ctx.first->dump();
- llvm::errs() << "\n";
- }
- return Ctx.second == Frame;
- }) == Set.end();
-}
-#endif
-
void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
@@ -1930,9 +2233,59 @@ void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
ExplodedNode *Pred,
const ReturnStmt *RS) {
- // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)).
- // We currently cannot enable this assert, as lifetime extended temporaries
- // are not modelled correctly.
+ // FIXME: We currently cannot assert that temporaries are clear, because
+ // lifetime extended temporaries are not always modelled correctly. In some
+ // cases when we materialize the temporary, we do
+ // createTemporaryRegionIfNeeded(), and the region changes, and also the
+ // respective destructor becomes automatic from temporary. So for now clean up
+ // the state manually before asserting. Ideally, the code above the assertion
+ // should go away, but the assertion should remain.
+ {
+ ExplodedNodeSet CleanUpObjects;
+ NodeBuilder Bldr(Pred, CleanUpObjects, BC);
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *FromLC = Pred->getLocationContext();
+ const LocationContext *ToLC = FromLC->getStackFrame()->getParent();
+ const LocationContext *LC = FromLC;
+ while (LC != ToLC) {
+ assert(LC && "ToLC must be a parent of FromLC!");
+ for (auto I : State->get<ObjectsUnderConstruction>())
+ if (I.first.getLocationContext() == LC) {
+ // The comment above only pardons us for not cleaning up a
+ // CXXBindTemporaryExpr. If any other statements are found here,
+ // it must be a separate problem.
+ assert(isa<CXXBindTemporaryExpr>(I.first.getStmt()));
+ State = State->remove<ObjectsUnderConstruction>(I.first);
+ // Also cleanup the elided destructor info.
+ ElidedDestructorItem Item(
+ cast<CXXBindTemporaryExpr>(I.first.getStmt()),
+ I.first.getLocationContext());
+ State = State->remove<ElidedDestructors>(Item);
+ }
+
+ // Also suppress the assertion for elided destructors when temporary
+ // destructors are not provided at all by the CFG, because there's no
+ // good place to clean them up.
+ if (!AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG())
+ for (auto I : State->get<ElidedDestructors>())
+ if (I.second == LC)
+ State = State->remove<ElidedDestructors>(I);
+
+ LC = LC->getParent();
+ }
+ if (State != Pred->getState()) {
+ Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
+ if (!Pred) {
+ // The node with clean temporaries already exists. We might have reached
+ // it on a path on which we initialize different temporaries.
+ return;
+ }
+ }
+ }
+ assert(areAllObjectsFullyConstructed(Pred->getState(),
+ Pred->getLocationContext(),
+ Pred->getStackFrame()->getParent()));
+
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
StateMgr.EndPath(Pred->getState());
@@ -1943,12 +2296,10 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead);
// Notify checkers.
- for (ExplodedNodeSet::iterator I = AfterRemovedDead.begin(),
- E = AfterRemovedDead.end(); I != E; ++I) {
- getCheckerManager().runCheckersForEndFunction(BC, Dst, *I, *this);
- }
+ for (const auto I : AfterRemovedDead)
+ getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS);
} else {
- getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this);
+ getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS);
}
Engine.enqueueEndOfFunction(Dst, RS);
@@ -1957,7 +2308,8 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
/// ProcessSwitch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a switch statement.
void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
- typedef SwitchNodeBuilder::iterator iterator;
+ using iterator = SwitchNodeBuilder::iterator;
+
ProgramStateRef state = builder.getState();
const Expr *CondE = builder.getCondition();
SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext());
@@ -2046,16 +2398,16 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
- if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
// C permits "extern void v", and if you cast the address to a valid type,
// you can even do things with it. We simply pretend
assert(Ex->isGLValue() || VD->getType()->isVoidType());
const LocationContext *LocCtxt = Pred->getLocationContext();
const Decl *D = LocCtxt->getDecl();
- const auto *MD = D ? dyn_cast<CXXMethodDecl>(D) : nullptr;
+ const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
- SVal V;
- bool IsReference;
+ Optional<std::pair<SVal, QualType>> VInfo;
+
if (AMgr.options.shouldInlineLambdas() && DeclRefEx &&
DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
MD->getParent()->isLambda()) {
@@ -2064,25 +2416,23 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
FieldDecl *LambdaThisCaptureField;
CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
- const FieldDecl *FD = LambdaCaptureFields[VD];
- if (!FD) {
- // When a constant is captured, sometimes no corresponding field is
- // created in the lambda object.
- assert(VD->getType().isConstQualified());
- V = state->getLValue(VD, LocCtxt);
- IsReference = false;
- } else {
+
+ // Sema follows a sequence of complex rules to determine whether the
+ // variable should be captured.
+ if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
Loc CXXThis =
- svalBuilder.getCXXThis(MD, LocCtxt->getCurrentStackFrame());
+ svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame());
SVal CXXThisVal = state->getSVal(CXXThis);
- V = state->getLValue(FD, CXXThisVal);
- IsReference = FD->getType()->isReferenceType();
+ VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType());
}
- } else {
- V = state->getLValue(VD, LocCtxt);
- IsReference = VD->getType()->isReferenceType();
}
+ if (!VInfo)
+ VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType());
+
+ SVal V = VInfo->first;
+ bool IsReference = VInfo->second->isReferenceType();
+
// For references, the 'lvalue' is the pointer address stored in the
// reference region.
if (IsReference) {
@@ -2096,13 +2446,13 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramPoint::PostLValueKind);
return;
}
- if (const EnumConstantDecl *ED = dyn_cast<EnumConstantDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) {
assert(!Ex->isGLValue());
SVal V = svalBuilder.makeIntVal(ED->getInitVal());
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
return;
}
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
SVal V = svalBuilder.getFunctionPointer(FD);
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
@@ -2118,7 +2468,12 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
currBldrCtx->blockCount());
state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true);
Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ ProgramPoint::PostLValueKind);
+ return;
+ }
+ if (isa<BindingDecl>(D)) {
+ // FIXME: proper support for bound declarations.
+ // For now, let's just prevent crashing.
return;
}
@@ -2151,9 +2506,17 @@ void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
ProgramStateRef state = Node->getState();
if (IsGLValueLike) {
- SVal V = state->getLValue(A->getType(),
- state->getSVal(Idx, LCtx),
- state->getSVal(Base, LCtx));
+ QualType T = A->getType();
+
+ // One of the forbidden LValue types! We still need to have sensible
+ // symbolic locations to represent this stuff. Note that arithmetic on
+ // void pointers is a GCC extension.
+ if (T->isVoidType())
+ T = getContext().CharTy;
+
+ SVal V = state->getLValue(T,
+ state->getSVal(Idx, LCtx),
+ state->getSVal(Base, LCtx));
Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr,
ProgramPoint::PostLValueKind);
} else if (IsVectorType) {
@@ -2171,41 +2534,36 @@ a vector and not a forbidden lvalue type");
/// VisitMemberExpr - Transfer function for member expressions.
void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
-
// FIXME: Prechecks eventually go in ::Visit().
ExplodedNodeSet CheckedSet;
getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this);
- ExplodedNodeSet EvalSet;
- ValueDecl *Member = M->getMemberDecl();
+ ExplodedNodeSet EvalSet;
+ ValueDecl *Member = M->getMemberDecl();
// Handle static member variables and enum constants accessed via
// member syntax.
- if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
- ExplodedNodeSet Dst;
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I != E; ++I) {
- VisitCommonDeclRefExpr(M, Member, Pred, EvalSet);
- }
+ if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
+ for (const auto I : CheckedSet)
+ VisitCommonDeclRefExpr(M, Member, I, EvalSet);
} else {
StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx);
ExplodedNodeSet Tmp;
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I != E; ++I) {
- ProgramStateRef state = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ for (const auto I : CheckedSet) {
+ ProgramStateRef state = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
Expr *BaseExpr = M->getBase();
// Handle C++ method calls.
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Member)) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
if (MD->isInstance())
state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
SVal MDVal = svalBuilder.getFunctionPointer(MD);
state = state->BindExpr(M, LCtx, MDVal);
- Bldr.generateNode(M, *I, state);
+ Bldr.generateNode(M, I, state);
continue;
}
@@ -2213,7 +2571,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
SVal baseExprVal = state->getSVal(BaseExpr, LCtx);
- FieldDecl *field = cast<FieldDecl>(Member);
+ const auto *field = cast<FieldDecl>(Member);
SVal L = state->getLValue(field, baseExprVal);
if (M->isGLValue() || M->getType()->isArrayType()) {
@@ -2223,8 +2581,8 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// pointers as soon as they are used.
if (!M->isGLValue()) {
assert(M->getType()->isArrayType());
- const ImplicitCastExpr *PE =
- dyn_cast<ImplicitCastExpr>((*I)->getParentMap().getParentIgnoreParens(M));
+ const auto *PE =
+ dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M));
if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) {
llvm_unreachable("should always be wrapped in ArrayToPointerDecay");
}
@@ -2237,11 +2595,11 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
L = UnknownVal();
}
- Bldr.generateNode(M, *I, state->BindExpr(M, LCtx, L), nullptr,
+ Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr,
ProgramPoint::PostLValueKind);
} else {
- Bldr.takeNodes(*I);
- evalLoad(Tmp, M, M, *I, state, L);
+ Bldr.takeNodes(I);
+ evalLoad(Tmp, M, M, I, state, L);
Bldr.addNodes(Tmp);
}
}
@@ -2261,10 +2619,9 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
ExplodedNodeSet AfterInvalidateSet;
StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = AfterPreSet.begin(), E = AfterPreSet.end();
- I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
- const LocationContext *LCtx = (*I)->getLocationContext();
+ for (const auto I : AfterPreSet) {
+ ProgramStateRef State = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
SmallVector<SVal, 8> ValuesToInvalidate;
for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) {
@@ -2281,7 +2638,7 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
SVal ResultVal = UnknownVal();
State = State->BindExpr(AE, LCtx, ResultVal);
- Bldr.generateNode(AE, *I, State, nullptr,
+ Bldr.generateNode(AE, I, State, nullptr,
ProgramPoint::PostStmtKind);
}
@@ -2324,15 +2681,7 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
// Otherwise, find all symbols referenced by 'val' that we are tracking
// and stop tracking them.
- CollectReachableSymbolsCallback Scanner =
- State->scanReachableSymbols<CollectReachableSymbolsCallback>(Val);
- const InvalidatedSymbols &EscapedSymbols = Scanner.getSymbols();
- State = getCheckerManager().runCheckersForPointerEscape(State,
- EscapedSymbols,
- /*CallEvent*/ nullptr,
- PSK_EscapeOnBind,
- nullptr);
-
+ State = escapeValue(State, Val, PSK_EscapeOnBind);
return State;
}
@@ -2343,7 +2692,6 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
ArrayRef<const MemRegion *> Regions,
const CallEvent *Call,
RegionAndSymbolInvalidationTraits &ITraits) {
-
if (!Invalidated || Invalidated->empty())
return State;
@@ -2357,16 +2705,13 @@ ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
// If the symbols were invalidated by a call, we want to find out which ones
// were invalidated directly due to being arguments to the call.
InvalidatedSymbols SymbolsDirectlyInvalidated;
- for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
- E = ExplicitRegions.end(); I != E; ++I) {
- if (const SymbolicRegion *R = (*I)->StripCasts()->getAs<SymbolicRegion>())
+ for (const auto I : ExplicitRegions) {
+ if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>())
SymbolsDirectlyInvalidated.insert(R->getSymbol());
}
InvalidatedSymbols SymbolsIndirectlyInvalidated;
- for (InvalidatedSymbols::const_iterator I=Invalidated->begin(),
- E = Invalidated->end(); I!=E; ++I) {
- SymbolRef sym = *I;
+ for (const auto &sym : *Invalidated) {
if (SymbolsDirectlyInvalidated.count(sym))
continue;
SymbolsIndirectlyInvalidated.insert(sym);
@@ -2390,7 +2735,6 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
ExplodedNode *Pred,
SVal location, SVal Val,
bool atDeclInit, const ProgramPoint *PP) {
-
const LocationContext *LC = Pred->getLocationContext();
PostStmt PS(StoreE, LC);
if (!PP)
@@ -2414,9 +2758,7 @@ void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
return;
}
- for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
- I!=E; ++I) {
- ExplodedNode *PredI = *I;
+ for (const auto PredI : CheckedSet) {
ProgramStateRef state = PredI->getState();
state = processPointerEscapedOnBind(state, location, Val, LC);
@@ -2465,8 +2807,8 @@ void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
if (location.isUndef())
return;
- for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI)
- evalBind(Dst, StoreE, *NI, location, Val, false);
+ for (const auto I : Tmp)
+ evalBind(Dst, StoreE, I, location, Val, false);
}
void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
@@ -2476,46 +2818,8 @@ void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
ProgramStateRef state,
SVal location,
const ProgramPointTag *tag,
- QualType LoadTy)
-{
+ QualType LoadTy) {
assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc.");
-
- // Are we loading from a region? This actually results in two loads; one
- // to fetch the address of the referenced value and one to fetch the
- // referenced value.
- if (const TypedValueRegion *TR =
- dyn_cast_or_null<TypedValueRegion>(location.getAsRegion())) {
-
- QualType ValTy = TR->getValueType();
- if (const ReferenceType *RT = ValTy->getAs<ReferenceType>()) {
- static SimpleProgramPointTag
- loadReferenceTag(TagProviderName, "Load Reference");
- ExplodedNodeSet Tmp;
- evalLoadCommon(Tmp, NodeEx, BoundEx, Pred, state,
- location, &loadReferenceTag,
- getContext().getPointerType(RT->getPointeeType()));
-
- // Perform the load from the referenced value.
- for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) {
- state = (*I)->getState();
- location = state->getSVal(BoundEx, (*I)->getLocationContext());
- evalLoadCommon(Dst, NodeEx, BoundEx, *I, state, location, tag, LoadTy);
- }
- return;
- }
- }
-
- evalLoadCommon(Dst, NodeEx, BoundEx, Pred, state, location, tag, LoadTy);
-}
-
-void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
- const Expr *NodeEx,
- const Expr *BoundEx,
- ExplodedNode *Pred,
- ProgramStateRef state,
- SVal location,
- const ProgramPointTag *tag,
- QualType LoadTy) {
assert(NodeEx);
assert(BoundEx);
// Evaluate the location (checks for bad dereferences).
@@ -2529,9 +2833,9 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
return;
// Proceed with the load.
- for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) {
- state = (*NI)->getState();
- const LocationContext *LCtx = (*NI)->getLocationContext();
+ for (const auto I : Tmp) {
+ state = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
SVal V = UnknownVal();
if (location.isValid()) {
@@ -2540,7 +2844,7 @@ void ExprEngine::evalLoadCommon(ExplodedNodeSet &Dst,
V = state->getSVal(location.castAs<Loc>(), LoadTy);
}
- Bldr.generateNode(NodeEx, *NI, state->BindExpr(BoundEx, LCtx, V), tag,
+ Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag,
ProgramPoint::PostLoadKind);
}
}
@@ -2597,8 +2901,7 @@ void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
const Expr *Ex) {
StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx);
- for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) {
- ExplodedNode *Pred = *I;
+ for (const auto Pred : Src) {
// Test if the previous node was as the same expression. This can happen
// when the expression fails to evaluate to anything meaningful and
// (as an optimization) we don't generate a node.
@@ -2648,7 +2951,7 @@ void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
for (const Expr *O : A->outputs()) {
SVal X = state->getSVal(O, Pred->getLocationContext());
- assert (!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
+ assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
if (Optional<Loc> LV = X.getAs<Loc>())
state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
@@ -2672,16 +2975,15 @@ static ExprEngine* GraphPrintCheckerState;
static SourceManager* GraphPrintSourceManager;
namespace llvm {
-template<>
-struct DOTGraphTraits<ExplodedNode*> :
- public DefaultDOTGraphTraits {
- DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {}
+template<>
+struct DOTGraphTraits<ExplodedNode*> : public DefaultDOTGraphTraits {
+ DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
// FIXME: Since we do not cache error nodes in ExprEngine now, this does not
// work.
static std::string getNodeAttributes(const ExplodedNode *N, void*) {
- return "";
+ return {};
}
// De-duplicate some source location pretty-printing.
@@ -2694,15 +2996,8 @@ struct DOTGraphTraits<ExplodedNode*> :
<< "\\l";
}
}
- static void printLocation2(raw_ostream &Out, SourceLocation SLoc) {
- if (SLoc.isFileID() && GraphPrintSourceManager->isInMainFile(SLoc))
- Out << "line " << GraphPrintSourceManager->getExpansionLineNumber(SLoc);
- else
- SLoc.print(Out, *GraphPrintSourceManager);
- }
static std::string getNodeLabel(const ExplodedNode *N, void*){
-
std::string sbuf;
llvm::raw_string_ostream Out(sbuf);
@@ -2710,14 +3005,13 @@ struct DOTGraphTraits<ExplodedNode*> :
ProgramPoint Loc = N->getLocation();
switch (Loc.getKind()) {
- case ProgramPoint::BlockEntranceKind: {
+ case ProgramPoint::BlockEntranceKind:
Out << "Block Entrance: B"
<< Loc.castAs<BlockEntrance>().getBlock()->getBlockID();
break;
- }
case ProgramPoint::BlockExitKind:
- assert (false);
+ assert(false);
break;
case ProgramPoint::CallEnterKind:
@@ -2808,7 +3102,7 @@ struct DOTGraphTraits<ExplodedNode*> :
const Stmt *Label = E.getDst()->getLabel();
if (Label) {
- if (const CaseStmt *C = dyn_cast<CaseStmt>(Label)) {
+ if (const auto *C = dyn_cast<CaseStmt>(Label)) {
Out << "\\lcase ";
LangOptions LO; // FIXME.
if (C->getLHS())
@@ -2822,7 +3116,7 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << ":";
}
else {
- assert (isa<DefaultStmt>(Label));
+ assert(isa<DefaultStmt>(Label));
Out << "\\ldefault:";
}
}
@@ -2863,6 +3157,8 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "\\lPostStore\\l";
else if (Loc.getAs<PostLValue>())
Out << "\\lPostLValue\\l";
+ else if (Loc.getAs<PostAllocatorCall>())
+ Out << "\\lPostAllocatorCall\\l";
break;
}
@@ -2872,40 +3168,7 @@ struct DOTGraphTraits<ExplodedNode*> :
Out << "\\|StateID: " << (const void*) state.get()
<< " NodeID: " << (const void*) N << "\\|";
- // Analysis stack backtrace.
- Out << "Location context stack (from current to outer):\\l";
- const LocationContext *LC = Loc.getLocationContext();
- unsigned Idx = 0;
- for (; LC; LC = LC->getParent(), ++Idx) {
- Out << Idx << ". (" << (const void *)LC << ") ";
- switch (LC->getKind()) {
- case LocationContext::StackFrame:
- if (const NamedDecl *D = dyn_cast<NamedDecl>(LC->getDecl()))
- Out << "Calling " << D->getQualifiedNameAsString();
- else
- Out << "Calling anonymous code";
- if (const Stmt *S = cast<StackFrameContext>(LC)->getCallSite()) {
- Out << " at ";
- printLocation2(Out, S->getLocStart());
- }
- break;
- case LocationContext::Block:
- Out << "Invoking block";
- if (const Decl *D = cast<BlockInvocationContext>(LC)->getBlockDecl()) {
- Out << " defined at ";
- printLocation2(Out, D->getLocStart());
- }
- break;
- case LocationContext::Scope:
- Out << "Entering scope";
- // FIXME: Add more info once ScopeContext is activated.
- break;
- }
- Out << "\\l";
- }
- Out << "\\l";
-
- state->printDOT(Out);
+ state->printDOT(Out, N->getLocationContext());
Out << "\\l";
@@ -2916,23 +3179,24 @@ struct DOTGraphTraits<ExplodedNode*> :
return Out.str();
}
};
-} // end llvm namespace
+
+} // namespace llvm
#endif
void ExprEngine::ViewGraph(bool trim) {
#ifndef NDEBUG
if (trim) {
- std::vector<const ExplodedNode*> Src;
+ std::vector<const ExplodedNode *> Src;
// Flush any outstanding reports to make sure we cover all the nodes.
// This does not cause them to get displayed.
- for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I)
- const_cast<BugType*>(*I)->FlushReports(BR);
+ for (const auto I : BR)
+ const_cast<BugType *>(I)->FlushReports(BR);
// Iterate through the reports and get their nodes.
for (BugReporter::EQClasses_iterator
EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) {
- ExplodedNode *N = const_cast<ExplodedNode*>(EI->begin()->getErrorNode());
+ const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode());
if (N) Src.push_back(N);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 3e7a50365f50..c7b1a9ac82f0 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -20,7 +20,7 @@ using namespace clang;
using namespace ento;
using llvm::APSInt;
-/// \brief Optionally conjure and return a symbol for offset when processing
+/// Optionally conjure and return a symbol for offset when processing
/// an expression \p Expression.
/// If \p Other is a location, conjure a symbol for \p Symbol
/// (offset) if it is unknown so that memory arithmetic always
@@ -257,13 +257,23 @@ ProgramStateRef ExprEngine::handleLValueBitCast(
ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx,
QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr,
ExplodedNode* Pred) {
+ if (T->isLValueReferenceType()) {
+ assert(!CastE->getType()->isLValueReferenceType());
+ ExTy = getContext().getLValueReferenceType(ExTy);
+ } else if (T->isRValueReferenceType()) {
+ assert(!CastE->getType()->isRValueReferenceType());
+ ExTy = getContext().getRValueReferenceType(ExTy);
+ }
// Delegate to SValBuilder to process.
- SVal V = state->getSVal(Ex, LCtx);
- V = svalBuilder.evalCast(V, T, ExTy);
+ SVal OrigV = state->getSVal(Ex, LCtx);
+ SVal V = svalBuilder.evalCast(OrigV, T, ExTy);
// Negate the result if we're treating the boolean as a signed i1
if (CastE->getCastKind() == CK_BooleanToSignedIntegral)
V = evalMinus(V);
state = state->BindExpr(CastE, LCtx, V);
+ if (V.isUnknown() && !OrigV.isUnknown()) {
+ state = escapeValue(state, OrigV, PSK_EscapeOther);
+ }
Bldr.generateNode(CastE, Pred, state);
return state;
@@ -580,24 +590,12 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
SVal InitVal = state->getSVal(InitEx, LC);
assert(DS->isSingleDecl());
- if (auto *CtorExpr = findDirectConstructorForCurrentCFGElement()) {
- assert(InitEx->IgnoreImplicit() == CtorExpr);
- (void)CtorExpr;
+ if (getObjectUnderConstruction(state, DS, LC)) {
+ state = finishObjectConstruction(state, DS, LC);
// We constructed the object directly in the variable.
// No need to bind anything.
B.generateNode(DS, UpdatedN, state);
} else {
- // We bound the temp obj region to the CXXConstructExpr. Now recover
- // the lazy compound value when the variable is not a reference.
- if (AMgr.getLangOpts().CPlusPlus && VD->getType()->isRecordType() &&
- !VD->getType()->isReferenceType()) {
- if (Optional<loc::MemRegionVal> M =
- InitVal.getAs<loc::MemRegionVal>()) {
- InitVal = state->getSVal(M->getRegion());
- assert(InitVal.getAs<nonloc::LazyCompoundVal>());
- }
- }
-
// Recover some path-sensitivity if a scalar value evaluated to
// UnknownVal.
if (InitVal.isUnknown()) {
@@ -760,7 +758,11 @@ void ExprEngine::VisitGuardedExpr(const Expr *Ex,
for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) {
ProgramPoint PP = N->getLocation();
if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) {
- assert(N->pred_size() == 1);
+ // If the state N has multiple predecessors P, it means that successors
+ // of P are all equivalent.
+ // In turn, that means that all nodes at P are equivalent in terms
+ // of observable behavior at N, and we can follow any of them.
+ // FIXME: a more robust solution which does not walk up the tree.
continue;
}
SrcBlock = PP.castAs<BlockEdge>().getSrc();
@@ -1062,6 +1064,7 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
// constant value. If the UnaryOperator has location type, create the
// constant with int type and pointer width.
SVal RHS;
+ SVal Result;
if (U->getType()->isAnyPointerType())
RHS = svalBuilder.makeArrayIndex(1);
@@ -1070,7 +1073,14 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
else
RHS = UnknownVal();
- SVal Result = evalBinOp(state, Op, V2, RHS, U->getType());
+ // The use of an operand of type bool with the ++ operators is deprecated
+ // but valid until C++17. And if the operand of the ++ operator is of type
+ // bool, it is set to true until C++17. Note that for '_Bool', it is also
+ // set to true when it encounters ++ operator.
+ if (U->getType()->isBooleanType() && U->isIncrementOp())
+ Result = svalBuilder.makeTruthVal(true, U->getType());
+ else
+ Result = evalBinOp(state, Op, V2, RHS, U->getType());
// Conjure a new symbol if necessary to recover precision.
if (Result.isUnknown()){
@@ -1092,7 +1102,6 @@ void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
Constraint = svalBuilder.evalEQ(state, SymVal,
svalBuilder.makeZeroVal(U->getType()));
-
state = state->assume(Constraint, false);
assert(state);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index dad93111966f..dc124fc3ff2d 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/ConstructionContext.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/ParentMap.h"
@@ -41,19 +42,30 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
const CallEvent &Call) {
SVal ThisVal;
bool AlwaysReturnsLValue;
+ const CXXRecordDecl *ThisRD = nullptr;
if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
assert(Ctor->getDecl()->isTrivial());
assert(Ctor->getDecl()->isCopyOrMoveConstructor());
ThisVal = Ctor->getCXXThisVal();
+ ThisRD = Ctor->getDecl()->getParent();
AlwaysReturnsLValue = false;
} else {
assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial());
assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() ==
OO_Equal);
ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal();
+ ThisRD = cast<CXXMethodDecl>(Call.getDecl())->getParent();
AlwaysReturnsLValue = true;
}
+ assert(ThisRD);
+ if (ThisRD->isEmpty()) {
+ // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal
+ // and bind it and RegionStore would think that the actual value
+ // in this region at this offset is unknown.
+ return;
+ }
+
const LocationContext *LCtx = Pred->getLocationContext();
ExplodedNodeSet Dst;
@@ -84,52 +96,50 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
}
-/// Returns a region representing the first element of a (possibly
-/// multi-dimensional) array.
-///
-/// On return, \p Ty will be set to the base type of the array.
-///
-/// If the type is not an array type at all, the original value is returned.
-static SVal makeZeroElementRegion(ProgramStateRef State, SVal LValue,
- QualType &Ty) {
+SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
+ QualType &Ty, bool &IsArray) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
ASTContext &Ctx = SVB.getContext();
while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
Ty = AT->getElementType();
LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue);
+ IsArray = true;
}
return LValue;
}
+std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
+ const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, EvalCallOptions &CallOpts) {
+ MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
-const MemRegion *
-ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
- ExplodedNode *Pred) {
- const LocationContext *LCtx = Pred->getLocationContext();
- ProgramStateRef State = Pred->getState();
-
- // See if we're constructing an existing region by looking at the next
- // element in the CFG.
-
- if (auto Elem = findElementDirectlyInitializedByCurrentConstructor()) {
- if (Optional<CFGStmt> StmtElem = Elem->getAs<CFGStmt>()) {
- auto *DS = cast<DeclStmt>(StmtElem->getStmt());
- if (const auto *Var = dyn_cast<VarDecl>(DS->getSingleDecl())) {
- if (Var->getInit() && Var->getInit()->IgnoreImplicit() == CE) {
- SVal LValue = State->getLValue(Var, LCtx);
- QualType Ty = Var->getType();
- LValue = makeZeroElementRegion(State, LValue, Ty);
- return LValue.getAsRegion();
- }
- }
- } else if (Optional<CFGInitializer> InitElem = Elem->getAs<CFGInitializer>()) {
- const CXXCtorInitializer *Init = InitElem->getInitializer();
+ // See if we're constructing an existing region by looking at the
+ // current construction context.
+ if (CC) {
+ switch (CC->getKind()) {
+ case ConstructionContext::CXX17ElidedCopyVariableKind:
+ case ConstructionContext::SimpleVariableKind: {
+ 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);
+ }
+ case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+ case ConstructionContext::SimpleConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ const auto *Init = ICC->getCXXCtorInitializer();
assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr =
- getSValBuilder().getCXXThis(CurCtor, LCtx->getCurrentStackFrame());
+ getSValBuilder().getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
const ValueDecl *Field;
@@ -143,92 +153,134 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
}
QualType Ty = Field->getType();
- FieldVal = makeZeroElementRegion(State, FieldVal, Ty);
- return FieldVal.getAsRegion();
+ FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
+ CallOpts.IsArrayCtorOrDtor);
+ State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
+ return std::make_pair(State, FieldVal);
}
-
- // FIXME: This will eventually need to handle new-expressions as well.
- // Don't forget to update the pre-constructor initialization code in
- // ExprEngine::VisitCXXConstructExpr.
- }
- // If we couldn't find an existing region to construct into, assume we're
- // constructing a temporary.
- MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
- return MRMgr.getCXXTempObjectRegion(CE, LCtx);
-}
-
-/// Returns true if the initializer for \Elem can be a direct
-/// constructor.
-static bool canHaveDirectConstructor(CFGElement Elem){
- // DeclStmts and CXXCtorInitializers for fields can be directly constructed.
-
- if (Optional<CFGStmt> StmtElem = Elem.getAs<CFGStmt>()) {
- if (isa<DeclStmt>(StmtElem->getStmt())) {
- return true;
+ case ConstructionContext::NewAllocatedObjectKind: {
+ if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ const auto *NE = NECC->getCXXNewExpr();
+ SVal V = *getObjectUnderConstruction(State, NE, LCtx);
+ if (const SubRegion *MR =
+ dyn_cast_or_null<SubRegion>(V.getAsRegion())) {
+ if (NE->isArray()) {
+ // TODO: In fact, we need to call the constructor for every
+ // allocated element, not just the first one!
+ CallOpts.IsArrayCtorOrDtor = true;
+ return std::make_pair(
+ State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType())));
+ }
+ return std::make_pair(State, V);
+ }
+ // TODO: Detect when the allocator returns a null pointer.
+ // Constructor shall not be called in this case.
+ }
+ break;
}
- }
-
- if (Elem.getKind() == CFGElement::Initializer) {
- return true;
- }
-
- return false;
-}
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+ // The temporary is to be managed by the parent stack frame.
+ // So build it in the parent stack frame if we're not in the
+ // top frame of the analysis.
+ const StackFrameContext *SFC = LCtx->getStackFrame();
+ if (const LocationContext *CallerLCtx = SFC->getParent()) {
+ auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
+ .getAs<CFGCXXRecordTypedCall>();
+ if (!RTC) {
+ // We were unable to find the correct construction context for the
+ // call in the parent stack frame. This is equivalent to not being
+ // able to find construction context at all.
+ break;
+ }
+ return prepareForObjectConstruction(
+ cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ RTC->getConstructionContext(), CallOpts);
+ } else {
+ // We are on the top frame of the analysis.
+ // TODO: What exactly happens when we are? Does the temporary object
+ // live long enough in the region store in this case? Would checkers
+ // think that this object immediately goes out of scope?
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ SVal V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+ return std::make_pair(State, V);
+ }
+ 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;
+ std::tie(State, V) = prepareForObjectConstruction(
+ CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
+
+ // 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);
+ }
+ case ConstructionContext::SimpleTemporaryObjectKind: {
+ const auto *TCC = cast<SimpleTemporaryObjectConstructionContext>(CC);
+ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
+ SVal V = UnknownVal();
+
+ if (MTE) {
+ if (const ValueDecl *VD = MTE->getExtendingDecl()) {
+ assert(MTE->getStorageDuration() != SD_FullExpression);
+ if (!VD->getType()->isReferenceType()) {
+ // We're lifetime-extended by a surrounding aggregate.
+ // Automatic destructors aren't quite working in this case
+ // on the CFG side. We should warn the caller about that.
+ // FIXME: Is there a better way to retrieve this information from
+ // the MaterializeTemporaryExpr?
+ CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
+ }
+ }
-Optional<CFGElement>
-ExprEngine::findElementDirectlyInitializedByCurrentConstructor() {
- const NodeBuilderContext &CurrBldrCtx = getBuilderContext();
- // See if we're constructing an existing region by looking at the next
- // element in the CFG.
- const CFGBlock *B = CurrBldrCtx.getBlock();
- assert(isa<CXXConstructExpr>(((*B)[currStmtIdx]).castAs<CFGStmt>().getStmt()));
- unsigned int NextStmtIdx = currStmtIdx + 1;
- if (NextStmtIdx >= B->size())
- return None;
-
- CFGElement Next = (*B)[NextStmtIdx];
-
- // Is this a destructor? If so, we might be in the middle of an assignment
- // to a local or member: look ahead one more element to see what we find.
- while (Next.getAs<CFGImplicitDtor>() && NextStmtIdx + 1 < B->size()) {
- ++NextStmtIdx;
- Next = (*B)[NextStmtIdx];
- }
+ if (MTE->getStorageDuration() == SD_Static ||
+ MTE->getStorageDuration() == SD_Thread)
+ V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
+ }
- if (canHaveDirectConstructor(Next))
- return Next;
+ if (V.isUnknown())
+ V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
- return None;
-}
+ if (BTE)
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
-const CXXConstructExpr *
-ExprEngine::findDirectConstructorForCurrentCFGElement() {
- // Go backward in the CFG to see if the previous element (ignoring
- // destructors) was a CXXConstructExpr. If so, that constructor
- // was constructed directly into an existing region.
- // This process is essentially the inverse of that performed in
- // findElementDirectlyInitializedByCurrentConstructor().
- if (currStmtIdx == 0)
- return nullptr;
-
- const CFGBlock *B = getBuilderContext().getBlock();
- assert(canHaveDirectConstructor((*B)[currStmtIdx]));
-
- unsigned int PreviousStmtIdx = currStmtIdx - 1;
- CFGElement Previous = (*B)[PreviousStmtIdx];
-
- while (Previous.getAs<CFGImplicitDtor>() && PreviousStmtIdx > 0) {
- --PreviousStmtIdx;
- Previous = (*B)[PreviousStmtIdx];
- }
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
- if (Optional<CFGStmt> PrevStmtElem = Previous.getAs<CFGStmt>()) {
- if (auto *CtorExpr = dyn_cast<CXXConstructExpr>(PrevStmtElem->getStmt())) {
- return CtorExpr;
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return std::make_pair(State, V);
+ }
}
}
-
- return nullptr;
+ // 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)));
}
void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
@@ -237,21 +289,41 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
- const MemRegion *Target = nullptr;
+ 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;
+ }
// FIXME: Handle arrays, which run the same constructor for every element.
// For now, we just run the first constructor (which should still invalidate
// the entire array).
+ EvalCallOptions CallOpts;
+ auto C = getCurrentCFGElement().getAs<CFGConstructor>();
+ assert(C || getCurrentCFGElement().getAs<CFGStmt>());
+ const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
+
switch (CE->getConstructionKind()) {
case CXXConstructExpr::CK_Complete: {
- Target = getRegionForConstructedObject(CE, Pred);
+ std::tie(State, Target) =
+ prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
break;
}
case CXXConstructExpr::CK_VirtualBase:
// Make sure we are not calling virtual base class initializers twice.
// Only the most-derived object should initialize virtual base classes.
- if (const Stmt *Outer = LCtx->getCurrentStackFrame()->getCallSite()) {
+ if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) {
const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer);
if (OuterCtor) {
switch (OuterCtor->getConstructionKind()) {
@@ -281,48 +353,59 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// otherwise always available during construction.
if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
- Target = MRMgr.getCXXTempObjectRegion(CE, LCtx);
+ Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
break;
}
// FALLTHROUGH
case CXXConstructExpr::CK_Delegating: {
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
- LCtx->getCurrentStackFrame());
+ LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
- Target = ThisVal.getAsRegion();
+ Target = ThisVal;
} else {
// Cast to the base type.
bool IsVirtual =
(CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
IsVirtual);
- Target = BaseVal.getAsRegion();
+ Target = BaseVal;
}
break;
}
}
+ if (State != Pred->getState()) {
+ static SimpleProgramPointTag T("ExprEngine",
+ "Prepare for object construction");
+ ExplodedNodeSet DstPrepare;
+ StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
+ BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
+ assert(DstPrepare.size() <= 1);
+ if (DstPrepare.size() == 0)
+ return;
+ Pred = *BldrPrepare.begin();
+ }
+
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXConstructorCall> Call =
- CEMgr.getCXXConstructorCall(CE, Target, State, LCtx);
+ CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
ExplodedNodeSet DstPreVisit;
getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+ // FIXME: Is it possible and/or useful to do this before PreStmt?
ExplodedNodeSet PreInitialized;
{
StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
- if (CE->requiresZeroInitialization()) {
- // Type of the zero doesn't matter.
- SVal ZeroVal = svalBuilder.makeZeroVal(getContext().CharTy);
-
- for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
- E = DstPreVisit.end();
- I != E; ++I) {
- ProgramStateRef State = (*I)->getState();
+ for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
+ E = DstPreVisit.end();
+ I != E; ++I) {
+ ProgramStateRef State = (*I)->getState();
+ if (CE->requiresZeroInitialization()) {
// FIXME: Once we properly handle constructors in new-expressions, we'll
// need to invalidate the region before setting a default value, to make
// sure there aren't any lingering bindings around. This probably needs
@@ -335,10 +418,11 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
// actually make things worse. Placement new makes this tricky as well,
// since it's then possible to be initializing one part of a multi-
// dimensional array.
- State = State->bindDefault(loc::MemRegionVal(Target), ZeroVal, LCtx);
- Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
- ProgramPoint::PreStmtKind);
+ State = State->bindDefaultZero(Target, LCtx);
}
+
+ Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
+ ProgramPoint::PreStmtKind);
}
}
@@ -349,10 +433,9 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
ExplodedNodeSet DstEvaluated;
StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
- bool IsArray = isa<ElementRegion>(Target);
if (CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
- !IsArray) {
+ !CallOpts.IsArrayCtorOrDtor) {
// FIXME: Handle other kinds of trivial constructors as well.
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
@@ -361,10 +444,10 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
} else {
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call);
+ defaultEvalCall(Bldr, *I, *Call, CallOpts);
}
- // If the CFG was contructed without elements for temporary destructors
+ // If the CFG was constructed without elements for temporary destructors
// and the just-called constructor created a temporary object then
// stop exploration if the temporary object has a noreturn constructor.
// This can lose coverage because the destructor, if it were present
@@ -373,20 +456,30 @@ 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()) {
+ const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
+ if (Target && isa<CXXTempObjectRegion>(Target) &&
+ 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
+ // in processCallExit. But we don't have that implemented at the moment,
+ // so if you hit this assertion, see if you can avoid inlining
+ // the respective constructor when analyzer-config cfg-temporary-dtors
+ // is set to false.
+ // Otherwise there's nothing wrong with inlining such constructor.
+ assert(!DstEvaluated.empty() &&
+ "We should not have inlined this constructor!");
for (ExplodedNode *N : DstEvaluated) {
Bldr.generateSink(CE, N, N->getState());
}
- // There is no need to run the PostCall and PostStmtchecker
+ // There is no need to run the PostCall and PostStmt checker
// callbacks because we just generated sinks on all nodes in th
// frontier.
return;
}
- }
+ }
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(DstPostCall, DstEvaluated,
@@ -399,19 +492,11 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
const Stmt *S,
bool IsBaseDtor,
ExplodedNode *Pred,
- ExplodedNodeSet &Dst) {
+ ExplodedNodeSet &Dst,
+ const EvalCallOptions &CallOpts) {
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
- // FIXME: We need to run the same destructor on every element of the array.
- // This workaround will just run the first destructor (which will still
- // invalidate the entire array).
- SVal DestVal = UnknownVal();
- if (Dest)
- DestVal = loc::MemRegionVal(Dest);
- DestVal = makeZeroElementRegion(State, DestVal, ObjectType);
- Dest = DestVal.getAsRegion();
-
const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
@@ -432,7 +517,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx);
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call);
+ defaultEvalCall(Bldr, *I, *Call, CallOpts);
ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
@@ -455,15 +540,58 @@ void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
*Call, *this);
- ExplodedNodeSet DstInvalidated;
- StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx);
- for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
- I != E; ++I)
- defaultEvalCall(Bldr, *I, *Call);
- getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
- *Call, *this);
-}
+ ExplodedNodeSet DstPostCall;
+ StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx);
+ for (auto I : DstPreCall) {
+ // FIXME: Provide evalCall for checkers?
+ defaultEvalCall(CallBldr, I, *Call);
+ }
+ // If the call is inlined, DstPostCall will be empty and we bail out now.
+
+ // Store return value of operator new() for future use, until the actual
+ // CXXNewExpr gets processed.
+ ExplodedNodeSet DstPostValue;
+ StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx);
+ for (auto 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
+ // this is not correct because the operator new's prototype always says that
+ // it returns a 'void *'. So we should change the type of the symbol,
+ // and then evaluate the cast over the symbolic pointer from 'void *' to
+ // the object pointer type. But without changing the symbol's type it
+ // is breaking too much to evaluate the no-op symbolic cast over it, so we
+ // skip it for now.
+ ProgramStateRef State = I->getState();
+ SVal RetVal = State->getSVal(CNE, LCtx);
+
+ // If this allocation function is not declared as non-throwing, failures
+ // /must/ be signalled by exceptions, and thus the return value will never
+ // be NULL. -fno-exceptions does not influence this semantics.
+ // FIXME: GCC has a -fcheck-new option, which forces it to consider the case
+ // where new can return NULL. If we end up supporting that option, we can
+ // consider adding a check for it here.
+ // C++11 [basic.stc.dynamic.allocation]p3.
+ if (const FunctionDecl *FD = CNE->getOperatorNew()) {
+ QualType Ty = FD->getType();
+ if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
+ if (!ProtoType->isNothrow())
+ State = State->assume(RetVal.castAs<DefinedOrUnknownSVal>(), true);
+ }
+ ValueBldr.generateNode(
+ CNE, I, addObjectUnderConstruction(State, CNE, LCtx, RetVal));
+ }
+
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ DstPostValue, *Call, *this);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I,
+ *this);
+ }
+}
void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
@@ -474,69 +602,74 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
unsigned blockCount = currBldrCtx->blockCount();
const LocationContext *LCtx = Pred->getLocationContext();
- DefinedOrUnknownSVal symVal = UnknownVal();
+ SVal symVal = UnknownVal();
FunctionDecl *FD = CNE->getOperatorNew();
- bool IsStandardGlobalOpNewFunction = false;
- if (FD && !isa<CXXMethodDecl>(FD) && !FD->isVariadic()) {
- if (FD->getNumParams() == 2) {
- QualType T = FD->getParamDecl(1)->getType();
- if (const IdentifierInfo *II = T.getBaseTypeIdentifier())
- // NoThrow placement new behaves as a standard new.
- IsStandardGlobalOpNewFunction = II->getName().equals("nothrow_t");
- }
- else
- // Placement forms are considered non-standard.
- IsStandardGlobalOpNewFunction = (FD->getNumParams() == 1);
+ bool IsStandardGlobalOpNewFunction =
+ FD->isReplaceableGlobalAllocationFunction();
+
+ ProgramStateRef State = Pred->getState();
+
+ // Retrieve the stored operator new() return value.
+ if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+ symVal = *getObjectUnderConstruction(State, CNE, LCtx);
+ State = finishObjectConstruction(State, CNE, LCtx);
}
// We assume all standard global 'operator new' functions allocate memory in
// heap. We realize this is an approximation that might not correctly model
// a custom global allocator.
- if (IsStandardGlobalOpNewFunction)
- symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
- else
- symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(),
- blockCount);
+ if (symVal.isUnknown()) {
+ if (IsStandardGlobalOpNewFunction)
+ symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
+ else
+ symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(),
+ blockCount);
+ }
- ProgramStateRef State = Pred->getState();
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXAllocatorCall> Call =
CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
- // Invalidate placement args.
- // FIXME: Once we figure out how we want allocators to work,
- // we should be using the usual pre-/(default-)eval-/post-call checks here.
- State = Call->invalidateRegions(blockCount);
- if (!State)
- return;
+ if (!AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
+ // Invalidate placement args.
+ // FIXME: Once we figure out how we want allocators to work,
+ // we should be using the usual pre-/(default-)eval-/post-call checks here.
+ State = Call->invalidateRegions(blockCount);
+ if (!State)
+ return;
- // If this allocation function is not declared as non-throwing, failures
- // /must/ be signalled by exceptions, and thus the return value will never be
- // NULL. -fno-exceptions does not influence this semantics.
- // FIXME: GCC has a -fcheck-new option, which forces it to consider the case
- // where new can return NULL. If we end up supporting that option, we can
- // consider adding a check for it here.
- // C++11 [basic.stc.dynamic.allocation]p3.
- if (FD) {
- QualType Ty = FD->getType();
- if (const FunctionProtoType *ProtoType = Ty->getAs<FunctionProtoType>())
- if (!ProtoType->isNothrow(getContext()))
- State = State->assume(symVal, true);
+ // If this allocation function is not declared as non-throwing, failures
+ // /must/ be signalled by exceptions, and thus the return value will never
+ // be NULL. -fno-exceptions does not influence this semantics.
+ // FIXME: GCC has a -fcheck-new option, which forces it to consider the case
+ // where new can return NULL. If we end up supporting that option, we can
+ // consider adding a check for it here.
+ // C++11 [basic.stc.dynamic.allocation]p3.
+ if (FD) {
+ QualType Ty = FD->getType();
+ if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
+ if (!ProtoType->isNothrow())
+ if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
+ State = State->assume(*dSymVal, true);
+ }
}
StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ SVal Result = symVal;
+
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- const SubRegion *NewReg =
- symVal.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();
- QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
- const ElementRegion *EleReg =
- getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
- State = State->BindExpr(CNE, Pred->getLocationContext(),
- loc::MemRegionVal(EleReg));
+ if (const SubRegion *NewReg =
+ dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) {
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ const ElementRegion *EleReg =
+ getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ Result = loc::MemRegionVal(EleReg);
+ }
+ State = State->BindExpr(CNE, Pred->getLocationContext(), Result);
Bldr.generateNode(CNE, Pred, State);
return;
}
@@ -545,7 +678,6 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
// CXXNewExpr, we need to make sure that the constructed object is not
// immediately invalidated here. (The placement call should happen before
// the constructor call anyway.)
- SVal Result = symVal;
if (FD && FD->isReservedGlobalPlacementOperator()) {
// Non-array placement new should always return the placement location.
SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index caf86b26b66d..3ee67f3d6882 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -15,8 +15,8 @@
#include "PrettyStackTraceLocationContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
-#include "clang/AST/ParentMap.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 "llvm/ADT/SmallSet.h"
@@ -74,15 +74,14 @@ static std::pair<const Stmt*,
const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
const Stmt *S = nullptr;
const CFGBlock *Blk = nullptr;
- const StackFrameContext *SF =
- Node->getLocation().getLocationContext()->getCurrentStackFrame();
+ const StackFrameContext *SF = Node->getStackFrame();
// Back up through the ExplodedGraph until we reach a statement node in this
// stack frame.
while (Node) {
const ProgramPoint &PP = Node->getLocation();
- if (PP.getLocationContext()->getCurrentStackFrame() == SF) {
+ if (PP.getStackFrame() == SF) {
if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
S = SP->getStmt();
break;
@@ -121,7 +120,7 @@ static std::pair<const Stmt*,
/// Adjusts a return value when the called function's return type does not
/// match the caller's expression type. This can happen when a dynamic call
-/// is devirtualized, and the overridding method has a covariant (more specific)
+/// is devirtualized, and the overriding method has a covariant (more specific)
/// return type than the parent's method. For C++ objects, this means we need
/// to add base casts.
static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
@@ -193,23 +192,6 @@ static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl();
}
-/// Returns true if the CXXConstructExpr \p E was intended to construct a
-/// prvalue for the region in \p V.
-///
-/// Note that we can't just test for rvalue vs. glvalue because
-/// CXXConstructExprs embedded in DeclStmts and initializers are considered
-/// rvalues by the AST, and the analyzer would like to treat them as lvalues.
-static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) {
- if (E->isGLValue())
- return false;
-
- const MemRegion *MR = V.getAsRegion();
- if (!MR)
- return false;
-
- return isa<CXXTempObjectRegion>(MR);
-}
-
/// The call exit is simulated with a sequence of nodes, which occur between
/// CallExitBegin and CallExitEnd. The following operations occur between the
/// two program points:
@@ -221,13 +203,12 @@ static bool isTemporaryPRValue(const CXXConstructExpr *E, SVal V) {
void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
// Step 1 CEBNode was generated before the call.
PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext());
- const StackFrameContext *calleeCtx =
- CEBNode->getLocationContext()->getCurrentStackFrame();
+ const StackFrameContext *calleeCtx = CEBNode->getStackFrame();
// The parent context might not be a stack frame, so make sure we
// look up the first enclosing stack frame.
const StackFrameContext *callerCtx =
- calleeCtx->getParent()->getCurrentStackFrame();
+ calleeCtx->getParent()->getStackFrame();
const Stmt *CE = calleeCtx->getCallSite();
ProgramStateRef state = CEBNode->getState();
@@ -269,13 +250,24 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
loc::MemRegionVal This =
svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
SVal ThisV = state->getSVal(This);
-
- // If the constructed object is a temporary prvalue, get its bindings.
- if (isTemporaryPRValue(CCE, ThisV))
- ThisV = state->getSVal(ThisV.castAs<Loc>());
-
+ ThisV = state->getSVal(ThisV.castAs<Loc>());
state = state->BindExpr(CCE, callerCtx, ThisV);
}
+
+ if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) {
+ // We are currently evaluating a CXXNewAllocator CFGElement. It takes a
+ // while to reach the actual CXXNewExpr element from here, so keep the
+ // region for later use.
+ // Additionally cast the return value of the inlined operator new
+ // (which is of type 'void *') to the correct object type.
+ SVal AllocV = state->getSVal(CNE, callerCtx);
+ AllocV = svalBuilder.evalCast(
+ AllocV, CNE->getType(),
+ getContext().getPointerType(getContext().VoidTy));
+
+ state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(),
+ AllocV);
+ }
}
// Step 3: BindedRetNode -> CleanedNodes
@@ -315,6 +307,7 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
CallExitEnd Loc(calleeCtx, callerCtx);
bool isNew;
ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState();
+
ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew);
CEENode->addPredecessor(*I, G);
if (!isNew)
@@ -331,16 +324,32 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
ExplodedNodeSet DstPostCall;
- getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
- *UpdatedCall, *this,
- /*WasInlined=*/true);
-
+ if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ CEENode, *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE,
+ *getObjectUnderConstruction(I->getState(), CNE,
+ calleeCtx->getParent()),
+ DstPostCall, I, *this,
+ /*WasInlined=*/true);
+ }
+ } else {
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
+ *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ }
ExplodedNodeSet Dst;
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
*this,
/*WasInlined=*/true);
- } else if (CE) {
+ } else if (CE &&
+ !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr.
+ AMgr.getAnalyzerOptions().mayInlineCXXAllocator())) {
getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
*this, /*WasInlined=*/true);
} else {
@@ -407,7 +416,7 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
assert(D);
const LocationContext *CurLC = Pred->getLocationContext();
- const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
+ const StackFrameContext *CallerSFC = CurLC->getStackFrame();
const LocationContext *ParentOfCallee = CallerSFC;
if (Call.getKind() == CE_Block &&
!cast<BlockCall>(Call).isConversionFromLambda()) {
@@ -542,19 +551,45 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
}
} else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){
SVal ThisV = C->getCXXThisVal();
-
- // If the constructed object is a temporary prvalue, get its bindings.
- if (isTemporaryPRValue(cast<CXXConstructExpr>(E), ThisV))
- ThisV = State->getSVal(ThisV.castAs<Loc>());
-
+ ThisV = State->getSVal(ThisV.castAs<Loc>());
return State->BindExpr(E, LCtx, ThisV);
}
- // Conjure a symbol if the return value is unknown.
+ SVal R;
QualType ResultTy = Call.getResultType();
- SValBuilder &SVB = getSValBuilder();
unsigned Count = currBldrCtx->blockCount();
- SVal R = SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count);
+ if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
+ // Conjure a temporary if the function returns an object by value.
+ SVal Target;
+ 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);
+ assert(Target.getAsRegion());
+ // Invalidate the region so that it didn't look uninitialized. Don't notify
+ // the checkers.
+ State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx,
+ /* CausedByPointerEscape=*/false, nullptr,
+ &Call, nullptr);
+
+ R = State->getSVal(Target.castAs<Loc>(), E->getType());
+ } else {
+ // Conjure a symbol if the return value is unknown.
+
+ // See if we need to conjure a heap pointer instead of
+ // a regular unknown pointer.
+ bool IsHeapPointer = false;
+ if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
+ if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
+ // FIXME: Delegate this to evalCall in MallocChecker?
+ IsHeapPointer = true;
+ }
+
+ R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
+ : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
+ Count);
+ }
return State->BindExpr(E, LCtx, R);
}
@@ -570,17 +605,12 @@ void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
Bldr.generateNode(Call.getProgramPoint(), State, Pred);
}
-enum CallInlinePolicy {
- CIP_Allowed,
- CIP_DisallowedOnce,
- CIP_DisallowedAlways
-};
-
-static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
- const ExplodedNode *Pred,
- AnalyzerOptions &Opts) {
+ExprEngine::CallInlinePolicy
+ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
+ AnalyzerOptions &Opts,
+ const ExprEngine::EvalCallOptions &CallOpts) {
const LocationContext *CurLC = Pred->getLocationContext();
- const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
+ const StackFrameContext *CallerSFC = CurLC->getStackFrame();
switch (Call.getKind()) {
case CE_Function:
case CE_Block:
@@ -596,22 +626,24 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
+ const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
+
+ auto CCE = getCurrentCFGElement().getAs<CFGConstructor>();
+ const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
+ : nullptr;
+
+ if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
+ !Opts.mayInlineCXXAllocator())
+ return CIP_DisallowedOnce;
+
// FIXME: We don't handle constructors or destructors for arrays properly.
// Even once we do, we still need to be careful about implicitly-generated
// initializers for array fields in default move/copy constructors.
- const MemRegion *Target = Ctor.getCXXThisVal().getAsRegion();
- if (Target && isa<ElementRegion>(Target))
+ // We still allow construction into ElementRegion targets when they don't
+ // represent array elements.
+ if (CallOpts.IsArrayCtorOrDtor)
return CIP_DisallowedOnce;
- // FIXME: This is a hack. We don't use the correct region for a new
- // expression, so if we inline the constructor its result will just be
- // thrown away. This short-term hack is tracked in <rdar://problem/12180598>
- // and the longer-term possible fix is discussed in PR12014.
- const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
- if (const Stmt *Parent = CurLC->getParentMap().getParent(CtorExpr))
- if (isa<CXXNewExpr>(Parent))
- return CIP_DisallowedOnce;
-
// Inlining constructors requires including initializers in the CFG.
const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers");
@@ -626,12 +658,25 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
return CIP_DisallowedAlways;
- // FIXME: This is a hack. We don't handle temporary destructors
- // right now, so we shouldn't inline their constructors.
- if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
- if (!Target || !isa<DeclRegion>(Target))
+ if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) {
+ // If we don't handle temporary destructors, we shouldn't inline
+ // their constructors.
+ if (CallOpts.IsTemporaryCtorOrDtor &&
+ !Opts.includeTemporaryDtorsInCFG())
+ return CIP_DisallowedOnce;
+
+ // If we did not find the correct this-region, it would be pointless
+ // to inline the constructor. Instead we will simply invalidate
+ // the fake temporary target.
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
return CIP_DisallowedOnce;
+ // If the temporary is lifetime-extended by binding it to a reference-type
+ // field within an aggregate, automatic destructors don't work properly.
+ if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
+ return CIP_DisallowedOnce;
+ }
+
break;
}
case CE_CXXDestructor: {
@@ -643,13 +688,19 @@ static CallInlinePolicy mayInlineCallKind(const CallEvent &Call,
assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors");
(void)ADC;
- const CXXDestructorCall &Dtor = cast<CXXDestructorCall>(Call);
-
// FIXME: We don't handle constructors or destructors for arrays properly.
- const MemRegion *Target = Dtor.getCXXThisVal().getAsRegion();
- if (Target && isa<ElementRegion>(Target))
+ if (CallOpts.IsArrayCtorOrDtor)
+ return CIP_DisallowedOnce;
+
+ // Allow disabling temporary destructor inlining with a separate option.
+ if (CallOpts.IsTemporaryCtorOrDtor && !Opts.mayInlineCXXTemporaryDtors())
return CIP_DisallowedOnce;
+ // If we did not find the correct this-region, it would be pointless
+ // to inline the destructor. Instead we will simply invalidate
+ // the fake temporary target.
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
+ return CIP_DisallowedOnce;
break;
}
case CE_CXXAllocator:
@@ -731,8 +782,9 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) {
/// This checks static properties of the function, such as its signature and
/// CFG, to determine whether the analyzer should ever consider inlining it,
/// in any context.
-static bool mayInlineDecl(AnalysisDeclContext *CalleeADC,
- AnalyzerOptions &Opts) {
+static bool mayInlineDecl(AnalysisManager &AMgr,
+ AnalysisDeclContext *CalleeADC) {
+ AnalyzerOptions &Opts = AMgr.getAnalyzerOptions();
// FIXME: Do not inline variadic calls.
if (CallEvent::isVariadic(CalleeADC->getDecl()))
return false;
@@ -755,7 +807,7 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC,
// Conditionally control the inlining of methods on objects that look
// like C++ containers.
if (!Opts.mayInlineCXXContainerMethods())
- if (!Ctx.getSourceManager().isInMainFile(FD->getLocation()))
+ if (!AMgr.isInCodeFile(FD->getLocation()))
if (isContainerMethod(Ctx, FD))
return false;
@@ -788,7 +840,8 @@ static bool mayInlineDecl(AnalysisDeclContext *CalleeADC,
}
bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
- const ExplodedNode *Pred) {
+ const ExplodedNode *Pred,
+ const EvalCallOptions &CallOpts) {
if (!D)
return false;
@@ -797,14 +850,6 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager();
AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D);
- // Temporary object destructor processing is currently broken, so we never
- // inline them.
- // FIXME: Remove this once temp destructors are working.
- if (isa<CXXDestructorCall>(Call)) {
- if ((*currBldrCtx->getBlock())[currStmtIdx].getAs<CFGTemporaryDtor>())
- return false;
- }
-
// The auto-synthesized bodies are essential to inline as they are
// usually small and commonly used. Note: we should do this check early on to
// ensure we always inline these calls.
@@ -823,7 +868,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
} else {
// We haven't actually checked the static properties of this function yet.
// Do that now, and record our decision in the function summaries.
- if (mayInlineDecl(CalleeADC, Opts)) {
+ if (mayInlineDecl(getAnalysisManager(), CalleeADC)) {
Engine.FunctionSummaries->markMayInline(D);
} else {
Engine.FunctionSummaries->markShouldNotInline(D);
@@ -835,7 +880,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
// FIXME: this checks both static and dynamic properties of the call, which
// means we're redoing a bit of work that could be cached in the function
// summary.
- CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts);
+ CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts, CallOpts);
if (CIP != CIP_Allowed) {
if (CIP == CIP_DisallowedAlways) {
assert(!MayInline.hasValue() || MayInline.getValue());
@@ -887,7 +932,8 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) {
}
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
- const CallEvent &CallTemplate) {
+ const CallEvent &CallTemplate,
+ const EvalCallOptions &CallOpts) {
// Make sure we have the most recent state attached to the call.
ProgramStateRef State = Pred->getState();
CallEventRef<> Call = CallTemplate.cloneWithState(State);
@@ -910,7 +956,7 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
} else {
RuntimeDefinition RD = Call->getRuntimeDefinition();
const Decl *D = RD.getDecl();
- if (shouldInlineCall(*Call, D, Pred)) {
+ if (shouldInlineCall(*Call, D, Pred, CallOpts)) {
if (RD.mayHaveOtherDefinitions()) {
AnalyzerOptions &Options = getAnalysisManager().options;
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index f5e64f4a5a8c..d76b9cbcfaca 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -42,6 +42,47 @@ void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
}
+/// Generate a node in \p Bldr for an iteration statement using ObjC
+/// for-loop iterator.
+static void populateObjCForDestinationSet(
+ ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder,
+ const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV,
+ SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx,
+ StmtNodeBuilder &Bldr, bool hasElements) {
+
+ for (ExplodedNode *Pred : dstLocation) {
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
+
+ // FIXME: S is not an expression. We should not be binding values to it.
+ ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
+
+ if (auto MV = elementV.getAs<loc::MemRegionVal>())
+ if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
+ // FIXME: The proper thing to do is to really iterate over the
+ // container. We will do this with dispatch logic to the store.
+ // For now, just 'conjure' up a symbolic value.
+ QualType T = R->getValueType();
+ assert(Loc::isLocType(T));
+
+ SVal V;
+ if (hasElements) {
+ SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
+ currBldrCtx->blockCount());
+ V = svalBuilder.makeLoc(Sym);
+ } else {
+ V = svalBuilder.makeIntVal(0, T);
+ }
+
+ nextState = nextState->bindLoc(elementV, V, LCtx);
+ }
+
+ Bldr.generateNode(S, Pred, nextState);
+ }
+}
+
void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
@@ -72,60 +113,35 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
// result in state splitting.
const Stmt *elem = S->getElement();
+ const Stmt *collection = S->getCollection();
ProgramStateRef state = Pred->getState();
- SVal elementV;
+ SVal collectionV = state->getSVal(collection, Pred->getLocationContext());
- if (const DeclStmt *DS = dyn_cast<DeclStmt>(elem)) {
+ SVal elementV;
+ if (const auto *DS = dyn_cast<DeclStmt>(elem)) {
const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
assert(elemD->getInit() == nullptr);
elementV = state->getLValue(elemD, Pred->getLocationContext());
- }
- else {
+ } else {
elementV = state->getSVal(elem, Pred->getLocationContext());
}
+ bool isContainerNull = state->isNull(collectionV).isConstrainedTrue();
+
ExplodedNodeSet dstLocation;
evalLocation(dstLocation, S, elem, Pred, state, elementV, nullptr, false);
ExplodedNodeSet Tmp;
StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
- for (ExplodedNodeSet::iterator NI = dstLocation.begin(),
- NE = dstLocation.end(); NI!=NE; ++NI) {
- Pred = *NI;
- ProgramStateRef state = Pred->getState();
- const LocationContext *LCtx = Pred->getLocationContext();
-
- // Handle the case where the container still has elements.
- SVal TrueV = svalBuilder.makeTruthVal(1);
- ProgramStateRef hasElems = state->BindExpr(S, LCtx, TrueV);
-
- // Handle the case where the container has no elements.
- SVal FalseV = svalBuilder.makeTruthVal(0);
- ProgramStateRef noElems = state->BindExpr(S, LCtx, FalseV);
+ if (!isContainerNull)
+ populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
+ SymMgr, currBldrCtx, Bldr,
+ /*hasElements=*/true);
- if (Optional<loc::MemRegionVal> MV = elementV.getAs<loc::MemRegionVal>())
- if (const TypedValueRegion *R =
- dyn_cast<TypedValueRegion>(MV->getRegion())) {
- // FIXME: The proper thing to do is to really iterate over the
- // container. We will do this with dispatch logic to the store.
- // For now, just 'conjure' up a symbolic value.
- QualType T = R->getValueType();
- assert(Loc::isLocType(T));
- SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
- currBldrCtx->blockCount());
- SVal V = svalBuilder.makeLoc(Sym);
- hasElems = hasElems->bindLoc(elementV, V, LCtx);
-
- // Bind the location to 'nil' on the false branch.
- SVal nilV = svalBuilder.makeIntVal(0, T);
- noElems = noElems->bindLoc(elementV, nilV, LCtx);
- }
-
- // Create the new nodes.
- Bldr.generateNode(S, Pred, hasElems);
- Bldr.generateNode(S, Pred, noElems);
- }
+ populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
+ SymMgr, currBldrCtx, Bldr,
+ /*hasElements=*/false);
// Finally, run any custom checkers.
// FIXME: Eventually all pre- and post-checks should live in VisitStmt.
diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
index c21735b8b882..94edd84d15d2 100644
--- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp
+++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
@@ -1,4 +1,4 @@
-//== FunctionSummary.cpp - Stores summaries of functions. ----------*- C++ -*-//
+//===- FunctionSummary.cpp - Stores summaries of functions. ---------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,21 +12,20 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
+
using namespace clang;
using namespace ento;
unsigned FunctionSummariesTy::getTotalNumBasicBlocks() {
unsigned Total = 0;
- for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) {
- Total += I->second.TotalBasicBlocks;
- }
+ for (const auto &I : Map)
+ Total += I.second.TotalBasicBlocks;
return Total;
}
unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() {
unsigned Total = 0;
- for (MapTy::iterator I = Map.begin(), E = Map.end(); I != E; ++I) {
- Total += I->second.VisitedBasicBlocks.count();
- }
+ for (const auto &I : Map)
+ Total += I.second.VisitedBasicBlocks.count();
return Total;
}
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index ebf1487d4bfc..d5e5f96dee0f 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -1,4 +1,4 @@
-//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===//
+//===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,24 +11,43 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Token.h"
#include "clang/Rewrite/Core/HTMLRewrite.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
-#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+#include <set>
#include <sstream>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ento;
@@ -41,15 +60,19 @@ namespace {
class HTMLDiagnostics : public PathDiagnosticConsumer {
std::string Directory;
- bool createdDir, noDir;
+ bool createdDir = false;
+ bool noDir = false;
const Preprocessor &PP;
AnalyzerOptions &AnalyzerOpts;
const bool SupportsCrossFileDiagnostics;
+
public:
HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
const std::string& prefix,
const Preprocessor &pp,
- bool supportsMultipleFiles);
+ bool supportsMultipleFiles)
+ : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
@@ -94,20 +117,13 @@ public:
/// \return Javascript for navigating the HTML report using j/k keys.
std::string generateKeyboardNavigationJavascript();
-};
-} // end anonymous namespace
+private:
+ /// \return Javascript for displaying shortcuts help;
+ std::string showHelpJavascript();
+};
-HTMLDiagnostics::HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
- const std::string& prefix,
- const Preprocessor &pp,
- bool supportsMultipleFiles)
- : Directory(prefix),
- createdDir(false),
- noDir(false),
- PP(pp),
- AnalyzerOpts(AnalyzerOpts),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
+} // namespace
void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
PathDiagnosticConsumers &C,
@@ -130,24 +146,19 @@ void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
void HTMLDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) {
- for (std::vector<const PathDiagnostic *>::iterator it = Diags.begin(),
- et = Diags.end(); it != et; ++it) {
- ReportDiag(**it, filesMade);
- }
+ for (const auto Diag : Diags)
+ ReportDiag(*Diag, filesMade);
}
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
FilesMade *filesMade) {
-
// Create the HTML directory if it is missing.
if (!createdDir) {
createdDir = true;
if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
llvm::errs() << "warning: could not create directory '"
<< Directory << "': " << ec.message() << '\n';
-
noDir = true;
-
return;
}
}
@@ -174,7 +185,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
SmallString<128> declName("unknown");
int offsetDecl = 0;
if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
- if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue))
+ if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
declName = ND->getDeclName().getAsString();
if (const Stmt *Body = DeclWithIssue->getBody()) {
@@ -212,7 +223,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
<< "': " << EC.message() << '\n';
return;
}
-
} else {
int i = 1;
std::error_code EC;
@@ -228,10 +238,8 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
<< "-" << i << ".html";
llvm::sys::path::append(Model, Directory,
filename.str());
- EC = llvm::sys::fs::openFileForWrite(Model,
- FD,
- llvm::sys::fs::F_RW |
- llvm::sys::fs::F_Excl);
+ EC = llvm::sys::fs::openFileForReadWrite(
+ Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None);
if (EC && EC != llvm::errc::file_exists) {
llvm::errs() << "warning: could not create file '" << Model
<< "': " << EC.message() << '\n';
@@ -253,7 +261,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
const SourceManager& SMgr, const PathPieces& path, const char *declName) {
-
// Rewrite source files as HTML for every new file the path crosses
std::vector<FileID> FileIDs;
for (auto I : path) {
@@ -309,10 +316,12 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]);
if (!Buf)
- return "";
+ return {};
// Add CSS, header, and footer.
- const FileEntry* Entry = SMgr.getFileEntryForID(FileIDs[0]);
+ FileID FID =
+ path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
+ const FileEntry* Entry = SMgr.getFileEntryForID(FID);
FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName);
std::string file;
@@ -323,6 +332,114 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
return os.str();
}
+/// Write executed lines from \p D in JSON format into \p os.
+static void serializeExecutedLines(
+ const PathDiagnostic &D,
+ const PathPieces &path,
+ llvm::raw_string_ostream &os) {
+ // Copy executed lines from path diagnostics.
+ std::map<unsigned, std::set<unsigned>> ExecutedLines;
+ for (auto I = D.executedLines_begin(),
+ E = D.executedLines_end(); I != E; ++I) {
+ std::set<unsigned> &LinesInFile = ExecutedLines[I->first];
+ for (unsigned LineNo : I->second) {
+ LinesInFile.insert(LineNo);
+ }
+ }
+
+ // We need to include all lines for which any kind of diagnostics appears.
+ for (const auto &P : path) {
+ FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc();
+ FileID FID = Loc.getFileID();
+ unsigned LineNo = Loc.getLineNumber();
+ ExecutedLines[FID.getHashValue()].insert(LineNo);
+ }
+
+ os << "var relevant_lines = {";
+ for (auto I = ExecutedLines.begin(),
+ E = ExecutedLines.end(); I != E; ++I) {
+ if (I != ExecutedLines.begin())
+ os << ", ";
+
+ os << "\"" << I->first << "\": {";
+ for (unsigned LineNo : I->second) {
+ if (LineNo != *(I->second.begin()))
+ os << ", ";
+
+ os << "\"" << LineNo << "\": 1";
+ }
+ os << "}";
+ }
+
+ os << "};";
+}
+
+/// \return JavaScript for an option to only show relevant lines.
+static std::string showRelevantLinesJavascript(
+ const PathDiagnostic &D, const PathPieces &path) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "<script type='text/javascript'>\n";
+ serializeExecutedLines(D, path, os);
+ os << R"<<<(
+
+var filterCounterexample = function (hide) {
+ var tables = document.getElementsByClassName("code");
+ for (var t=0; t<tables.length; t++) {
+ var table = tables[t];
+ var file_id = table.getAttribute("data-fileid");
+ var lines_in_fid = relevant_lines[file_id];
+ if (!lines_in_fid) {
+ lines_in_fid = {};
+ }
+ var lines = table.getElementsByClassName("codeline");
+ for (var i=0; i<lines.length; i++) {
+ var el = lines[i];
+ var lineNo = el.getAttribute("data-linenumber");
+ if (!lines_in_fid[lineNo]) {
+ if (hide) {
+ el.setAttribute("hidden", "");
+ } else {
+ el.removeAttribute("hidden");
+ }
+ }
+ }
+ }
+}
+
+window.addEventListener("keydown", function (event) {
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (event.key == "S") {
+ var checked = document.getElementsByName("showCounterexample")[0].checked;
+ filterCounterexample(!checked);
+ document.getElementsByName("showCounterexample")[0].checked = !checked;
+ } else {
+ return;
+ }
+ event.preventDefault();
+}, true);
+
+document.addEventListener("DOMContentLoaded", function() {
+ document.querySelector('input[name="showCounterexample"]').onchange=
+ function (event) {
+ filterCounterexample(this.checked);
+ };
+});
+</script>
+
+<form>
+ <input type="checkbox" name="showCounterexample" id="showCounterexample" />
+ <label for="showCounterexample">
+ Show only relevant lines
+ </label>
+</form>
+)<<<";
+
+ return os.str();
+}
+
void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
const SourceManager& SMgr, const PathPieces& path, FileID FID,
const FileEntry *Entry, const char *declName) {
@@ -340,9 +457,15 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), showHelpJavascript());
+
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
generateKeyboardNavigationJavascript());
+ // Checkbox and javascript for filtering the output to the counterexample.
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+ showRelevantLinesJavascript(D, path));
+
// Add the name of the file as an <h1> tag.
{
std::string s;
@@ -379,8 +502,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
// Output any other meta data.
- for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end();
- I!=E; ++I) {
+ for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end();
+ I != E; ++I) {
os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
}
@@ -388,11 +511,24 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
</table>
<!-- REPORTSUMMARYEXTRA -->
<h3>Annotated Source Code</h3>
-<p><span class='macro'>[?]
- <span class='expansion'>Use j/k keys for keyboard navigation</span>
-</span></p>
+<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
+ to see keyboard shortcuts</p>
+<input type="checkbox" class="spoilerhider" id="showinvocation" />
+<label for="showinvocation" >Show analyzer invocation</label>
+<div class="spoiler">clang -cc1 )<<<";
+ os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation);
+ os << R"<<<(
+</div>
+<div id='tooltiphint' hidden="true">
+ <p>Keyboard shortcuts: </p>
+ <ul>
+ <li>Use 'j/k' keys for keyboard navigation</li>
+ <li>Use 'Shift+S' to show/hide relevant lines</li>
+ <li>Use '?' to toggle this window</li>
+ </ul>
+ <a href="#" onclick="toggleHelp(); return false;">Close</a>
+</div>
)<<<";
-
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
}
@@ -450,6 +586,34 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
}
+std::string HTMLDiagnostics::showHelpJavascript() {
+ return R"<<<(
+<script type='text/javascript'>
+
+var toggleHelp = function() {
+ var hint = document.querySelector("#tooltiphint");
+ var attributeName = "hidden";
+ if (hint.hasAttribute(attributeName)) {
+ hint.removeAttribute(attributeName);
+ } else {
+ hint.setAttribute("hidden", "true");
+ }
+};
+window.addEventListener("keydown", function (event) {
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (event.key == "?") {
+ toggleHelp();
+ } else {
+ return;
+ }
+ event.preventDefault();
+});
+</script>
+)<<<";
+}
+
void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr,
const PathPieces& path, FileID FID) {
// Process the path.
@@ -494,7 +658,6 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, const SourceManager& SMgr,
void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
const PathDiagnosticPiece& P,
unsigned num, unsigned max) {
-
// For now, just draw a box above the line in question, and emit the
// warning.
FullSourceLoc Pos = P.getLocation().asLocation();
@@ -634,9 +797,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
os << "</td><td>";
}
- if (const PathDiagnosticMacroPiece *MP =
- dyn_cast<PathDiagnosticMacroPiece>(&P)) {
-
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) {
os << "Within the expansion of the macro '";
// Get the name of the macro by relexing it.
@@ -707,10 +868,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Now highlight the ranges.
ArrayRef<SourceRange> Ranges = P.getRanges();
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
- E = Ranges.end(); I != E; ++I) {
- HighlightRange(R, LPosInfo.first, *I);
- }
+ for (const auto &Range : Ranges)
+ HighlightRange(R, LPosInfo.first, Range);
}
static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
@@ -726,18 +885,13 @@ static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
const PathDiagnosticMacroPiece& P,
unsigned num) {
-
- for (PathPieces::const_iterator I = P.subPieces.begin(), E=P.subPieces.end();
- I!=E; ++I) {
-
- if (const PathDiagnosticMacroPiece *MP =
- dyn_cast<PathDiagnosticMacroPiece>(I->get())) {
+ for (const auto &subPiece : P.subPieces) {
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
num = ProcessMacroPiece(os, *MP, num);
continue;
}
- if (PathDiagnosticEventPiece *EP =
- dyn_cast<PathDiagnosticEventPiece>(I->get())) {
+ if (const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
os << "<div class=\"msg msgEvent\" style=\"width:94%; "
"margin-left:5px\">"
"<table class=\"msgT\"><tr>"
@@ -862,7 +1016,7 @@ window.addEventListener("keydown", function (event) {
navigateTo(/*up=*/true);
} else {
return;
- }
+ }
event.preventDefault();
}, true);
</script>
diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index a8c4b05cea13..da4574c61515 100644
--- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -97,9 +97,7 @@ changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) {
unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")),
hasUnaryOperand(ignoringParenImpCasts(
declRefExpr(to(varDecl(VarNodeMatcher)))))),
- binaryOperator(anyOf(hasOperatorName("="), hasOperatorName("+="),
- hasOperatorName("/="), hasOperatorName("*="),
- hasOperatorName("-=")),
+ binaryOperator(isAssignmentOperator(),
hasLHS(ignoringParenImpCasts(
declRefExpr(to(varDecl(VarNodeMatcher)))))));
}
@@ -143,13 +141,15 @@ static internal::Matcher<Stmt> forLoopMatcher() {
return forStmt(
hasCondition(simpleCondition("initVarName")),
// Initialization should match the form: 'int i = 6' or 'i = 42'.
- hasLoopInit(anyOf(
- declStmt(hasSingleDecl(varDecl(
- allOf(hasInitializer(integerLiteral().bind("initNum")),
- equalsBoundNode("initVarName"))))),
- binaryOperator(hasLHS(declRefExpr(to(
- varDecl(equalsBoundNode("initVarName"))))),
- hasRHS(integerLiteral().bind("initNum"))))),
+ hasLoopInit(
+ anyOf(declStmt(hasSingleDecl(
+ varDecl(allOf(hasInitializer(ignoringParenImpCasts(
+ integerLiteral().bind("initNum"))),
+ equalsBoundNode("initVarName"))))),
+ binaryOperator(hasLHS(declRefExpr(to(varDecl(
+ equalsBoundNode("initVarName"))))),
+ hasRHS(ignoringParenImpCasts(
+ integerLiteral().bind("initNum")))))),
// Incrementation should be a simple increment or decrement
// operator call.
hasIncrement(unaryOperator(
diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 05865c294cb7..9192f49eac6d 100644
--- a/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -14,10 +14,16 @@
///
//===----------------------------------------------------------------------===//
+#include "clang/AST/AST.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
using namespace clang;
using namespace ento;
+using namespace clang::ast_matchers;
+
+const auto MatchRef = "matchref";
/// Return the loops condition Stmt or NULL if LoopStmt is not a loop
static const Expr *getLoopCondition(const Stmt *LoopStmt) {
@@ -49,7 +55,8 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
// TODO Nested loops are currently widened as a result of the invalidation
// being so inprecise. When the invalidation is improved, the handling
// of nested loops will also need to be improved.
- const StackFrameContext *STC = LCtx->getCurrentStackFrame();
+ ASTContext &ASTCtx = LCtx->getAnalysisDeclContext()->getASTContext();
+ const StackFrameContext *STC = LCtx->getStackFrame();
MemRegionManager &MRMgr = PrevState->getStateManager().getRegionManager();
const MemRegion *Regions[] = {MRMgr.getStackLocalsRegion(STC),
MRMgr.getStackArgumentsRegion(STC),
@@ -59,6 +66,30 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
ITraits.setTrait(Region,
RegionAndSymbolInvalidationTraits::TK_EntireMemSpace);
}
+
+ // References should not be invalidated.
+ auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))),
+ *LCtx->getDecl()->getBody(), ASTCtx);
+ for (BoundNodes Match : Matches) {
+ const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef);
+ assert(VD);
+ const VarRegion *VarMem = MRMgr.getVarRegion(VD, LCtx);
+ ITraits.setTrait(VarMem,
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ }
+
+
+ // 'this' pointer is not an lvalue, we should not invalidate it. If the loop
+ // is located in a method, constructor or destructor, the value of 'this'
+ // pointer shoule remain unchanged.
+ if (const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl())) {
+ const CXXThisRegion *ThisR = MRMgr.getCXXThisRegion(
+ CXXMD->getThisType(STC->getAnalysisDeclContext()->getASTContext()),
+ STC);
+ ITraits.setTrait(ThisR,
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ }
+
return PrevState->invalidateRegions(Regions, getLoopCondition(LoopStmt),
BlockCount, LCtx, true, nullptr, nullptr,
&ITraits);
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index cb8ba6de3626..cb2122c7749e 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1,4 +1,4 @@
-//== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--//
+//===- MemRegion.cpp - Abstract memory regions for static analysis --------===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,19 +14,51 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Type.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CheckedArithmetic.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <string>
+#include <tuple>
+#include <utility>
using namespace clang;
using namespace ento;
+#define DEBUG_TYPE "MemRegion"
+
//===----------------------------------------------------------------------===//
// MemRegion Construction.
//===----------------------------------------------------------------------===//
@@ -37,8 +69,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1,
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, arg1, superRegion);
void *InsertPos;
- RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
- InsertPos));
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
@@ -55,8 +86,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, arg1, arg2, superRegion);
void *InsertPos;
- RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
- InsertPos));
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
@@ -75,8 +105,7 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
llvm::FoldingSetNodeID ID;
RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion);
void *InsertPos;
- RegionTy* R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID,
- InsertPos));
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
if (!R) {
R = A.Allocate<RegionTy>();
@@ -91,27 +120,26 @@ RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
// Object destruction.
//===----------------------------------------------------------------------===//
-MemRegion::~MemRegion() {}
+MemRegion::~MemRegion() = default;
-MemRegionManager::~MemRegionManager() {
- // All regions and their data are BumpPtrAllocated. No need to call
- // their destructors.
-}
+// All regions and their data are BumpPtrAllocated. No need to call their
+// destructors.
+MemRegionManager::~MemRegionManager() = default;
//===----------------------------------------------------------------------===//
// Basic methods.
//===----------------------------------------------------------------------===//
bool SubRegion::isSubRegionOf(const MemRegion* R) const {
- const MemRegion* r = getSuperRegion();
- while (r != nullptr) {
+ const MemRegion* r = this;
+ do {
if (r == R)
return true;
- if (const SubRegion* sr = dyn_cast<SubRegion>(r))
+ if (const auto *sr = dyn_cast<SubRegion>(r))
r = sr->getSuperRegion();
else
break;
- }
+ } while (r != nullptr);
return false;
}
@@ -119,16 +147,16 @@ MemRegionManager* SubRegion::getMemRegionManager() const {
const SubRegion* r = this;
do {
const MemRegion *superRegion = r->getSuperRegion();
- if (const SubRegion *sr = dyn_cast<SubRegion>(superRegion)) {
+ if (const auto *sr = dyn_cast<SubRegion>(superRegion)) {
r = sr;
continue;
}
return superRegion->getMemRegionManager();
- } while (1);
+ } while (true);
}
const StackFrameContext *VarRegion::getStackFrame() const {
- const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace());
+ const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace());
return SSR ? SSR->getStackFrame() : nullptr;
}
@@ -215,17 +243,17 @@ void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(getCodeRegion());
}
-void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
- const StringLiteral* Str,
- const MemRegion* superRegion) {
+void StringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const StringLiteral *Str,
+ const MemRegion *superRegion) {
ID.AddInteger(static_cast<unsigned>(StringRegionKind));
ID.AddPointer(Str);
ID.AddPointer(superRegion);
}
-void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
- const ObjCStringLiteral* Str,
- const MemRegion* superRegion) {
+void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const ObjCStringLiteral *Str,
+ const MemRegion *superRegion) {
ID.AddInteger(static_cast<unsigned>(ObjCStringRegionKind));
ID.AddPointer(Str);
ID.AddPointer(superRegion);
@@ -380,13 +408,19 @@ void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
// Region anchors.
//===----------------------------------------------------------------------===//
-void GlobalsSpaceRegion::anchor() { }
-void NonStaticGlobalSpaceRegion::anchor() { }
-void StackSpaceRegion::anchor() { }
-void TypedRegion::anchor() { }
-void TypedValueRegion::anchor() { }
-void CodeTextRegion::anchor() { }
-void SubRegion::anchor() { }
+void GlobalsSpaceRegion::anchor() {}
+
+void NonStaticGlobalSpaceRegion::anchor() {}
+
+void StackSpaceRegion::anchor() {}
+
+void TypedRegion::anchor() {}
+
+void TypedValueRegion::anchor() {}
+
+void CodeTextRegion::anchor() {}
+
+void SubRegion::anchor() {}
//===----------------------------------------------------------------------===//
// Region pretty-printing.
@@ -408,7 +442,7 @@ void MemRegion::dumpToStream(raw_ostream &os) const {
}
void AllocaRegion::dumpToStream(raw_ostream &os) const {
- os << "alloca{" << static_cast<const void*>(Ex) << ',' << Cnt << '}';
+ os << "alloca{" << static_cast<const void *>(Ex) << ',' << Cnt << '}';
}
void FunctionCodeRegion::dumpToStream(raw_ostream &os) const {
@@ -416,7 +450,7 @@ void FunctionCodeRegion::dumpToStream(raw_ostream &os) const {
}
void BlockCodeRegion::dumpToStream(raw_ostream &os) const {
- os << "block_code{" << static_cast<const void*>(this) << '}';
+ os << "block_code{" << static_cast<const void *>(this) << '}';
}
void BlockDataRegion::dumpToStream(raw_ostream &os) const {
@@ -425,19 +459,19 @@ void BlockDataRegion::dumpToStream(raw_ostream &os) const {
for (BlockDataRegion::referenced_vars_iterator
I = referenced_vars_begin(),
E = referenced_vars_end(); I != E; ++I)
- os << "(" << I.getCapturedRegion() << "," <<
+ os << "(" << I.getCapturedRegion() << "<-" <<
I.getOriginalRegion() << ") ";
os << '}';
}
void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const {
// FIXME: More elaborate pretty-printing.
- os << "{ " << static_cast<const void*>(CL) << " }";
+ os << "{ " << static_cast<const void *>(CL) << " }";
}
void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
os << "temp_object{" << getValueType().getAsString() << ','
- << static_cast<const void*>(Ex) << '}';
+ << static_cast<const void *>(Ex) << '}';
}
void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
@@ -478,7 +512,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const {
}
void VarRegion::dumpToStream(raw_ostream &os) const {
- os << *cast<VarDecl>(D);
+ const auto *VD = cast<VarDecl>(D);
+ if (const IdentifierInfo *ID = VD->getIdentifier())
+ os << ID->getName();
+ else
+ os << "VarRegion{" << static_cast<const void *>(this) << '}';
}
LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
@@ -622,19 +660,18 @@ std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
// Get variable name.
if (R && R->canPrintPrettyAsExpr()) {
R->printPrettyAsExpr(os);
- if (UseQuotes) {
+ if (UseQuotes)
return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str();
- } else {
+ else
return (llvm::Twine(os.str()) + ArrayIndices).str();
- }
}
return VariableName;
}
SourceRange MemRegion::sourceRange() const {
- const VarRegion *const VR = dyn_cast<VarRegion>(this->getBaseRegion());
- const FieldRegion *const FR = dyn_cast<FieldRegion>(this);
+ const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion());
+ const auto *const FR = dyn_cast<FieldRegion>(this);
// Check for more specific regions first.
// FieldRegion
@@ -646,9 +683,8 @@ SourceRange MemRegion::sourceRange() const {
return VR->getDecl()->getSourceRange();
}
// Return invalid source range (can be checked by client).
- else {
- return SourceRange{};
- }
+ else
+ return {};
}
//===----------------------------------------------------------------------===//
@@ -738,13 +774,14 @@ const CodeSpaceRegion *MemRegionManager::getCodeRegion() {
//===----------------------------------------------------------------------===//
// Constructing regions.
//===----------------------------------------------------------------------===//
-const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){
+
+const StringRegion *MemRegionManager::getStringRegion(const StringLiteral *Str){
return getSubRegion<StringRegion>(
Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
}
const ObjCStringRegion *
-MemRegionManager::getObjCStringRegion(const ObjCStringLiteral* Str){
+MemRegionManager::getObjCStringRegion(const ObjCStringLiteral *Str){
return getSubRegion<ObjCStringRegion>(
Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
}
@@ -757,21 +794,20 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
const DeclContext *DC,
const VarDecl *VD) {
while (LC) {
- if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LC)) {
+ if (const auto *SFC = dyn_cast<StackFrameContext>(LC)) {
if (cast<DeclContext>(SFC->getDecl()) == DC)
return SFC;
}
- if (const BlockInvocationContext *BC =
- dyn_cast<BlockInvocationContext>(LC)) {
- const BlockDataRegion *BR =
- static_cast<const BlockDataRegion*>(BC->getContextData());
+ if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) {
+ const auto *BR =
+ static_cast<const BlockDataRegion *>(BC->getContextData());
// 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) {
- if (const VarRegion *VR = dyn_cast<VarRegion>(I.getOriginalRegion()))
- if (VR->getDecl() == VD)
- return cast<VarRegion>(I.getCapturedRegion());
+ const VarRegion *VR = I.getOriginalRegion();
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
}
}
@@ -818,7 +854,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
if (V.is<const VarRegion*>())
return V.get<const VarRegion*>();
- const StackFrameContext *STC = V.get<const StackFrameContext*>();
+ const auto *STC = V.get<const StackFrameContext *>();
if (!STC) {
// FIXME: Assign a more sensible memory space to static locals
@@ -836,7 +872,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD))
sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
getFunctionCodeRegion(cast<NamedDecl>(STCD)));
- else if (const BlockDecl *BD = dyn_cast<BlockDecl>(STCD)) {
+ else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) {
// FIXME: The fallback type here is totally bogus -- though it should
// never be queried, it will prevent uniquing with the real
// BlockCodeRegion. Ideally we'd fix the AST so that we always had a
@@ -885,7 +921,7 @@ MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
if (LC) {
// FIXME: Once we implement scope handling, we want the parent region
// to be the scope.
- const StackFrameContext *STC = LC->getCurrentStackFrame();
+ const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
sReg = getStackLocalsRegion(STC);
}
@@ -913,7 +949,7 @@ MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
if (CL->isFileScope())
sReg = getGlobalsRegion();
else {
- const StackFrameContext *STC = LC->getCurrentStackFrame();
+ const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
sReg = getStackLocalsRegion(STC);
}
@@ -932,7 +968,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
void *InsertPos;
MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos);
- ElementRegion* R = cast_or_null<ElementRegion>(data);
+ auto *R = cast_or_null<ElementRegion>(data);
if (!R) {
R = A.Allocate<ElementRegion>();
@@ -954,7 +990,6 @@ MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy,
return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion());
}
-
/// getSymbolicRegion - Retrieve or create a "symbolic" memory region.
const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
@@ -979,7 +1014,7 @@ MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d,
const CXXTempObjectRegion*
MemRegionManager::getCXXTempObjectRegion(Expr const *E,
LocationContext const *LC) {
- const StackFrameContext *SFC = LC->getCurrentStackFrame();
+ const StackFrameContext *SFC = LC->getStackFrame();
assert(SFC);
return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC));
}
@@ -1017,10 +1052,8 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
if (IsVirtual) {
// Virtual base regions should not be layered, since the layout rules
// are different.
- while (const CXXBaseObjectRegion *Base =
- dyn_cast<CXXBaseObjectRegion>(Super)) {
+ while (const auto *Base = dyn_cast<CXXBaseObjectRegion>(Super))
Super = cast<SubRegion>(Base->getSuperRegion());
- }
assert(Super && !isa<MemSpaceRegion>(Super));
}
}
@@ -1031,7 +1064,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
const CXXThisRegion*
MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
const LocationContext *LC) {
- const PointerType *PT = thisPointerTy->getAs<PointerType>();
+ const auto *PT = thisPointerTy->getAs<PointerType>();
assert(PT);
// Inside the body of the operator() of a lambda a this expr might refer to an
// object in one of the parent location contexts.
@@ -1045,7 +1078,7 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
LC = LC->getParent();
D = dyn_cast<CXXMethodDecl>(LC->getDecl());
}
- const StackFrameContext *STC = LC->getCurrentStackFrame();
+ const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC));
}
@@ -1053,14 +1086,14 @@ MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
const AllocaRegion*
MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt,
const LocationContext *LC) {
- const StackFrameContext *STC = LC->getCurrentStackFrame();
+ const StackFrameContext *STC = LC->getStackFrame();
assert(STC);
return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC));
}
const MemSpaceRegion *MemRegion::getMemorySpace() const {
const MemRegion *R = this;
- const SubRegion* SR = dyn_cast<SubRegion>(this);
+ const auto *SR = dyn_cast<SubRegion>(this);
while (SR) {
R = SR->getSuperRegion();
@@ -1121,7 +1154,7 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
while (true) {
switch (R->getKind()) {
case ElementRegionKind: {
- const ElementRegion *ER = cast<ElementRegion>(R);
+ const auto *ER = cast<ElementRegion>(R);
if (!ER->getIndex().isZeroConstant())
return R;
R = ER->getSuperRegion();
@@ -1139,10 +1172,10 @@ const MemRegion *MemRegion::StripCasts(bool StripBaseCasts) const {
}
const SymbolicRegion *MemRegion::getSymbolicBase() const {
- const SubRegion *SubR = dyn_cast<SubRegion>(this);
+ const auto *SubR = dyn_cast<SubRegion>(this);
while (SubR) {
- if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SubR))
+ if (const auto *SymR = dyn_cast<SymbolicRegion>(SubR))
return SymR;
SubR = dyn_cast<SubRegion>(SubR->getSuperRegion());
}
@@ -1150,7 +1183,7 @@ const SymbolicRegion *MemRegion::getSymbolicBase() const {
}
RegionRawOffset ElementRegion::getAsArrayOffset() const {
- CharUnits offset = CharUnits::Zero();
+ int64_t offset = 0;
const ElementRegion *ER = this;
const MemRegion *superR = nullptr;
ASTContext &C = getContext();
@@ -1162,7 +1195,7 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const {
// FIXME: generalize to symbolic offsets.
SVal index = ER->getIndex();
- if (Optional<nonloc::ConcreteInt> CI = index.getAs<nonloc::ConcreteInt>()) {
+ if (auto CI = index.getAs<nonloc::ConcreteInt>()) {
// Update the offset.
int64_t i = CI->getValue().getSExtValue();
@@ -1175,8 +1208,15 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const {
break;
}
- CharUnits size = C.getTypeSizeInChars(elemType);
- offset += (i * size);
+ int64_t size = C.getTypeSizeInChars(elemType).getQuantity();
+ if (auto NewOffset = llvm::checkedMulAdd(i, size, offset)) {
+ offset = *NewOffset;
+ } else {
+ LLVM_DEBUG(llvm::dbgs() << "MemRegion::getAsArrayOffset: "
+ << "offset overflowing, returning unknown\n");
+
+ return nullptr;
+ }
}
// Go to the next ElementRegion (if any).
@@ -1188,10 +1228,9 @@ RegionRawOffset ElementRegion::getAsArrayOffset() const {
}
assert(superR && "super region cannot be NULL");
- return RegionRawOffset(superR, offset);
+ return RegionRawOffset(superR, CharUnits::fromQuantity(offset));
}
-
/// Returns true if \p Base is an immediate base class of \p Child
static bool isImmediateBase(const CXXRecordDecl *Child,
const CXXRecordDecl *Base) {
@@ -1207,47 +1246,46 @@ static bool isImmediateBase(const CXXRecordDecl *Child,
return false;
}
-RegionOffset MemRegion::getAsOffset() const {
- const MemRegion *R = this;
+static RegionOffset calculateOffset(const MemRegion *R) {
const MemRegion *SymbolicOffsetBase = nullptr;
int64_t Offset = 0;
- while (1) {
+ while (true) {
switch (R->getKind()) {
- case CodeSpaceRegionKind:
- case StackLocalsSpaceRegionKind:
- case StackArgumentsSpaceRegionKind:
- case HeapSpaceRegionKind:
- case UnknownSpaceRegionKind:
- case StaticGlobalSpaceRegionKind:
- case GlobalInternalSpaceRegionKind:
- case GlobalSystemSpaceRegionKind:
- case GlobalImmutableSpaceRegionKind:
+ case MemRegion::CodeSpaceRegionKind:
+ case MemRegion::StackLocalsSpaceRegionKind:
+ case MemRegion::StackArgumentsSpaceRegionKind:
+ case MemRegion::HeapSpaceRegionKind:
+ case MemRegion::UnknownSpaceRegionKind:
+ case MemRegion::StaticGlobalSpaceRegionKind:
+ case MemRegion::GlobalInternalSpaceRegionKind:
+ case MemRegion::GlobalSystemSpaceRegionKind:
+ case MemRegion::GlobalImmutableSpaceRegionKind:
// Stores can bind directly to a region space to set a default value.
assert(Offset == 0 && !SymbolicOffsetBase);
goto Finish;
- case FunctionCodeRegionKind:
- case BlockCodeRegionKind:
- case BlockDataRegionKind:
+ case MemRegion::FunctionCodeRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::BlockDataRegionKind:
// These will never have bindings, but may end up having values requested
// if the user does some strange casting.
if (Offset != 0)
SymbolicOffsetBase = R;
goto Finish;
- case SymbolicRegionKind:
- case AllocaRegionKind:
- case CompoundLiteralRegionKind:
- case CXXThisRegionKind:
- case StringRegionKind:
- case ObjCStringRegionKind:
- case VarRegionKind:
- case CXXTempObjectRegionKind:
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::StringRegionKind:
+ case MemRegion::ObjCStringRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
// Usual base regions.
goto Finish;
- case ObjCIvarRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
// This is a little strange, but it's a compromise between
// ObjCIvarRegions having unknown compile-time offsets (when using the
// non-fragile runtime) and yet still being distinct, non-overlapping
@@ -1255,15 +1293,15 @@ RegionOffset MemRegion::getAsOffset() const {
// of computing offsets.
goto Finish;
- case CXXBaseObjectRegionKind: {
- const CXXBaseObjectRegion *BOR = cast<CXXBaseObjectRegion>(R);
+ case MemRegion::CXXBaseObjectRegionKind: {
+ const auto *BOR = cast<CXXBaseObjectRegion>(R);
R = BOR->getSuperRegion();
QualType Ty;
bool RootIsSymbolic = false;
- if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) {
- Ty = TVR->getDesugaredValueType(getContext());
- } else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) {
+ Ty = TVR->getDesugaredValueType(R->getContext());
+ } else if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
// If our base region is symbolic, we don't know what type it really is.
// Pretend the type of the symbol is the true dynamic type.
// (This will at least be self-consistent for the life of the symbol.)
@@ -1296,18 +1334,18 @@ RegionOffset MemRegion::getAsOffset() const {
continue;
CharUnits BaseOffset;
- const ASTRecordLayout &Layout = getContext().getASTRecordLayout(Child);
+ const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(Child);
if (BOR->isVirtual())
BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl());
else
BaseOffset = Layout.getBaseClassOffset(BOR->getDecl());
// The base offset is in chars, not in bits.
- Offset += BaseOffset.getQuantity() * getContext().getCharWidth();
+ Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
break;
}
- case ElementRegionKind: {
- const ElementRegion *ER = cast<ElementRegion>(R);
+ case MemRegion::ElementRegionKind: {
+ const auto *ER = cast<ElementRegion>(R);
R = ER->getSuperRegion();
QualType EleTy = ER->getValueType();
@@ -1327,15 +1365,15 @@ RegionOffset MemRegion::getAsOffset() const {
int64_t i = CI->getValue().getSExtValue();
// This type size is in bits.
- Offset += i * getContext().getTypeSize(EleTy);
+ Offset += i * R->getContext().getTypeSize(EleTy);
} else {
// We cannot compute offset for non-concrete index.
SymbolicOffsetBase = R;
}
break;
}
- case FieldRegionKind: {
- const FieldRegion *FR = cast<FieldRegion>(R);
+ case MemRegion::FieldRegionKind: {
+ const auto *FR = cast<FieldRegion>(R);
R = FR->getSuperRegion();
const RecordDecl *RD = FR->getDecl()->getParent();
@@ -1360,7 +1398,7 @@ RegionOffset MemRegion::getAsOffset() const {
if (FR->getDecl() == *FI)
break;
}
- const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD);
+ const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(RD);
// This is offset in bits.
Offset += Layout.getFieldOffset(idx);
break;
@@ -1374,6 +1412,12 @@ RegionOffset MemRegion::getAsOffset() const {
return RegionOffset(R, Offset);
}
+RegionOffset MemRegion::getAsOffset() const {
+ if (!cachedOffset)
+ cachedOffset = calculateOffset(this);
+ return *cachedOffset;
+}
+
//===----------------------------------------------------------------------===//
// BlockDataRegion
//===----------------------------------------------------------------------===//
@@ -1419,13 +1463,14 @@ void BlockDataRegion::LazyInitializeReferencedVars() {
llvm::BumpPtrAllocator &A = MemMgr.getAllocator();
BumpVectorContext BC(A);
- typedef BumpVector<const MemRegion*> VarVec;
- VarVec *BV = A.Allocate<VarVec>();
+ using VarVec = BumpVector<const MemRegion *>;
+
+ auto *BV = A.Allocate<VarVec>();
new (BV) VarVec(BC, NumBlockVars);
- VarVec *BVOriginal = A.Allocate<VarVec>();
+ auto *BVOriginal = A.Allocate<VarVec>();
new (BVOriginal) VarVec(BC, NumBlockVars);
- for (const VarDecl *VD : ReferencedBlockVars) {
+ for (const auto *VD : ReferencedBlockVars) {
const VarRegion *VR = nullptr;
const VarRegion *OriginalVR = nullptr;
std::tie(VR, OriginalVR) = getCaptureRegions(VD);
@@ -1443,14 +1488,13 @@ BlockDataRegion::referenced_vars_iterator
BlockDataRegion::referenced_vars_begin() const {
const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars();
- BumpVector<const MemRegion*> *Vec =
- static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
+ auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars);
if (Vec == (void*) 0x1)
return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr);
- BumpVector<const MemRegion*> *VecOriginal =
- static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+ auto *VecOriginal =
+ static_cast<BumpVector<const MemRegion *> *>(OriginalVars);
return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
VecOriginal->begin());
@@ -1460,14 +1504,13 @@ BlockDataRegion::referenced_vars_iterator
BlockDataRegion::referenced_vars_end() const {
const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars();
- BumpVector<const MemRegion*> *Vec =
- static_cast<BumpVector<const MemRegion*>*>(ReferencedVars);
+ auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars);
if (Vec == (void*) 0x1)
return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr);
- BumpVector<const MemRegion*> *VecOriginal =
- static_cast<BumpVector<const MemRegion*>*>(OriginalVars);
+ auto *VecOriginal =
+ static_cast<BumpVector<const MemRegion *> *>(OriginalVars);
return BlockDataRegion::referenced_vars_iterator(Vec->end(),
VecOriginal->end());
@@ -1495,7 +1538,7 @@ void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym,
void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR,
InvalidationKinds IK) {
assert(MR);
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
setTrait(SR->getSymbol(), IK);
else
MRTraitsMap[MR] |= IK;
@@ -1515,7 +1558,7 @@ bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR,
if (!MR)
return false;
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
return hasTrait(SR->getSymbol(), IK);
const_region_iterator I = MRTraitsMap.find(MR);
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 669748c0127a..1b698ec5c086 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -1,4 +1,4 @@
-//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===//
+//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,26 +13,52 @@
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
#include "clang/AST/ParentMap.h"
-#include "clang/AST/StmtCXX.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
using namespace clang;
using namespace ento;
bool PathDiagnosticMacroPiece::containsEvent() const {
- for (auto &P : subPieces) {
+ for (const auto &P : subPieces) {
if (isa<PathDiagnosticEventPiece>(*P))
return true;
- if (auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get()))
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get()))
if (MP->containsEvent())
return true;
}
@@ -43,23 +69,27 @@ static StringRef StripTrailingDots(StringRef s) {
for (StringRef::size_type i = s.size(); i != 0; --i)
if (s[i - 1] != '.')
return s.substr(0, i);
- return "";
+ return {};
}
PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
Kind k, DisplayHint hint)
- : str(StripTrailingDots(s)), kind(k), Hint(hint),
- LastInMainSourceFile(false) {}
+ : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
- : kind(k), Hint(hint), LastInMainSourceFile(false) {}
+ : kind(k), Hint(hint) {}
-PathDiagnosticPiece::~PathDiagnosticPiece() {}
-PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {}
-PathDiagnosticCallPiece::~PathDiagnosticCallPiece() {}
-PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {}
-PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() {}
-PathDiagnosticNotePiece::~PathDiagnosticNotePiece() {}
+PathDiagnosticPiece::~PathDiagnosticPiece() = default;
+
+PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default;
+
+PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default;
+
+PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default;
+
+PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;
+
+PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;
void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
bool ShouldFlattenMacros) const {
@@ -96,22 +126,20 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
}
}
-PathDiagnostic::~PathDiagnostic() {}
-
-PathDiagnostic::PathDiagnostic(StringRef CheckName, const Decl *declWithIssue,
- StringRef bugtype, StringRef verboseDesc,
- StringRef shortDesc, StringRef category,
- PathDiagnosticLocation LocationToUnique,
- const Decl *DeclToUnique)
- : CheckName(CheckName),
- DeclWithIssue(declWithIssue),
- BugType(StripTrailingDots(bugtype)),
- VerboseDesc(StripTrailingDots(verboseDesc)),
- ShortDesc(StripTrailingDots(shortDesc)),
- Category(StripTrailingDots(category)),
- UniqueingLoc(LocationToUnique),
- UniqueingDecl(DeclToUnique),
- path(pathImpl) {}
+PathDiagnostic::~PathDiagnostic() = default;
+
+PathDiagnostic::PathDiagnostic(
+ StringRef CheckName, const Decl *declWithIssue, StringRef bugtype,
+ StringRef verboseDesc, StringRef shortDesc, StringRef category,
+ PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,
+ std::unique_ptr<FilesToLineNumsMap> ExecutedLines)
+ : CheckName(CheckName), DeclWithIssue(declWithIssue),
+ BugType(StripTrailingDots(bugtype)),
+ VerboseDesc(StripTrailingDots(verboseDesc)),
+ ShortDesc(StripTrailingDots(shortDesc)),
+ Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),
+ UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)),
+ path(pathImpl) {}
static PathDiagnosticCallPiece *
getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
@@ -122,11 +150,11 @@ getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
if (CallLoc.isMacroID())
return nullptr;
- assert(SMgr.isInMainFile(CallLoc) &&
- "The call piece should be in the main file.");
+ assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) &&
+ "The call piece should not be in a header file.");
// Check if CP represents a path through a function outside of the main file.
- if (!SMgr.isInMainFile(CP->callEnterWithin.asLocation()))
+ if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr))
return CP;
const PathPieces &Path = CP->path;
@@ -135,10 +163,8 @@ getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
// Check if the last piece in the callee path is a call to a function outside
// of the main file.
- if (PathDiagnosticCallPiece *CPInner =
- dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) {
+ if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
return getFirstStackedCallToHeaderFile(CPInner, SMgr);
- }
// Otherwise, the last piece is in the main file.
return nullptr;
@@ -154,14 +180,14 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() {
// We only need to check if the report ends inside headers, if the last piece
// is a call piece.
- if (PathDiagnosticCallPiece *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
+ if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
CP = getFirstStackedCallToHeaderFile(CP, SMgr);
if (CP) {
// Mark the piece.
CP->setAsLastInMainSourceFile();
// Update the path diagnostic message.
- const NamedDecl *ND = dyn_cast<NamedDecl>(CP->getCallee());
+ const auto *ND = dyn_cast<NamedDecl>(CP->getCallee());
if (ND) {
SmallString<200> buf;
llvm::raw_svector_ostream os(buf);
@@ -178,14 +204,12 @@ void PathDiagnostic::resetDiagnosticLocationToMainFile() {
}
}
-void PathDiagnosticConsumer::anchor() { }
+void PathDiagnosticConsumer::anchor() {}
PathDiagnosticConsumer::~PathDiagnosticConsumer() {
// Delete the contents of the FoldingSet if it isn't empty already.
- for (llvm::FoldingSet<PathDiagnostic>::iterator it =
- Diags.begin(), et = Diags.end() ; it != et ; ++it) {
- delete &*it;
- }
+ for (auto &Diag : Diags)
+ delete &Diag;
}
void PathDiagnosticConsumer::HandlePathDiagnostic(
@@ -216,9 +240,8 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
while (!WorkList.empty()) {
const PathPieces &path = *WorkList.pop_back_val();
- for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E;
- ++I) {
- const PathDiagnosticPiece *piece = I->get();
+ for (const auto &I : path) {
+ const PathDiagnosticPiece *piece = I.get();
FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
if (FID.isInvalid()) {
@@ -230,28 +253,23 @@ void PathDiagnosticConsumer::HandlePathDiagnostic(
// Check the source ranges.
ArrayRef<SourceRange> Ranges = piece->getRanges();
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(),
- E = Ranges.end(); I != E; ++I) {
- SourceLocation L = SMgr.getExpansionLoc(I->getBegin());
+ for (const auto &I : Ranges) {
+ SourceLocation L = SMgr.getExpansionLoc(I.getBegin());
if (!L.isFileID() || SMgr.getFileID(L) != FID) {
llvm::errs() << warning.str();
return;
}
- L = SMgr.getExpansionLoc(I->getEnd());
+ L = SMgr.getExpansionLoc(I.getEnd());
if (!L.isFileID() || SMgr.getFileID(L) != FID) {
llvm::errs() << warning.str();
return;
}
}
- if (const PathDiagnosticCallPiece *call =
- dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece))
WorkList.push_back(&call->path);
- }
- else if (const PathDiagnosticMacroPiece *macro =
- dyn_cast<PathDiagnosticMacroPiece>(piece)) {
+ else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece))
WorkList.push_back(&macro->subPieces);
- }
}
}
@@ -381,11 +399,29 @@ static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) {
return None;
}
+static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) {
+ std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc();
+ std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc();
+ const SourceManager &SM = XL.getManager();
+ std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs);
+ if (InSameTU.first)
+ return XL.isBeforeInTranslationUnitThan(YL);
+ const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID());
+ const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID());
+ if (!XFE || !YFE)
+ return XFE && !YFE;
+ int NameCmp = XFE->getName().compare(YFE->getName());
+ if (NameCmp != 0)
+ return NameCmp == -1;
+ // Last resort: Compare raw file IDs that are possibly expansions.
+ return XL.getFileID() < YL.getFileID();
+}
+
static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {
FullSourceLoc XL = X.getLocation().asLocation();
FullSourceLoc YL = Y.getLocation().asLocation();
if (XL != YL)
- return XL.isBeforeInTranslationUnitThan(YL);
+ return compareCrossTUSourceLocs(XL, YL);
if (X.getBugType() != Y.getBugType())
return X.getBugType() < Y.getBugType();
if (X.getCategory() != Y.getCategory())
@@ -405,7 +441,8 @@ static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {
SourceLocation YDL = YD->getLocation();
if (XDL != YDL) {
const SourceManager &SM = XL.getManager();
- return SM.isBeforeInTranslationUnit(XDL, YDL);
+ return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM),
+ FullSourceLoc(YDL, SM));
}
}
PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();
@@ -429,11 +466,8 @@ void PathDiagnosticConsumer::FlushDiagnostics(
flushed = true;
std::vector<const PathDiagnostic *> BatchDiags;
- for (llvm::FoldingSet<PathDiagnostic>::iterator it = Diags.begin(),
- et = Diags.end(); it != et; ++it) {
- const PathDiagnostic *D = &*it;
- BatchDiags.push_back(D);
- }
+ for (const auto &D : Diags)
+ BatchDiags.push_back(&D);
// Sort the diagnostics so that they are always emitted in a deterministic
// order.
@@ -450,11 +484,8 @@ void PathDiagnosticConsumer::FlushDiagnostics(
FlushDiagnosticsImpl(BatchDiags, Files);
// Delete the flushed diagnostics.
- for (std::vector<const PathDiagnostic *>::iterator it = BatchDiags.begin(),
- et = BatchDiags.end(); it != et; ++it) {
- const PathDiagnostic *D = *it;
+ for (const auto D : BatchDiags)
delete D;
- }
// Clear out the FoldingSet.
Diags.clear();
@@ -553,6 +584,8 @@ getLocationForCaller(const StackFrameContext *SFC,
switch (Source.getKind()) {
case CFGElement::Statement:
+ case CFGElement::Constructor:
+ case CFGElement::CXXRecordTypedCall:
return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
SM, CallerCtx);
case CFGElement::Initializer: {
@@ -576,8 +609,20 @@ getLocationForCaller(const StackFrameContext *SFC,
return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
}
- case CFGElement::TemporaryDtor:
- case CFGElement::NewAllocator:
+ case CFGElement::NewAllocator: {
+ const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>();
+ return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx);
+ }
+ case CFGElement::TemporaryDtor: {
+ // Temporary destructors are for temporaries. They die immediately at around
+ // the location of CXXBindTemporaryExpr. If they are lifetime-extended,
+ // they'd be dealt with via an AutomaticObjectDtor instead.
+ const auto &Dtor = Source.castAs<CFGTemporaryDtor>();
+ return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM,
+ CallerCtx);
+ }
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
llvm_unreachable("not yet implemented!");
case CFGElement::LifetimeEnds:
case CFGElement::LoopExit:
@@ -605,7 +650,7 @@ PathDiagnosticLocation
PathDiagnosticLocation::createEnd(const Stmt *S,
const SourceManager &SM,
LocationOrAnalysisDeclContext LAC) {
- if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(S))
+ if (const auto *CS = dyn_cast<CompoundStmt>(S))
return createEndBrace(CS, SM);
return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),
SM, SingleLocK);
@@ -624,7 +669,6 @@ PathDiagnosticLocation::createConditionalColonLoc(
return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);
}
-
PathDiagnosticLocation
PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
const SourceManager &SM) {
@@ -649,8 +693,7 @@ PathDiagnosticLocation
PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
const SourceManager &SM) {
// FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
- if (const CompoundStmt *CS =
- dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
+ if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
if (!CS->body_empty()) {
SourceLocation Loc = (*CS->body_begin())->getLocStart();
return PathDiagnosticLocation(Loc, SM, SingleLocK);
@@ -741,6 +784,8 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) {
return CEE->getCalleeContext()->getCallSite();
if (Optional<PostInitializer> PIPP = P.getAs<PostInitializer>())
return PIPP->getInitializer()->getInit();
+ if (Optional<CallExitBegin> CEB = P.getAs<CallExitBegin>())
+ return CEB->getReturnStmt();
return nullptr;
}
@@ -790,11 +835,11 @@ PathDiagnosticLocation
const LocationContext *LC = N->getLocationContext();
// For member expressions, return the location of the '.' or '->'.
- if (const MemberExpr *ME = dyn_cast<MemberExpr>(S))
+ if (const auto *ME = dyn_cast<MemberExpr>(S))
return PathDiagnosticLocation::createMemberLoc(ME, SM);
// For binary operators, return the location of the operator.
- if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S))
+ if (const auto *B = dyn_cast<BinaryOperator>(S))
return PathDiagnosticLocation::createOperatorLoc(B, SM);
if (P.getAs<PostStmtPurgeDeadSymbols>())
@@ -856,7 +901,7 @@ PathDiagnosticRange
default:
break;
case Stmt::DeclStmtClass: {
- const DeclStmt *DS = cast<DeclStmt>(S);
+ const auto *DS = cast<DeclStmt>(S);
if (DS->isSingleDecl()) {
// Should always be the case, but we'll be defensive.
return SourceRange(DS->getLocStart(),
@@ -886,9 +931,9 @@ PathDiagnosticRange
break;
}
case DeclK:
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
return MD->getSourceRange();
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
if (Stmt *Body = FD->getBody())
return Body->getSourceRange();
}
@@ -898,7 +943,7 @@ PathDiagnosticRange
}
}
- return SourceRange(Loc,Loc);
+ return SourceRange(Loc, Loc);
}
void PathDiagnosticLocation::flatten() {
@@ -954,17 +999,55 @@ void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
// non-autosynthesized callbacks.
// Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag
// defaults to false.
- if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Callee))
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee))
IsCalleeAnAutosynthesizedPropertyAccessor = (
MD->isPropertyAccessor() &&
CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized());
}
-static inline void describeClass(raw_ostream &Out, const CXXRecordDecl *D,
- StringRef Prefix = StringRef()) {
+static void describeTemplateParameters(raw_ostream &Out,
+ const ArrayRef<TemplateArgument> TAList,
+ const LangOptions &LO,
+ StringRef Prefix = StringRef(),
+ StringRef Postfix = StringRef());
+
+static void describeTemplateParameter(raw_ostream &Out,
+ const TemplateArgument &TArg,
+ const LangOptions &LO) {
+
+ if (TArg.getKind() == TemplateArgument::ArgKind::Pack) {
+ describeTemplateParameters(Out, TArg.getPackAsArray(), LO);
+ } else {
+ TArg.print(PrintingPolicy(LO), Out);
+ }
+}
+
+static void describeTemplateParameters(raw_ostream &Out,
+ const ArrayRef<TemplateArgument> TAList,
+ const LangOptions &LO,
+ StringRef Prefix, StringRef Postfix) {
+ if (TAList.empty())
+ return;
+
+ Out << Prefix;
+ for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) {
+ describeTemplateParameter(Out, TAList[I], LO);
+ Out << ", ";
+ }
+ describeTemplateParameter(Out, TAList[TAList.size() - 1], LO);
+ Out << Postfix;
+}
+
+static void describeClass(raw_ostream &Out, const CXXRecordDecl *D,
+ StringRef Prefix = StringRef()) {
if (!D->getIdentifier())
return;
- Out << Prefix << '\'' << *D << '\'';
+ Out << Prefix << '\'' << *D;
+ if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D))
+ describeTemplateParameters(Out, T->getTemplateArgs().asArray(),
+ D->getASTContext().getLangOpts(), "<", ">");
+
+ Out << '\'';
}
static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
@@ -979,7 +1062,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
return ExtendedDescription;
}
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
Out << Prefix;
if (ExtendedDescription && !MD->isUserProvided()) {
if (MD->isExplicitlyDefaulted())
@@ -988,7 +1071,7 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
Out << "implicit ";
}
- if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(MD)) {
+ if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
if (CD->isDefaultConstructor())
Out << "default ";
else if (CD->isCopyConstructor())
@@ -998,7 +1081,6 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
Out << "constructor";
describeClass(Out, MD->getParent(), " for ");
-
} else if (isa<CXXDestructorDecl>(MD)) {
if (!MD->isUserProvided()) {
Out << "destructor";
@@ -1007,15 +1089,12 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
// Use ~Foo for explicitly-written destructors.
Out << "'" << *MD << "'";
}
-
} else if (MD->isCopyAssignmentOperator()) {
Out << "copy assignment operator";
describeClass(Out, MD->getParent(), " for ");
-
} else if (MD->isMoveAssignmentOperator()) {
Out << "move assignment operator";
describeClass(Out, MD->getParent(), " for ");
-
} else {
if (MD->getParent()->getIdentifier())
Out << "'" << *MD->getParent() << "::" << *MD << "'";
@@ -1026,7 +1105,16 @@ static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
return true;
}
- Out << Prefix << '\'' << cast<NamedDecl>(*D) << '\'';
+ Out << Prefix << '\'' << cast<NamedDecl>(*D);
+
+ // Adding template parameters.
+ if (const auto FD = dyn_cast<FunctionDecl>(D))
+ if (const TemplateArgumentList *TAList =
+ FD->getTemplateSpecializationArgs())
+ describeTemplateParameters(Out, TAList->asArray(),
+ FD->getASTContext().getLangOpts(), "<", ">");
+
+ Out << '\'';
return true;
}
@@ -1055,7 +1143,7 @@ PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {
return nullptr;
if (Callee->isImplicit() || !Callee->hasBody())
return nullptr;
- if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee))
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee))
if (MD->isDefaulted())
return nullptr;
@@ -1095,13 +1183,10 @@ PathDiagnosticCallPiece::getCallExitEvent() const {
}
static void compute_path_size(const PathPieces &pieces, unsigned &size) {
- for (PathPieces::const_iterator it = pieces.begin(),
- et = pieces.end(); it != et; ++it) {
- const PathDiagnosticPiece *piece = it->get();
- if (const PathDiagnosticCallPiece *cp =
- dyn_cast<PathDiagnosticCallPiece>(piece)) {
+ for (const auto &I : pieces) {
+ const PathDiagnosticPiece *piece = I.get();
+ if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece))
compute_path_size(cp->path, size);
- }
else
++size;
}
@@ -1129,19 +1214,16 @@ void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
// FIXME: Add profiling support for code hints.
ID.AddInteger((unsigned) getDisplayHint());
ArrayRef<SourceRange> Ranges = getRanges();
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
- I != E; ++I) {
- ID.AddInteger(I->getBegin().getRawEncoding());
- ID.AddInteger(I->getEnd().getRawEncoding());
+ for (const auto &I : Ranges) {
+ ID.AddInteger(I.getBegin().getRawEncoding());
+ ID.AddInteger(I.getEnd().getRawEncoding());
}
}
void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticPiece::Profile(ID);
- for (PathPieces::const_iterator it = path.begin(),
- et = path.end(); it != et; ++it) {
- ID.Add(**it);
- }
+ for (const auto &I : path)
+ ID.Add(*I);
}
void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
@@ -1151,15 +1233,14 @@ void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticPiece::Profile(ID);
- for (const_iterator I = begin(), E = end(); I != E; ++I)
- ID.Add(*I);
+ for (const auto &I : *this)
+ ID.Add(I);
}
void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticSpotPiece::Profile(ID);
- for (PathPieces::const_iterator I = subPieces.begin(), E = subPieces.end();
- I != E; ++I)
- ID.Add(**I);
+ for (const auto &I : subPieces)
+ ID.Add(*I);
}
void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {
@@ -1175,34 +1256,32 @@ void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
Profile(ID);
- for (PathPieces::const_iterator I = path.begin(), E = path.end(); I != E; ++I)
- ID.Add(**I);
+ for (const auto &I : path)
+ ID.Add(*I);
for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
ID.AddString(*I);
}
-StackHintGenerator::~StackHintGenerator() {}
+StackHintGenerator::~StackHintGenerator() = default;
std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
+ if (!N)
+ return getMessageForSymbolNotFound();
+
ProgramPoint P = N->getLocation();
CallExitEnd CExit = P.castAs<CallExitEnd>();
// FIXME: Use CallEvent to abstract this over all calls.
const Stmt *CallSite = CExit.getCalleeContext()->getCallSite();
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite);
+ const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
if (!CE)
- return "";
-
- if (!N)
- return getMessageForSymbolNotFound();
+ return {};
// Check if one of the parameters are set to the interesting symbol.
- ProgramStateRef State = N->getState();
- const LocationContext *LCtx = N->getLocationContext();
unsigned ArgIndex = 0;
for (CallExpr::const_arg_iterator I = CE->arg_begin(),
E = CE->arg_end(); I != E; ++I, ++ArgIndex){
- SVal SV = State->getSVal(*I, LCtx);
+ SVal SV = N->getSVal(*I);
// Check if the variable corresponding to the symbol is passed by value.
SymbolRef AS = SV.getAsLocSymbol();
@@ -1212,7 +1291,10 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
// Check if the parameter is a pointer to the symbol.
if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
- SVal PSV = State->getSVal(Reg->getRegion());
+ // Do not attempt to dereference void*.
+ if ((*I)->getType()->isVoidPointerType())
+ continue;
+ SVal PSV = N->getState()->getSVal(Reg->getRegion());
SymbolRef AS = PSV.getAsLocSymbol();
if (AS == Sym) {
return getMessageForArg(*I, ArgIndex);
@@ -1221,7 +1303,7 @@ std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
}
// Check if we are returning the interesting symbol.
- SVal SV = State->getSVal(CE, LCtx);
+ SVal SV = N->getSVal(CE);
SymbolRef RetSym = SV.getAsLocSymbol();
if (RetSym == Sym) {
return getMessageForReturn(CE);
@@ -1243,3 +1325,84 @@ std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
return os.str();
}
+
+LLVM_DUMP_METHOD void PathPieces::dump() const {
+ unsigned index = 0;
+ for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {
+ llvm::errs() << "[" << index++ << "] ";
+ (*I)->dump();
+ llvm::errs() << "\n";
+ }
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const {
+ llvm::errs() << "CALL\n--------------\n";
+
+ if (const Stmt *SLoc = getLocation().getStmtOrNull())
+ SLoc->dump();
+ else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee()))
+ llvm::errs() << *ND << "\n";
+ else
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const {
+ llvm::errs() << "EVENT\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const {
+ llvm::errs() << "CONTROL\n--------------\n";
+ getStartLocation().dump();
+ llvm::errs() << " ---- to ----\n";
+ getEndLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {
+ llvm::errs() << "MACRO\n--------------\n";
+ // FIXME: Print which macro is being invoked.
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
+ llvm::errs() << "NOTE\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
+ if (!isValid()) {
+ llvm::errs() << "<INVALID>\n";
+ return;
+ }
+
+ switch (K) {
+ case RangeK:
+ // FIXME: actually print the range.
+ llvm::errs() << "<range>\n";
+ break;
+ case SingleLocK:
+ asLocation().dump();
+ llvm::errs() << "\n";
+ break;
+ case StmtK:
+ if (S)
+ S->dump();
+ else
+ llvm::errs() << "<NULL STMT>\n";
+ break;
+ case DeclK:
+ if (const auto *ND = dyn_cast_or_null<NamedDecl>(D))
+ llvm::errs() << *ND << "\n";
+ else if (isa<BlockDecl>(D))
+ // FIXME: Make this nicer.
+ llvm::errs() << "<block>\n";
+ else if (D)
+ llvm::errs() << "<unknown decl>\n";
+ else
+ llvm::errs() << "<NULL DECL>\n";
+ break;
+ }
+}
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index 66812ed8ff5b..cfe780db9ec9 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -16,9 +16,12 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/HTMLRewrite.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
using namespace clang;
@@ -30,6 +33,7 @@ namespace {
const std::string OutputFile;
const LangOptions &LangOpts;
const bool SupportsCrossFileDiagnostics;
+ const bool SerializeStatistics;
public:
PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
const std::string& prefix,
@@ -61,7 +65,8 @@ PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
bool supportsMultipleFiles)
: OutputFile(output),
LangOpts(LO),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
+ SupportsCrossFileDiagnostics(supportsMultipleFiles),
+ SerializeStatistics(AnalyzerOpts.shouldSerializeStats()) {}
void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
PathDiagnosticConsumers &C,
@@ -79,6 +84,41 @@ void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
PP.getLangOpts(), true));
}
+static void EmitRanges(raw_ostream &o,
+ const ArrayRef<SourceRange> Ranges,
+ const FIDMap& FM,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent) {
+
+ if (Ranges.empty())
+ return;
+
+ Indent(o, indent) << "<key>ranges</key>\n";
+ Indent(o, indent) << "<array>\n";
+ ++indent;
+ for (auto &R : Ranges)
+ EmitRange(o, SM,
+ Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
+ FM, indent + 1);
+ --indent;
+ Indent(o, indent) << "</array>\n";
+}
+
+static void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent) {
+ // Output the text.
+ assert(!Message.empty());
+ Indent(o, indent) << "<key>extended_message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+
+ // Output the short text.
+ // FIXME: Really use a short string.
+ Indent(o, indent) << "<key>message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+}
+
static void ReportControlFlow(raw_ostream &o,
const PathDiagnosticControlFlowPiece& P,
const FIDMap& FM,
@@ -133,7 +173,7 @@ static void ReportControlFlow(raw_ostream &o,
Indent(o, indent) << "</dict>\n";
}
-static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
+static void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
const FIDMap& FM,
const SourceManager &SM,
const LangOptions &LangOpts,
@@ -158,34 +198,14 @@ static void ReportEvent(raw_ostream &o, const PathDiagnosticPiece& P,
// Output the ranges (if any).
ArrayRef<SourceRange> Ranges = P.getRanges();
-
- if (!Ranges.empty()) {
- Indent(o, indent) << "<key>ranges</key>\n";
- Indent(o, indent) << "<array>\n";
- ++indent;
- for (auto &R : Ranges)
- EmitRange(o, SM,
- Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
- FM, indent + 1);
- --indent;
- Indent(o, indent) << "</array>\n";
- }
+ EmitRanges(o, Ranges, FM, SM, LangOpts, indent);
// Output the call depth.
Indent(o, indent) << "<key>depth</key>";
EmitInteger(o, depth) << '\n';
// Output the text.
- assert(!P.getString().empty());
- Indent(o, indent) << "<key>extended_message</key>\n";
- Indent(o, indent);
- EmitString(o, P.getString()) << '\n';
-
- // Output the short text.
- // FIXME: Really use a short string.
- Indent(o, indent) << "<key>message</key>\n";
- Indent(o, indent);
- EmitString(o, P.getString()) << '\n';
+ EmitMessage(o, P.getString(), indent);
// Finish up.
--indent;
@@ -241,6 +261,34 @@ static void ReportMacro(raw_ostream &o,
}
}
+static void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
+ const FIDMap& FM,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ unsigned indent,
+ unsigned depth) {
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ // Output the location.
+ FullSourceLoc L = P.getLocation().asLocation();
+
+ Indent(o, indent) << "<key>location</key>\n";
+ EmitLocation(o, SM, L, FM, indent);
+
+ // Output the ranges (if any).
+ ArrayRef<SourceRange> Ranges = P.getRanges();
+ EmitRanges(o, Ranges, FM, SM, LangOpts, indent);
+
+ // Output the text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent); o << "</dict>\n";
+}
+
static void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P,
const FIDMap& FM, const SourceManager &SM,
const LangOptions &LangOpts) {
@@ -266,7 +314,7 @@ static void ReportPiece(raw_ostream &o,
indent, depth);
break;
case PathDiagnosticPiece::Event:
- ReportEvent(o, cast<PathDiagnosticSpotPiece>(P), FM, SM, LangOpts,
+ ReportEvent(o, cast<PathDiagnosticEventPiece>(P), FM, SM, LangOpts,
indent, depth, isKeyEvent);
break;
case PathDiagnosticPiece::Macro:
@@ -274,7 +322,8 @@ static void ReportPiece(raw_ostream &o,
indent, depth);
break;
case PathDiagnosticPiece::Note:
- // FIXME: Extend the plist format to support those.
+ ReportNote(o, cast<PathDiagnosticNotePiece>(P), FM, SM, LangOpts,
+ indent, depth);
break;
}
}
@@ -359,15 +408,39 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
DE = Diags.end(); DI!=DE; ++DI) {
- o << " <dict>\n"
- " <key>path</key>\n";
+ o << " <dict>\n";
const PathDiagnostic *D = *DI;
+ const PathPieces &PP = D->path;
+
+ assert(std::is_partitioned(
+ PP.begin(), PP.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; }) &&
+ "PathDiagnostic is not partitioned so that notes precede the rest");
+
+ PathPieces::const_iterator FirstNonNote = std::partition_point(
+ PP.begin(), PP.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; });
+
+ PathPieces::const_iterator I = PP.begin();
+
+ if (FirstNonNote != PP.begin()) {
+ o << " <key>notes</key>\n"
+ " <array>\n";
+
+ for (; I != FirstNonNote; ++I)
+ ReportDiag(o, **I, FM, *SM, LangOpts);
+
+ o << " </array>\n";
+ }
+
+ o << " <key>path</key>\n";
o << " <array>\n";
- for (PathPieces::const_iterator I = D->path.begin(), E = D->path.end();
- I != E; ++I)
+ for (PathPieces::const_iterator E = PP.end(); I != E; ++I)
ReportDiag(o, **I, FM, *SM, LangOpts);
o << " </array>\n";
@@ -484,6 +557,15 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " </array>\n";
+ if (llvm::AreStatisticsEnabled() && SerializeStatistics) {
+ o << " <key>statistics</key>\n";
+ std::string stats;
+ llvm::raw_string_ostream os(stats);
+ llvm::PrintStatisticsJSON(os);
+ os.flush();
+ EmitString(o, html::EscapeText(stats)) << '\n';
+ }
+
// Finish.
o << "</dict>\n</plist>";
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index 5b6b7339697f..2b401607293b 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -125,16 +126,27 @@ ProgramStateRef ProgramState::bindLoc(Loc LV,
return newState;
}
-ProgramStateRef ProgramState::bindDefault(SVal loc,
- SVal V,
- const LocationContext *LCtx) const {
+ProgramStateRef
+ProgramState::bindDefaultInitial(SVal loc, SVal V,
+ const LocationContext *LCtx) const {
+ ProgramStateManager &Mgr = getStateManager();
+ const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
+ const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
+ ProgramStateRef new_state = makeWithStore(newStore);
+ return Mgr.getOwningEngine()
+ ? Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx)
+ : new_state;
+}
+
+ProgramStateRef
+ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const {
ProgramStateManager &Mgr = getStateManager();
const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
- const StoreRef &newStore = Mgr.StoreMgr->BindDefault(getStore(), R, V);
+ const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
ProgramStateRef new_state = makeWithStore(newStore);
- return Mgr.getOwningEngine() ?
- Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx) :
- new_state;
+ return Mgr.getOwningEngine()
+ ? Mgr.getOwningEngine()->processRegionChange(new_state, R, LCtx)
+ : new_state;
}
typedef ArrayRef<const MemRegion *> RegionList;
@@ -254,7 +266,7 @@ SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
}
SVal ProgramState::getSVal(Loc location, QualType T) const {
- SVal V = getRawSVal(cast<Loc>(location), T);
+ SVal V = getRawSVal(location, T);
// If 'V' is a symbolic value that is *perfectly* constrained to
// be a constant value, use that value instead to lessen the burden
@@ -324,9 +336,8 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
// Get the offset: the minimum value of the array index type.
BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
- // FIXME: This should be using ValueManager::ArrayindexTy...somehow.
if (indexTy.isNull())
- indexTy = Ctx.IntTy;
+ indexTy = svalBuilder.getArrayIndexType();
nonloc::ConcreteInt Min(BVF.getMinValue(indexTy));
// Adjust the index.
@@ -354,6 +365,17 @@ ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption);
}
+ConditionTruthVal ProgramState::isNonNull(SVal V) const {
+ ConditionTruthVal IsNull = isNull(V);
+ if (IsNull.isUnderconstrained())
+ return IsNull;
+ return ConditionTruthVal(!IsNull.getValue());
+}
+
+ConditionTruthVal ProgramState::areEqual(SVal Lhs, SVal Rhs) const {
+ return stateMgr->getSValBuilder().areEqual(this, Lhs, Rhs);
+}
+
ConditionTruthVal ProgramState::isNull(SVal V) const {
if (V.isZeroConstant())
return true;
@@ -426,24 +448,30 @@ void ProgramState::setStore(const StoreRef &newStore) {
// State pretty-printing.
//===----------------------------------------------------------------------===//
-void ProgramState::print(raw_ostream &Out,
- const char *NL, const char *Sep) const {
+void ProgramState::print(raw_ostream &Out, const char *NL, const char *Sep,
+ const LocationContext *LC) const {
// Print the store.
ProgramStateManager &Mgr = getStateManager();
Mgr.getStoreManager().print(getStore(), Out, NL, Sep);
// Print out the environment.
- Env.print(Out, NL, Sep);
+ Env.print(Out, NL, Sep, LC);
// Print out the constraints.
Mgr.getConstraintManager().print(this, Out, NL, Sep);
+ // Print out the tracked dynamic types.
+ printDynamicTypeInfo(this, Out, NL, Sep);
+
+ // Print out tainted symbols.
+ printTaint(Out, NL, Sep);
+
// Print checker-specific data.
- Mgr.getOwningEngine()->printState(Out, this, NL, Sep);
+ Mgr.getOwningEngine()->printState(Out, this, NL, Sep, LC);
}
-void ProgramState::printDOT(raw_ostream &Out) const {
- print(Out, "\\l", "\\|");
+void ProgramState::printDOT(raw_ostream &Out, const LocationContext *LC) const {
+ print(Out, "\\l", "\\|", LC);
}
LLVM_DUMP_METHOD void ProgramState::dump() const {
@@ -455,7 +483,7 @@ void ProgramState::printTaint(raw_ostream &Out,
TaintMapImpl TM = get<TaintMap>();
if (!TM.isEmpty())
- Out <<"Tainted Symbols:" << NL;
+ Out <<"Tainted symbols:" << NL;
for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) {
Out << I->first << " : " << I->second << NL;
@@ -781,8 +809,7 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
// complete. For example, this would not currently identify
// overlapping fields in a union as tainted. To identify this we can
// check for overlapping/nested byte offsets.
- if (Kind == I.second &&
- (R == I.first || R->isSubRegionOf(I.first)))
+ if (Kind == I.second && R->isSubRegionOf(I.first))
return true;
}
}
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 5a4031c0b4a5..e8c7bdbde385 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -12,10 +12,10 @@
//
//===----------------------------------------------------------------------===//
-#include "RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/ImmutableSet.h"
#include "llvm/Support/raw_ostream.h"
@@ -23,263 +23,203 @@
using namespace clang;
using namespace ento;
-/// A Range represents the closed range [from, to]. The caller must
-/// guarantee that from <= to. Note that Range is immutable, so as not
-/// to subvert RangeSet's immutability.
-namespace {
-class Range : public std::pair<const llvm::APSInt *, const llvm::APSInt *> {
-public:
- Range(const llvm::APSInt &from, const llvm::APSInt &to)
- : std::pair<const llvm::APSInt *, const llvm::APSInt *>(&from, &to) {
- assert(from <= to);
- }
- bool Includes(const llvm::APSInt &v) const {
- return *first <= v && v <= *second;
- }
- const llvm::APSInt &From() const { return *first; }
- const llvm::APSInt &To() const { return *second; }
- const llvm::APSInt *getConcreteValue() const {
- return &From() == &To() ? &From() : nullptr;
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddPointer(&From());
- ID.AddPointer(&To());
- }
-};
-
-class RangeTrait : public llvm::ImutContainerInfo<Range> {
-public:
- // When comparing if one Range is less than another, we should compare
- // the actual APSInt values instead of their pointers. This keeps the order
- // consistent (instead of comparing by pointer values) and can potentially
- // be used to speed up some of the operations in RangeSet.
- static inline bool isLess(key_type_ref lhs, key_type_ref rhs) {
- return *lhs.first < *rhs.first ||
- (!(*rhs.first < *lhs.first) && *lhs.second < *rhs.second);
- }
-};
-
-/// RangeSet contains a set of ranges. If the set is empty, then
-/// there the value of a symbol is overly constrained and there are no
-/// possible values for that symbol.
-class RangeSet {
- typedef llvm::ImmutableSet<Range, RangeTrait> PrimRangeSet;
- PrimRangeSet ranges; // no need to make const, since it is an
- // ImmutableSet - this allows default operator=
- // to work.
-public:
- typedef PrimRangeSet::Factory Factory;
- typedef PrimRangeSet::iterator iterator;
-
- RangeSet(PrimRangeSet RS) : ranges(RS) {}
-
- /// Create a new set with all ranges of this set and RS.
- /// Possible intersections are not checked here.
- RangeSet addRange(Factory &F, const RangeSet &RS) {
- PrimRangeSet Ranges(RS.ranges);
- for (const auto &range : ranges)
- Ranges = F.add(Ranges, range);
- return RangeSet(Ranges);
- }
-
- iterator begin() const { return ranges.begin(); }
- iterator end() const { return ranges.end(); }
-
- bool isEmpty() const { return ranges.isEmpty(); }
-
- /// Construct a new RangeSet representing '{ [from, to] }'.
- RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to)
- : ranges(F.add(F.getEmptySet(), Range(from, to))) {}
-
- /// Profile - Generates a hash profile of this RangeSet for use
- /// by FoldingSet.
- void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); }
-
- /// getConcreteValue - If a symbol is contrained to equal a specific integer
- /// constant then this method returns that value. Otherwise, it returns
- /// NULL.
- const llvm::APSInt *getConcreteValue() const {
- return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : nullptr;
- }
+void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
+ 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.
+ // 3. R contains the entire intersection range.
+ // 4. R starts before the intersection range and ends in the middle.
+ // 5. R starts in the middle of the intersection range and ends after it.
+ // 6. R is entirely contained in the intersection range.
+ // These correspond to each of the conditions below.
+ for (/* i = begin(), e = end() */; i != e; ++i) {
+ if (i->To() < Lower) {
+ continue;
+ }
+ if (i->From() > Upper) {
+ break;
+ }
-private:
- void IntersectInRange(BasicValueFactory &BV, Factory &F,
- 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.
- // 3. R contains the entire intersection range.
- // 4. R starts before the intersection range and ends in the middle.
- // 5. R starts in the middle of the intersection range and ends after it.
- // 6. R is entirely contained in the intersection range.
- // These correspond to each of the conditions below.
- for (/* i = begin(), e = end() */; i != e; ++i) {
- if (i->To() < Lower) {
- continue;
- }
- if (i->From() > Upper) {
+ if (i->Includes(Lower)) {
+ if (i->Includes(Upper)) {
+ newRanges =
+ F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper)));
break;
- }
-
- if (i->Includes(Lower)) {
- if (i->Includes(Upper)) {
- newRanges =
- F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper)));
- break;
- } else
- newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To()));
- } else {
- if (i->Includes(Upper)) {
- newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper)));
- break;
- } else
- newRanges = F.add(newRanges, *i);
- }
+ } else
+ newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To()));
+ } else {
+ if (i->Includes(Upper)) {
+ newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper)));
+ break;
+ } else
+ newRanges = F.add(newRanges, *i);
}
}
+}
- const llvm::APSInt &getMinValue() const {
- assert(!isEmpty());
- return ranges.begin()->From();
- }
+const llvm::APSInt &RangeSet::getMinValue() const {
+ assert(!isEmpty());
+ return ranges.begin()->From();
+}
- bool pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
- // 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.
- // The function returns false if the described range is entirely outside
- // the range of values for the associated symbol.
- APSIntType Type(getMinValue());
- APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true);
- APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true);
-
- switch (LowerTest) {
+bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ // 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.
+ // The function returns false if the described range is entirely outside
+ // the range of values for the associated symbol.
+ APSIntType Type(getMinValue());
+ APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true);
+ APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true);
+
+ switch (LowerTest) {
+ case APSIntType::RTR_Below:
+ switch (UpperTest) {
case APSIntType::RTR_Below:
- switch (UpperTest) {
- case APSIntType::RTR_Below:
- // The entire range is outside the symbol's set of possible values.
- // If this is a conventionally-ordered range, the state is infeasible.
- if (Lower <= Upper)
- return false;
-
- // However, if the range wraps around, it spans all possible values.
- Lower = Type.getMinValue();
- Upper = Type.getMaxValue();
- break;
- case APSIntType::RTR_Within:
- // The range starts below what's possible but ends within it. Pin.
- Lower = Type.getMinValue();
- Type.apply(Upper);
- break;
- case APSIntType::RTR_Above:
- // The range spans all possible values for the symbol. Pin.
- Lower = Type.getMinValue();
- Upper = Type.getMaxValue();
- break;
- }
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower <= Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
break;
case APSIntType::RTR_Within:
- switch (UpperTest) {
- case APSIntType::RTR_Below:
- // The range wraps around, but all lower values are not possible.
- Type.apply(Lower);
- Upper = Type.getMaxValue();
- break;
- case APSIntType::RTR_Within:
- // The range may or may not wrap around, but both limits are valid.
- Type.apply(Lower);
- Type.apply(Upper);
- break;
- case APSIntType::RTR_Above:
- // The range starts within what's possible but ends above it. Pin.
- Type.apply(Lower);
- Upper = Type.getMaxValue();
- break;
- }
+ // The range starts below what's possible but ends within it. Pin.
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
break;
case APSIntType::RTR_Above:
- switch (UpperTest) {
- case APSIntType::RTR_Below:
- // The range wraps but is outside the symbol's set of possible values.
- return false;
- case APSIntType::RTR_Within:
- // The range starts above what's possible but ends within it (wrap).
- Lower = Type.getMinValue();
- Type.apply(Upper);
- break;
- case APSIntType::RTR_Above:
- // The entire range is outside the symbol's set of possible values.
- // If this is a conventionally-ordered range, the state is infeasible.
- if (Lower <= Upper)
- return false;
-
- // However, if the range wraps around, it spans all possible values.
- Lower = Type.getMinValue();
- Upper = Type.getMaxValue();
- break;
- }
+ // The range spans all possible values for the symbol. Pin.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Within:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps around, but all lower values are not possible.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range may or may not wrap around, but both limits are valid.
+ Type.apply(Lower);
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range starts within what's possible but ends above it. Pin.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
break;
}
+ break;
+ case APSIntType::RTR_Above:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps but is outside the symbol's set of possible values.
+ return false;
+ case APSIntType::RTR_Within:
+ // The range starts above what's possible but ends within it (wrap).
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower <= Upper)
+ return false;
- return true;
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
}
-public:
- // Returns a set containing the values in the receiving set, intersected with
- // the closed range [Lower, Upper]. Unlike the Range type, this range uses
- // modular arithmetic, corresponding to the common treatment of C integer
- // overflow. Thus, if the Lower bound is greater than the Upper bound, the
- // range is taken to wrap around. This is equivalent to taking the
- // intersection with the two ranges [Min, Upper] and [Lower, Max],
- // or, alternatively, /removing/ all integers between Upper and Lower.
- RangeSet Intersect(BasicValueFactory &BV, Factory &F, llvm::APSInt Lower,
- llvm::APSInt Upper) const {
- if (!pin(Lower, Upper))
- return F.getEmptySet();
-
- PrimRangeSet newRanges = F.getEmptySet();
-
- PrimRangeSet::iterator i = begin(), e = end();
- if (Lower <= Upper)
- IntersectInRange(BV, F, Lower, Upper, newRanges, i, e);
- else {
- // The order of the next two statements is important!
- // IntersectInRange() does not reset the iteration state for i and e.
- // Therefore, the lower range most be handled first.
- IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e);
- IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e);
- }
+ return true;
+}
+
+// Returns a set containing the values in the receiving set, intersected with
+// the closed range [Lower, Upper]. Unlike the Range type, this range uses
+// modular arithmetic, corresponding to the common treatment of C integer
+// overflow. Thus, if the Lower bound is greater than the Upper bound, the
+// range is taken to wrap around. This is equivalent to taking the
+// intersection with the two ranges [Min, Upper] and [Lower, Max],
+// 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();
- return newRanges;
+ PrimRangeSet newRanges = F.getEmptySet();
+
+ PrimRangeSet::iterator i = begin(), e = end();
+ if (Lower <= Upper)
+ IntersectInRange(BV, F, Lower, Upper, newRanges, i, e);
+ else {
+ // The order of the next two statements is important!
+ // IntersectInRange() does not reset the iteration state for i and e.
+ // Therefore, the lower range most be handled first.
+ IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e);
+ IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e);
}
- void print(raw_ostream &os) const {
- bool isFirst = true;
- os << "{ ";
- for (iterator i = begin(), e = end(); i != e; ++i) {
- if (isFirst)
- isFirst = false;
- else
- os << ", ";
-
- os << '[' << i->From().toString(10) << ", " << i->To().toString(10)
- << ']';
+ 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.
+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)));
}
- os << " }";
}
- bool operator==(const RangeSet &other) const {
- return ranges == other.ranges;
- }
-};
-} // end anonymous namespace
+ return newRanges;
+}
-REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintRange,
- CLANG_ENTO_PROGRAMSTATE_MAP(SymbolRef,
- RangeSet))
+void RangeSet::print(raw_ostream &os) const {
+ bool isFirst = true;
+ os << "{ ";
+ for (iterator i = begin(), e = end(); i != e; ++i) {
+ if (isFirst)
+ isFirst = false;
+ else
+ os << ", ";
+
+ os << '[' << i->From().toString(10) << ", " << i->To().toString(10)
+ << ']';
+ }
+ os << " }";
+}
namespace {
class RangeConstraintManager : public RangedConstraintManager {
@@ -344,6 +284,8 @@ 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,
@@ -360,6 +302,7 @@ private:
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
+
};
} // end anonymous namespace
@@ -400,9 +343,11 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const {
if (BinaryOperator::isEqualityOp(SSE->getOpcode()) ||
BinaryOperator::isRelationalOp(SSE->getOpcode())) {
// We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc.
+ // We've recently started producing Loc <> NonLoc comparisons (that
+ // result from casts of one of the operands between eg. intptr_t and
+ // void *), but we can't reason about them yet.
if (Loc::isLocType(SSE->getLHS()->getType())) {
- assert(Loc::isLocType(SSE->getRHS()->getType()));
- return true;
+ return Loc::isLocType(SSE->getRHS()->getType());
}
}
}
@@ -474,7 +419,7 @@ static RangeSet assumeNonZero(
--IntType.getZeroValue());
}
-/// \brief Apply implicit constraints for bitwise OR- and AND-.
+/// 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.
@@ -515,9 +460,15 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym))
return *V;
+ BasicValueFactory &BV = getBasicVals();
+
+ // If Sym is a difference of symbols A - B, then maybe we have range set
+ // stored for B - A.
+ if (const RangeSet *R = getRangeForMinusSymbol(State, Sym))
+ return R->Negate(BV, F);
+
// Lazily generate a new RangeSet representing all possible values for the
// given symbol type.
- BasicValueFactory &BV = getBasicVals();
QualType T = Sym->getType();
RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T));
@@ -533,6 +484,32 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
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;
+}
+
//===------------------------------------------------------------------------===
// assumeSymX methods: protected interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
index 55ff15806efe..f99853f07073 100644
--- a/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -12,8 +12,8 @@
//
//===----------------------------------------------------------------------===//
-#include "RangedConstraintManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
namespace clang {
@@ -52,17 +52,18 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
assert(BinaryOperator::isComparisonOp(Op));
// For now, we only support comparing pointers.
- assert(Loc::isLocType(SSE->getLHS()->getType()));
- assert(Loc::isLocType(SSE->getRHS()->getType()));
- QualType DiffTy = SymMgr.getContext().getPointerDiffType();
- SymbolRef Subtraction =
- SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
-
- const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
- Op = BinaryOperator::reverseComparisonOp(Op);
- if (!Assumption)
- Op = BinaryOperator::negateComparisonOp(Op);
- return assumeSymRel(State, Subtraction, Op, Zero);
+ if (Loc::isLocType(SSE->getLHS()->getType()) &&
+ Loc::isLocType(SSE->getRHS()->getType())) {
+ QualType DiffTy = SymMgr.getContext().getPointerDiffType();
+ SymbolRef Subtraction =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
+ Op = BinaryOperator::reverseComparisonOp(Op);
+ if (!Assumption)
+ Op = BinaryOperator::negateComparisonOp(Op);
+ return assumeSymRel(State, Subtraction, Op, Zero);
+ }
}
// If we get here, there's nothing else we can do but treat the symbol as
diff --git a/lib/StaticAnalyzer/Core/RangedConstraintManager.h b/lib/StaticAnalyzer/Core/RangedConstraintManager.h
deleted file mode 100644
index a4e6062a4f57..000000000000
--- a/lib/StaticAnalyzer/Core/RangedConstraintManager.h
+++ /dev/null
@@ -1,102 +0,0 @@
-//== RangedConstraintManager.h ----------------------------------*- C++ -*--==//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Ranged constraint manager, built on SimpleConstraintManager.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
-#define LLVM_CLANG_LIB_STATICANALYZER_CORE_RANGEDCONSTRAINTMANAGER_H
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
-
-namespace clang {
-
-namespace ento {
-
-class RangedConstraintManager : public SimpleConstraintManager {
-public:
- RangedConstraintManager(SubEngine *SE, SValBuilder &SB)
- : SimpleConstraintManager(SE, SB) {}
-
- ~RangedConstraintManager() override;
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from SimpleConstraintManager.
- //===------------------------------------------------------------------===//
-
- ProgramStateRef assumeSym(ProgramStateRef State, SymbolRef Sym,
- bool Assumption) override;
-
- ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &From,
- const llvm::APSInt &To,
- bool InRange) override;
-
- ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
- bool Assumption) override;
-
-protected:
- /// Assume a constraint between a symbolic expression and a concrete integer.
- virtual ProgramStateRef assumeSymRel(ProgramStateRef State, SymbolRef Sym,
- BinaryOperator::Opcode op,
- const llvm::APSInt &Int);
-
- //===------------------------------------------------------------------===//
- // Interface that subclasses must implement.
- //===------------------------------------------------------------------===//
-
- // Each of these is of the form "$Sym+Adj <> V", where "<>" is the comparison
- // operation for the method being invoked.
-
- virtual ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymWithinInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0;
-
- virtual ProgramStateRef assumeSymOutsideInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) = 0;
-
- //===------------------------------------------------------------------===//
- // Internal implementation.
- //===------------------------------------------------------------------===//
-private:
- static void computeAdjustment(SymbolRef &Sym, llvm::APSInt &Adjustment);
-};
-
-} // end GR namespace
-
-} // end clang namespace
-
-#endif
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index e2e69bb28ec2..db6449e6d5f3 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -230,11 +230,6 @@ Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
}
Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
- if (R->isBoundable())
- if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R))
- if (TR->getValueType()->isUnionType())
- return UnknownVal();
-
return Optional<SVal>::create(lookup(R, BindingKey::Default));
}
@@ -338,7 +333,7 @@ private:
/// To disable all small-struct-dependent behavior, set the option to "0".
unsigned SmallStructLimit;
- /// \brief A helper used to populate the work list with the given set of
+ /// A helper used to populate the work list with the given set of
/// regions.
void populateWorkList(invalidateRegionsWorker &W,
ArrayRef<SVal> Values,
@@ -409,8 +404,22 @@ public: // Part of public interface to class.
RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V);
- // BindDefault is only used to initialize a region with a default value.
- StoreRef BindDefault(Store store, const MemRegion *R, SVal V) override {
+ // BindDefaultInitial is only used to initialize a region with
+ // a default value.
+ StoreRef BindDefaultInitial(Store store, const MemRegion *R,
+ SVal V) override {
+ RegionBindingsRef B = getRegionBindings(store);
+ // Use other APIs when you have to wipe the region that was initialized
+ // earlier.
+ assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
+ "Double initialization!");
+ B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
+ return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
+ }
+
+ // BindDefaultZero is used for zeroing constructors that may accidentally
+ // overwrite existing bindings.
+ StoreRef BindDefaultZero(Store store, const MemRegion *R) override {
// FIXME: The offsets of empty bases can be tricky because of
// of the so called "empty base class optimization".
// If a base class has been optimized out
@@ -420,24 +429,14 @@ public: // Part of public interface to class.
// and trying to infer them from offsets/alignments
// seems to be error-prone and non-trivial because of the trailing padding.
// As a temporary mitigation we don't create bindings for empty bases.
- if (R->getKind() == MemRegion::CXXBaseObjectRegionKind &&
- cast<CXXBaseObjectRegion>(R)->getDecl()->isEmpty())
- return StoreRef(store, *this);
+ if (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
+ if (BR->getDecl()->isEmpty())
+ return StoreRef(store, *this);
RegionBindingsRef B = getRegionBindings(store);
- assert(!B.lookup(R, BindingKey::Direct));
-
- BindingKey Key = BindingKey::Make(R, BindingKey::Default);
- if (B.lookup(Key)) {
- const SubRegion *SR = cast<SubRegion>(R);
- assert(SR->getAsOffset().getOffset() ==
- SR->getSuperRegion()->getAsOffset().getOffset() &&
- "A default value must come from a super-region");
- B = removeSubRegionBindings(B, SR);
- } else {
- B = B.addBinding(Key, V);
- }
-
+ SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
+ B = removeSubRegionBindings(B, cast<SubRegion>(R));
+ B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
}
@@ -474,7 +473,7 @@ public: // Part of public interface to class.
const TypedRegion *R,
SVal DefaultVal);
- /// \brief Create a new store with the specified binding removed.
+ /// Create a new store with the specified binding removed.
/// \param ST the original store, that is the basis for the new store.
/// \param L the location whose binding should be removed.
StoreRef killBinding(Store ST, Loc L) override;
@@ -492,7 +491,7 @@ public: // Part of public interface to class.
bool includedInBindings(Store store, const MemRegion *region) const override;
- /// \brief Return the value bound to specified location in a given state.
+ /// Return the value bound to specified location in a given state.
///
/// The high level logic for this method is this:
/// getBinding (L)
@@ -825,7 +824,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
FieldVector FieldsInSymbolicSubregions;
if (TopKey.hasSymbolicOffset()) {
getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions);
- Top = cast<SubRegion>(TopKey.getConcreteOffsetRegion());
+ Top = TopKey.getConcreteOffsetRegion();
TopKey = BindingKey::Make(Top, BindingKey::Default);
}
@@ -871,7 +870,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
} else if (NextKey.hasSymbolicOffset()) {
const MemRegion *Base = NextKey.getConcreteOffsetRegion();
- if (Top->isSubRegionOf(Base)) {
+ if (Top->isSubRegionOf(Base) && Top != Base) {
// Case 3: The next key is symbolic and we just changed something within
// its concrete region. We don't know if the binding is still valid, so
// we'll be conservative and include it.
@@ -881,7 +880,7 @@ collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
} else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
// Case 4: The next key is symbolic, but we changed a known
// super-region. In this case the binding is certainly included.
- if (Top == Base || BaseSR->isSubRegionOf(Top))
+ if (BaseSR->isSubRegionOf(Top))
if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
Bindings.push_back(*I);
}
@@ -1095,7 +1094,7 @@ void invalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
return;
}
- if (T->isStructureOrClassType()) {
+ if (T->isRecordType()) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelevant.
DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx,
@@ -1342,7 +1341,8 @@ RegionStoreManager::getSizeInElements(ProgramStateRef state,
// 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, false);
+ return svalBuilder.makeIntVal(RegionSize / EleSize,
+ svalBuilder.getArrayIndexType());
}
//===----------------------------------------------------------------------===//
@@ -1401,12 +1401,12 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
T = TR->getLocationType()->getPointeeType();
else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
T = SR->getSymbol()->getType()->getPointeeType();
- else if (isa<AllocaRegion>(MR))
- T = Ctx.VoidTy;
}
assert(!T.isNull() && "Unable to auto-detect binding type!");
assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
+ } else {
+ T = cast<TypedValueRegion>(MR)->getValueType();
}
// FIXME: Perhaps this method should just take a 'const MemRegion*' argument
@@ -1446,7 +1446,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
return UnknownVal();
if (const FieldRegion* FR = dyn_cast<FieldRegion>(R))
- return CastRetrievedVal(getBindingForField(B, FR), FR, T, false);
+ return CastRetrievedVal(getBindingForField(B, FR), FR, T);
if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) {
// FIXME: Here we actually perform an implicit conversion from the loaded
@@ -1454,7 +1454,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
// more intelligently. For example, an 'element' can encompass multiple
// bound regions (e.g., several bound bytes), or could be a subset of
// a larger value.
- return CastRetrievedVal(getBindingForElement(B, ER), ER, T, false);
+ return CastRetrievedVal(getBindingForElement(B, ER), ER, T);
}
if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) {
@@ -1464,7 +1464,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
// reinterpretted, it is possible we stored a different value that could
// fit within the ivar. Either we need to cast these when storing them
// or reinterpret them lazily (as we do here).
- return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T, false);
+ return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T);
}
if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
@@ -1474,7 +1474,7 @@ SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T)
// variable is reinterpretted, it is possible we stored a different value
// that could fit within the variable. Either we need to cast these when
// storing them or reinterpret them lazily (as we do here).
- return CastRetrievedVal(getBindingForVar(B, VR), VR, T, false);
+ return CastRetrievedVal(getBindingForVar(B, VR), VR, T);
}
const SVal *V = B.lookup(R, BindingKey::Direct);
@@ -1606,7 +1606,7 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const MemRegion* superR = R->getSuperRegion();
// Check if the region is an element region of a string literal.
- if (const StringRegion *StrR=dyn_cast<StringRegion>(superR)) {
+ if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
// FIXME: Handle loads from strings where the literal is treated as
// an integer, e.g., *((unsigned int*)"hello")
QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
@@ -1629,6 +1629,36 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
return svalBuilder.makeIntVal(c, T);
}
+ } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
+ // Check if the containing array is const and has an initialized value.
+ const VarDecl *VD = VR->getDecl();
+ // Either the array or the array element has to be const.
+ if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
+ if (const Expr *Init = VD->getInit()) {
+ if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
+ // The array index has to be known.
+ if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
+ int64_t i = CI->getValue().getSExtValue();
+ // If it is known that the index is out of bounds, we can return
+ // an undefined value.
+ if (i < 0)
+ return UndefinedVal();
+
+ if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
+ if (CAT->getSize().sle(i))
+ return UndefinedVal();
+
+ // If there is a list, but no init, it must be zero.
+ if (i >= InitList->getNumInits())
+ return svalBuilder.makeZeroVal(R->getElementType());
+
+ if (const Expr *ElemInit = InitList->getInit(i))
+ if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
+ return *V;
+ }
+ }
+ }
+ }
}
// Check for loads from a code text region. For such loads, just give up.
@@ -1678,7 +1708,34 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
if (const Optional<SVal> &V = B.getDirectBinding(R))
return *V;
- QualType Ty = R->getValueType();
+ // Is the field declared constant and has an in-class initializer?
+ const FieldDecl *FD = R->getDecl();
+ QualType Ty = FD->getType();
+ if (Ty.isConstQualified())
+ if (const Expr *Init = FD->getInClassInitializer())
+ if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ return *V;
+
+ // If the containing record was initialized, try to get its constant value.
+ const MemRegion* superR = R->getSuperRegion();
+ if (const auto *VR = dyn_cast<VarRegion>(superR)) {
+ const VarDecl *VD = VR->getDecl();
+ QualType RecordVarTy = VD->getType();
+ unsigned Index = FD->getFieldIndex();
+ // Either the record variable or the field has to be const qualified.
+ if (RecordVarTy.isConstQualified() || Ty.isConstQualified())
+ if (const Expr *Init = VD->getInit())
+ if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
+ if (Index < InitList->getNumInits()) {
+ if (const Expr *FieldInit = InitList->getInit(Index))
+ if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
+ return *V;
+ } else {
+ return svalBuilder.makeZeroVal(Ty);
+ }
+ }
+ }
+
return getBindingForFieldOrElementCommon(B, R, Ty);
}
@@ -1776,7 +1833,7 @@ RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
// quickly result in a warning.
bool hasPartialLazyBinding = false;
- const SubRegion *SR = dyn_cast<SubRegion>(R);
+ const SubRegion *SR = R;
while (SR) {
const MemRegion *Base = SR->getSuperRegion();
if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
@@ -2050,6 +2107,9 @@ RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) {
R = GetElementZeroRegion(SR, T);
}
+ assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) &&
+ "'this' pointer is not an l-value and is not assignable");
+
// Clear out bindings that may overlap with this binding.
RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V);
diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
new file mode 100644
index 000000000000..d379562bf325
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -0,0 +1,181 @@
+//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+using namespace clang;
+using namespace ento;
+
+ProgramStateRef SMTConstraintManager::assumeSym(ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption) {
+ ASTContext &Ctx = getBasicVals().getContext();
+
+ QualType RetTy;
+ bool hasComparison;
+
+ SMTExprRef Exp = Solver->getExpr(Ctx, Sym, &RetTy, &hasComparison);
+
+ // Create zero comparison for implicit boolean cast, with reversed assumption
+ if (!hasComparison && !RetTy->isBooleanType())
+ return assumeExpr(State, Sym,
+ Solver->getZeroExpr(Ctx, Exp, RetTy, !Assumption));
+
+ return assumeExpr(State, Sym, Assumption ? Exp : Solver->mkNot(Exp));
+}
+
+ProgramStateRef SMTConstraintManager::assumeSymInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InRange) {
+ ASTContext &Ctx = getBasicVals().getContext();
+ return assumeExpr(State, Sym,
+ Solver->getRangeExpr(Ctx, Sym, From, To, InRange));
+}
+
+ProgramStateRef
+SMTConstraintManager::assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
+ bool Assumption) {
+ // Skip anything that is unsupported
+ return State;
+}
+
+ConditionTruthVal SMTConstraintManager::checkNull(ProgramStateRef State,
+ SymbolRef Sym) {
+ ASTContext &Ctx = getBasicVals().getContext();
+
+ QualType RetTy;
+ // The expression may be casted, so we cannot call getZ3DataExpr() directly
+ SMTExprRef VarExp = Solver->getExpr(Ctx, Sym, &RetTy);
+ SMTExprRef Exp = Solver->getZeroExpr(Ctx, VarExp, RetTy, /*Assumption=*/true);
+
+ // Negate the constraint
+ SMTExprRef NotExp =
+ Solver->getZeroExpr(Ctx, VarExp, RetTy, /*Assumption=*/false);
+
+ Solver->reset();
+ addStateConstraints(State);
+
+ Solver->push();
+ Solver->addConstraint(Exp);
+ ConditionTruthVal isSat = Solver->check();
+
+ Solver->pop();
+ Solver->addConstraint(NotExp);
+ ConditionTruthVal isNotSat = Solver->check();
+
+ // Zero is the only possible solution
+ if (isSat.isConstrainedTrue() && isNotSat.isConstrainedFalse())
+ return true;
+
+ // Zero is not a solution
+ if (isSat.isConstrainedFalse() && isNotSat.isConstrainedTrue())
+ return false;
+
+ // Zero may be a solution
+ return ConditionTruthVal();
+}
+
+const llvm::APSInt *SMTConstraintManager::getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const {
+ BasicValueFactory &BVF = getBasicVals();
+ ASTContext &Ctx = BVF.getContext();
+
+ if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
+ QualType Ty = Sym->getType();
+ assert(!Ty->isRealFloatingType());
+ llvm::APSInt Value(Ctx.getTypeSize(Ty),
+ !Ty->isSignedIntegerOrEnumerationType());
+
+ SMTExprRef Exp =
+ Solver->fromData(SD->getSymbolID(), Ty, Ctx.getTypeSize(Ty));
+
+ Solver->reset();
+ addStateConstraints(State);
+
+ // Constraints are unsatisfiable
+ ConditionTruthVal isSat = Solver->check();
+ if (!isSat.isConstrainedTrue())
+ return nullptr;
+
+ // Model does not assign interpretation
+ if (!Solver->getInterpretation(Exp, Value))
+ return nullptr;
+
+ // A value has been obtained, check if it is the only value
+ SMTExprRef NotExp = Solver->fromBinOp(
+ Exp, BO_NE,
+ Ty->isBooleanType() ? Solver->fromBoolean(Value.getBoolValue())
+ : Solver->fromAPSInt(Value),
+ false);
+
+ Solver->addConstraint(NotExp);
+
+ ConditionTruthVal isNotSat = Solver->check();
+ if (isNotSat.isConstrainedTrue())
+ return nullptr;
+
+ // This is the only solution, store it
+ return &BVF.getValue(Value);
+ }
+
+ if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
+ SymbolRef CastSym = SC->getOperand();
+ QualType CastTy = SC->getType();
+ // Skip the void type
+ if (CastTy->isVoidType())
+ return nullptr;
+
+ const llvm::APSInt *Value;
+ if (!(Value = getSymVal(State, CastSym)))
+ return nullptr;
+ return &BVF.Convert(SC->getType(), *Value);
+ }
+
+ if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
+ const llvm::APSInt *LHS, *RHS;
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
+ LHS = getSymVal(State, SIE->getLHS());
+ RHS = &SIE->getRHS();
+ } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
+ LHS = &ISE->getLHS();
+ RHS = getSymVal(State, ISE->getRHS());
+ } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
+ // Early termination to avoid expensive call
+ LHS = getSymVal(State, SSM->getLHS());
+ RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
+ } else {
+ llvm_unreachable("Unsupported binary expression to get symbol value!");
+ }
+
+ if (!LHS || !RHS)
+ return nullptr;
+
+ llvm::APSInt ConvertedLHS, ConvertedRHS;
+ QualType LTy, RTy;
+ std::tie(ConvertedLHS, LTy) = Solver->fixAPSInt(Ctx, *LHS);
+ std::tie(ConvertedRHS, RTy) = Solver->fixAPSInt(Ctx, *RHS);
+ Solver->doIntTypeConversion<llvm::APSInt, &SMTSolver::castAPSInt>(
+ Ctx, ConvertedLHS, LTy, ConvertedRHS, RTy);
+ return BVF.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
+ }
+
+ llvm_unreachable("Unsupported expression to get symbol value!");
+}
+
+ConditionTruthVal
+SMTConstraintManager::checkModel(ProgramStateRef State,
+ const SMTExprRef &Exp) const {
+ Solver->reset();
+ Solver->addConstraint(Exp);
+ addStateConstraints(State);
+ return Solver->check();
+}
diff --git a/lib/StaticAnalyzer/Core/SValBuilder.cpp b/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 04452e3e7cc2..f292dca8e99f 100644
--- a/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -1,4 +1,4 @@
-// SValBuilder.cpp - Basic class for all SValBuilder implementations -*- C++ -*-
+//===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,12 +13,33 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#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/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"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+#include <tuple>
using namespace clang;
using namespace ento;
@@ -27,7 +48,7 @@ using namespace ento;
// Basic SVal creation.
//===----------------------------------------------------------------------===//
-void SValBuilder::anchor() { }
+void SValBuilder::anchor() {}
DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
if (Loc::isLocType(type))
@@ -95,12 +116,12 @@ nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){
}
DefinedOrUnknownSVal
-SValBuilder::getRegionValueSymbolVal(const TypedValueRegion* region) {
+SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) {
QualType T = region->getValueType();
if (T->isNullPtrType())
return makeZeroVal(T);
-
+
if (!SymbolManager::canSymbolicate(T))
return UnknownVal();
@@ -149,7 +170,6 @@ DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag,
return nonloc::SymbolVal(sym);
}
-
DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt,
const LocationContext *LCtx,
QualType type,
@@ -217,10 +237,10 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
return nonloc::SymbolVal(sym);
}
-DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl* DD) {
+DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD));
- if (auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) {
// Sema treats pointers to static member functions as have function pointer
// type, so return a function pointer for the method.
// We don't need to play a similar trick for static member fields
@@ -277,19 +297,19 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
return makeZeroVal(E->getType());
case Stmt::ObjCStringLiteralClass: {
- const ObjCStringLiteral *SL = cast<ObjCStringLiteral>(E);
+ const auto *SL = cast<ObjCStringLiteral>(E);
return makeLoc(getRegionManager().getObjCStringRegion(SL));
}
case Stmt::StringLiteralClass: {
- const StringLiteral *SL = cast<StringLiteral>(E);
+ const auto *SL = cast<StringLiteral>(E);
return makeLoc(getRegionManager().getStringRegion(SL));
}
// Fast-path some expressions to avoid the overhead of going through the AST's
// constant evaluator
case Stmt::CharacterLiteralClass: {
- const CharacterLiteral *C = cast<CharacterLiteral>(E);
+ const auto *C = cast<CharacterLiteral>(E);
return makeIntVal(C->getValue(), C->getType());
}
@@ -297,7 +317,7 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
return makeBoolVal(cast<CXXBoolLiteralExpr>(E));
case Stmt::TypeTraitExprClass: {
- const TypeTraitExpr *TE = cast<TypeTraitExpr>(E);
+ const auto *TE = cast<TypeTraitExpr>(E);
return makeTruthVal(TE->getValue(), TE->getType());
}
@@ -310,12 +330,19 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
case Stmt::CXXNullPtrLiteralExprClass:
return makeNull();
+ case Stmt::CStyleCastExprClass:
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::CXXConstCastExprClass:
+ case Stmt::CXXReinterpretCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
case Stmt::ImplicitCastExprClass: {
- const CastExpr *CE = cast<CastExpr>(E);
+ const auto *CE = cast<CastExpr>(E);
switch (CE->getCastKind()) {
default:
break;
case CK_ArrayToPointerDecay:
+ case CK_IntegralToPointer:
+ case CK_NoOp:
case CK_BitCast: {
const Expr *SE = CE->getSubExpr();
Optional<SVal> Val = getConstantVal(SE);
@@ -348,20 +375,18 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
}
}
-//===----------------------------------------------------------------------===//
-
SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
BinaryOperator::Opcode Op,
NonLoc LHS, NonLoc RHS,
QualType ResultTy) {
- if (!State->isTainted(RHS) && !State->isTainted(LHS))
- return UnknownVal();
-
const SymExpr *symLHS = LHS.getAsSymExpr();
const SymExpr *symRHS = RHS.getAsSymExpr();
+
// TODO: When the Max Complexity is reached, we should conjure a symbol
// instead of generating an Unknown value and propagate the taint info to it.
- const unsigned MaxComp = 10000; // 100000 28X
+ const unsigned MaxComp = StateMgr.getOwningEngine()
+ ->getAnalysisManager()
+ .options.getMaxSymbolComplexity();
if (symLHS && symRHS &&
(symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
@@ -378,10 +403,8 @@ SVal SValBuilder::makeSymExprValNN(ProgramStateRef State,
return UnknownVal();
}
-
SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
SVal lhs, SVal rhs, QualType type) {
-
if (lhs.isUndef() || rhs.isUndef())
return UndefinedVal();
@@ -413,10 +436,19 @@ SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
type);
}
+ConditionTruthVal SValBuilder::areEqual(ProgramStateRef state, SVal lhs,
+ SVal rhs) {
+ return state->isNonNull(evalEQ(state, lhs, rhs));
+}
+
+SVal SValBuilder::evalEQ(ProgramStateRef state, SVal lhs, SVal rhs) {
+ return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType());
+}
+
DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state,
DefinedOrUnknownSVal lhs,
DefinedOrUnknownSVal rhs) {
- return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType())
+ return evalEQ(state, static_cast<SVal>(lhs), static_cast<SVal>(rhs))
.castAs<DefinedOrUnknownSVal>();
}
@@ -425,7 +457,7 @@ DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state,
/// Assumes the input types are canonical.
static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy,
QualType FromTy) {
- while (Context.UnwrapSimilarPointerTypes(ToTy, FromTy)) {
+ while (Context.UnwrapSimilarTypes(ToTy, FromTy)) {
Qualifiers Quals1, Quals2;
ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1);
FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2);
@@ -440,6 +472,10 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy,
// If we are casting to void, the 'From' value can be used to represent the
// 'To' value.
+ //
+ // FIXME: Doing this after unwrapping the types doesn't make any sense. A
+ // cast from 'int**' to 'void**' is not special in the way that a cast from
+ // 'int*' to 'void*' is.
if (ToTy->isVoidType())
return true;
@@ -454,7 +490,6 @@ static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy,
// of the original value is known to be greater than the max of the target type.
SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
QualType castTy, QualType originalTy) {
-
// No truncations if target type is big enough.
if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy))
return evalCast(val, castTy, originalTy);
@@ -548,8 +583,8 @@ SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
}
// Check for casts from array type to another type.
- if (const ArrayType *arrayT =
- dyn_cast<ArrayType>(originalTy.getCanonicalType())) {
+ if (const auto *arrayT =
+ dyn_cast<ArrayType>(originalTy.getCanonicalType())) {
// We will always decay to a pointer.
QualType elemTy = arrayT->getElementType();
val = StateMgr.ArrayToPointer(val.castAs<Loc>(), elemTy);
diff --git a/lib/StaticAnalyzer/Core/SVals.cpp b/lib/StaticAnalyzer/Core/SVals.cpp
index a83421426a13..559ca2c9840d 100644
--- a/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/lib/StaticAnalyzer/Core/SVals.cpp
@@ -1,4 +1,4 @@
-//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==//
+//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,20 +12,31 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/AST/ExprObjC.h"
-#include "clang/Basic/IdentifierTable.h"
-#include "llvm/Support/raw_ostream.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
using namespace clang;
using namespace ento;
-using llvm::APSInt;
//===----------------------------------------------------------------------===//
// Symbol iteration within an SVal.
//===----------------------------------------------------------------------===//
-
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
@@ -39,7 +50,7 @@ bool SVal::hasConjuredSymbol() const {
if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) {
const MemRegion *R = RV->getRegion();
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
SymbolRef sym = SR->getSymbol();
if (isa<SymbolConjured>(sym))
return true;
@@ -53,18 +64,18 @@ const FunctionDecl *SVal::getAsFunctionDecl() const {
if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
const MemRegion* R = X->getRegion();
if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>())
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CTR->getDecl()))
+ if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl()))
return FD;
}
if (auto X = getAs<nonloc::PointerToMember>()) {
- if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl()))
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl()))
return MD;
}
return nullptr;
}
-/// \brief If this SVal is a location (subclasses Loc) and wraps a symbol,
+/// If this SVal is a location (subclasses Loc) and wraps a symbol,
/// return that SymbolRef. Otherwise return 0.
///
/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element
@@ -95,8 +106,8 @@ SymbolRef SVal::getLocSymbolInBase() const {
const MemRegion *R = X->getRegion();
- while (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
- if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR))
+ while (const auto *SR = dyn_cast<SubRegion>(R)) {
+ if (const auto *SymR = dyn_cast<SymbolicRegion>(SR))
return SymR->getSymbol();
else
R = SR->getSuperRegion();
@@ -107,7 +118,7 @@ SymbolRef SVal::getLocSymbolInBase() const {
// TODO: The next 3 functions have to be simplified.
-/// \brief If this SVal wraps a symbol return that SymbolRef.
+/// If this SVal wraps a symbol return that SymbolRef.
/// Otherwise, return 0.
///
/// Casts are ignored during lookup.
@@ -189,14 +200,14 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const {
nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
const PTMDataType PTMD = getPTMData();
if (PTMD.is<const DeclaratorDecl *>())
- return nonloc::PointerToMember::iterator();
+ return {};
return PTMD.get<const PointerToMemberData *>()->begin();
}
nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
const PTMDataType PTMD = getPTMData();
if (PTMD.is<const DeclaratorDecl *>())
- return nonloc::PointerToMember::iterator();
+ return {};
return PTMD.get<const PointerToMemberData *>()->end();
}
@@ -220,7 +231,6 @@ bool SVal::isZeroConstant() const {
return isConstant(0);
}
-
//===----------------------------------------------------------------------===//
// Transfer function dispatch for Non-Locs.
//===----------------------------------------------------------------------===//
@@ -254,7 +264,6 @@ nonloc::ConcreteInt::evalMinus(SValBuilder &svalBuilder) const {
SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals,
BinaryOperator::Opcode Op,
const loc::ConcreteInt& R) const {
-
assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub);
const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue());
@@ -291,19 +300,15 @@ void SVal::dumpToStream(raw_ostream &os) const {
void NonLoc::dumpToStream(raw_ostream &os) const {
switch (getSubKind()) {
case nonloc::ConcreteIntKind: {
- const nonloc::ConcreteInt& C = castAs<nonloc::ConcreteInt>();
- if (C.getValue().isUnsigned())
- os << C.getValue().getZExtValue();
- else
- os << C.getValue().getSExtValue();
- os << ' ' << (C.getValue().isUnsigned() ? 'U' : 'S')
- << C.getValue().getBitWidth() << 'b';
+ const auto &Value = castAs<nonloc::ConcreteInt>().getValue();
+ os << Value << ' ' << (Value.isSigned() ? 'S' : 'U')
+ << Value.getBitWidth() << 'b';
break;
}
- case nonloc::SymbolValKind: {
+ case nonloc::SymbolValKind:
os << castAs<nonloc::SymbolVal>().getSymbol();
break;
- }
+
case nonloc::LocAsIntegerKind: {
const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>();
os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]";
@@ -313,14 +318,14 @@ void NonLoc::dumpToStream(raw_ostream &os) const {
const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>();
os << "compoundVal{";
bool first = true;
- for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) {
+ for (const auto &I : C) {
if (first) {
os << ' '; first = false;
}
else
os << ", ";
- (*I).dumpToStream(os);
+ I.dumpToStream(os);
}
os << "}";
break;
@@ -353,7 +358,7 @@ void NonLoc::dumpToStream(raw_ostream &os) const {
break;
}
default:
- assert (false && "Pretty-printed not implemented for this NonLoc.");
+ assert(false && "Pretty-printed not implemented for this NonLoc.");
break;
}
}
diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 94d29d5a6ba3..beae0dfae289 100644
--- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -12,8 +12,10 @@
//===----------------------------------------------------------------------===//
#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/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
using namespace clang;
@@ -157,7 +159,8 @@ SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) {
return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR));
if (const SymbolicRegion *SymR = R->getSymbolicBase())
- return nonloc::SymbolVal(SymR->getSymbol());
+ return makeNonLoc(SymR->getSymbol(), BO_NE,
+ BasicVals.getZeroWithPtrWidth(), castTy);
// FALL-THROUGH
LLVM_FALLTHROUGH;
@@ -307,6 +310,197 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
return makeNonLoc(LHS, op, *ConvertedRHS, resultTy);
}
+// See if Sym is known to be a relation Rel with Bound.
+static bool isInRelation(BinaryOperator::Opcode Rel, SymbolRef Sym,
+ llvm::APSInt Bound, ProgramStateRef State) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ SVal Result =
+ SVB.evalBinOpNN(State, Rel, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Bound), SVB.getConditionType());
+ if (auto DV = Result.getAs<DefinedSVal>()) {
+ return !State->assume(*DV, false);
+ }
+ return false;
+}
+
+// See if Sym is known to be within [min/4, max/4], where min and max
+// are the bounds of the symbol's integral type. With such symbols,
+// some manipulations can be performed without the risk of overflow.
+// assume() doesn't cause infinite recursion because we should be dealing
+// with simpler symbols on every recursive call.
+static bool isWithinConstantOverflowBounds(SymbolRef Sym,
+ ProgramStateRef State) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+
+ QualType T = Sym->getType();
+ assert(T->isSignedIntegerOrEnumerationType() &&
+ "This only works with signed integers!");
+ APSIntType AT = BV.getAPSIntType(T);
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max;
+ return isInRelation(BO_LE, Sym, Max, State) &&
+ isInRelation(BO_GE, Sym, Min, State);
+}
+
+// Same for the concrete integers: see if I is within [min/4, max/4].
+static bool isWithinConstantOverflowBounds(llvm::APSInt I) {
+ APSIntType AT(I);
+ assert(!AT.isUnsigned() &&
+ "This only works with signed integers!");
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max;
+ return (I <= Max) && (I >= -Max);
+}
+
+static std::pair<SymbolRef, llvm::APSInt>
+decomposeSymbol(SymbolRef Sym, BasicValueFactory &BV) {
+ if (const auto *SymInt = dyn_cast<SymIntExpr>(Sym))
+ if (BinaryOperator::isAdditiveOp(SymInt->getOpcode()))
+ return std::make_pair(SymInt->getLHS(),
+ (SymInt->getOpcode() == BO_Add) ?
+ (SymInt->getRHS()) :
+ (-SymInt->getRHS()));
+
+ // Fail to decompose: "reduce" the problem to the "$x + 0" case.
+ return std::make_pair(Sym, BV.getValue(0, Sym->getType()));
+}
+
+// Simplify "(LSym + LInt) Op (RSym + RInt)" assuming all values are of the
+// same signed integral type and no overflows occur (which should be checked
+// by the caller).
+static NonLoc doRearrangeUnchecked(ProgramStateRef State,
+ BinaryOperator::Opcode Op,
+ SymbolRef LSym, llvm::APSInt LInt,
+ SymbolRef RSym, llvm::APSInt RInt) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+ SymbolManager &SymMgr = SVB.getSymbolManager();
+
+ QualType SymTy = LSym->getType();
+ assert(SymTy == RSym->getType() &&
+ "Symbols are not of the same type!");
+ assert(APSIntType(LInt) == BV.getAPSIntType(SymTy) &&
+ "Integers are not of the same type as symbols!");
+ assert(APSIntType(RInt) == BV.getAPSIntType(SymTy) &&
+ "Integers are not of the same type as symbols!");
+
+ QualType ResultTy;
+ if (BinaryOperator::isComparisonOp(Op))
+ ResultTy = SVB.getConditionType();
+ else if (BinaryOperator::isAdditiveOp(Op))
+ ResultTy = SymTy;
+ else
+ llvm_unreachable("Operation not suitable for unchecked rearrangement!");
+
+ // FIXME: Can we use assume() without getting into an infinite recursion?
+ if (LSym == RSym)
+ return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt),
+ nonloc::ConcreteInt(RInt), ResultTy)
+ .castAs<NonLoc>();
+
+ SymbolRef ResultSym = nullptr;
+ BinaryOperator::Opcode ResultOp;
+ llvm::APSInt ResultInt;
+ if (BinaryOperator::isComparisonOp(Op)) {
+ // Prefer comparing to a non-negative number.
+ // FIXME: Maybe it'd be better to have consistency in
+ // "$x - $y" vs. "$y - $x" because those are solver's keys.
+ if (LInt > RInt) {
+ ResultSym = SymMgr.getSymSymExpr(RSym, BO_Sub, LSym, SymTy);
+ ResultOp = BinaryOperator::reverseComparisonOp(Op);
+ ResultInt = LInt - RInt; // Opposite order!
+ } else {
+ ResultSym = SymMgr.getSymSymExpr(LSym, BO_Sub, RSym, SymTy);
+ ResultOp = Op;
+ ResultInt = RInt - LInt; // Opposite order!
+ }
+ } else {
+ ResultSym = SymMgr.getSymSymExpr(LSym, Op, RSym, SymTy);
+ ResultInt = (Op == BO_Add) ? (LInt + RInt) : (LInt - RInt);
+ ResultOp = BO_Add;
+ // Bring back the cosmetic difference.
+ if (ResultInt < 0) {
+ ResultInt = -ResultInt;
+ ResultOp = BO_Sub;
+ } else if (ResultInt == 0) {
+ // Shortcut: Simplify "$x + 0" to "$x".
+ return nonloc::SymbolVal(ResultSym);
+ }
+ }
+ const llvm::APSInt &PersistentResultInt = BV.getValue(ResultInt);
+ return nonloc::SymbolVal(
+ SymMgr.getSymIntExpr(ResultSym, ResultOp, PersistentResultInt, ResultTy));
+}
+
+// Rearrange if symbol type matches the result type and if the operator is a
+// comparison operator, both symbol and constant must be within constant
+// overflow bounds.
+static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op,
+ SymbolRef Sym, llvm::APSInt Int, QualType Ty) {
+ return Sym->getType() == Ty &&
+ (!BinaryOperator::isComparisonOp(Op) ||
+ (isWithinConstantOverflowBounds(Sym, State) &&
+ isWithinConstantOverflowBounds(Int)));
+}
+
+static Optional<NonLoc> tryRearrange(ProgramStateRef State,
+ BinaryOperator::Opcode Op, NonLoc Lhs,
+ NonLoc Rhs, QualType ResultTy) {
+ ProgramStateManager &StateMgr = State->getStateManager();
+ SValBuilder &SVB = StateMgr.getSValBuilder();
+
+ // We expect everything to be of the same type - this type.
+ QualType SingleTy;
+
+ auto &Opts =
+ StateMgr.getOwningEngine()->getAnalysisManager().getAnalyzerOptions();
+
+ // FIXME: After putting complexity threshold to the symbols we can always
+ // rearrange additive operations but rearrange comparisons only if
+ // option is set.
+ if(!Opts.shouldAggressivelySimplifyBinaryOperation())
+ return None;
+
+ SymbolRef LSym = Lhs.getAsSymbol();
+ if (!LSym)
+ return None;
+
+ if (BinaryOperator::isComparisonOp(Op)) {
+ SingleTy = LSym->getType();
+ if (ResultTy != SVB.getConditionType())
+ return None;
+ // Initialize SingleTy later with a symbol's type.
+ } else if (BinaryOperator::isAdditiveOp(Op)) {
+ SingleTy = ResultTy;
+ if (LSym->getType() != SingleTy)
+ return None;
+ // Substracting unsigned integers is a nightmare.
+ if (!SingleTy->isSignedIntegerOrEnumerationType())
+ return None;
+ } else {
+ // Don't rearrange other operations.
+ return None;
+ }
+
+ assert(!SingleTy.isNull() && "We should have figured out the type by now!");
+
+ SymbolRef RSym = Rhs.getAsSymbol();
+ if (!RSym || RSym->getType() != SingleTy)
+ return None;
+
+ BasicValueFactory &BV = State->getBasicVals();
+ llvm::APSInt LInt, RInt;
+ std::tie(LSym, LInt) = decomposeSymbol(LSym, BV);
+ std::tie(RSym, RInt) = decomposeSymbol(RSym, BV);
+ if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) ||
+ !shouldRearrange(State, Op, RSym, RInt, SingleTy))
+ return None;
+
+ // We know that no overflows can occur anymore.
+ return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt);
+}
+
SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
BinaryOperator::Opcode op,
NonLoc lhs, NonLoc rhs,
@@ -559,6 +753,9 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
+ if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
+ return *V;
+
// Give up -- this is not a symbolic expression we can handle.
return makeSymExprValNN(state, op, InputLHS, InputRHS, resultTy);
}
@@ -988,6 +1185,12 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
elementType = resultTy->getPointeeType();
}
+ // Represent arithmetic on void pointers as arithmetic on char pointers.
+ // It is fine when a TypedValueRegion of char value type represents
+ // a void pointer. Note that arithmetic on void pointers is a GCC extension.
+ if (elementType->isVoidType())
+ elementType = getContext().CharTy;
+
if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) {
return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV,
superR, getContext()));
@@ -1023,24 +1226,42 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
ProgramStateRef State;
SValBuilder &SVB;
+ // Cache results for the lifetime of the Simplifier. Results change every
+ // time new constraints are added to the program state, which is the whole
+ // point of simplifying, and for that very reason it's pointless to maintain
+ // the same cache for the duration of the whole analysis.
+ llvm::DenseMap<SymbolRef, SVal> Cached;
+
+ static bool isUnchanged(SymbolRef Sym, SVal Val) {
+ return Sym == Val.getAsSymbol();
+ }
+
public:
Simplifier(ProgramStateRef State)
: State(State), SVB(State->getStateManager().getSValBuilder()) {}
SVal VisitSymbolData(const SymbolData *S) {
if (const llvm::APSInt *I =
- SVB.getKnownValue(State, nonloc::SymbolVal(S)))
+ SVB.getKnownValue(State, SVB.makeSymbolVal(S)))
return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
: (SVal)SVB.makeIntVal(*I);
- return Loc::isLocType(S->getType()) ? (SVal)SVB.makeLoc(S)
- : nonloc::SymbolVal(S);
+ return SVB.makeSymbolVal(S);
}
// TODO: Support SymbolCast. Support IntSymExpr when/if we actually
// start producing them.
SVal VisitSymIntExpr(const SymIntExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+
SVal LHS = Visit(S->getLHS());
+ if (isUnchanged(S->getLHS(), LHS)) {
+ SVal V = SVB.makeSymbolVal(S);
+ Cached[S] = V;
+ return V;
+ }
SVal RHS;
// By looking at the APSInt in the right-hand side of S, we cannot
// figure out if it should be treated as a Loc or as a NonLoc.
@@ -1059,13 +1280,27 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
} else {
RHS = SVB.makeIntVal(S->getRHS());
}
- return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+
+ SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+ Cached[S] = V;
+ return V;
}
SVal VisitSymSymExpr(const SymSymExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+
SVal LHS = Visit(S->getLHS());
SVal RHS = Visit(S->getRHS());
- return SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+ if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) {
+ SVal V = SVB.makeSymbolVal(S);
+ Cached[S] = V;
+ return V;
+ }
+ SVal V = SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType());
+ Cached[S] = V;
+ return V;
}
SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
@@ -1075,13 +1310,20 @@ SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
// Simplification is much more costly than computing complexity.
// For high complexity, it may be not worth it.
- if (V.getSymbol()->computeComplexity() > 100)
- return V;
return Visit(V.getSymbol());
}
SVal VisitSVal(SVal V) { return V; }
};
- return Simplifier(State).Visit(V);
+ // A crude way of preventing this function from calling itself from evalBinOp.
+ static bool isReentering = false;
+ if (isReentering)
+ return V;
+
+ isReentering = true;
+ SVal SimplifiedV = Simplifier(State).Visit(V);
+ isReentering = false;
+
+ return SimplifiedV;
}
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 173fdd8d0056..5ab5c082269b 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -1,4 +1,4 @@
-//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==//
+//===- Store.cpp - Interface for maps from Locations to Values ------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,18 +12,37 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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/StoreRef.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
+#include <cstdint>
using namespace clang;
using namespace ento;
StoreManager::StoreManager(ProgramStateManager &stateMgr)
- : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
- MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
+ : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
+ MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
StoreRef StoreManager::enterStackFrame(Store OldStore,
const CallEvent &Call,
@@ -33,11 +52,8 @@ StoreRef StoreManager::enterStackFrame(Store OldStore,
SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
Call.getInitialStackFrameContents(LCtx, InitialBindings);
- for (CallEvent::BindingsTy::iterator I = InitialBindings.begin(),
- E = InitialBindings.end();
- I != E; ++I) {
- Store = Bind(Store.getStore(), I->first, I->second);
- }
+ for (const auto &I : InitialBindings)
+ Store = Bind(Store.getStore(), I.first, I.second);
return Store;
}
@@ -49,10 +65,6 @@ const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base,
return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext());
}
-StoreRef StoreManager::BindDefault(Store store, const MemRegion *R, SVal V) {
- return StoreRef(store, *this);
-}
-
const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
QualType T) {
NonLoc idx = svalBuilder.makeZeroArrayIndex();
@@ -61,7 +73,6 @@ const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
}
const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) {
-
ASTContext &Ctx = StateMgr.getContext();
// Handle casts to Objective-C objects.
@@ -92,7 +103,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
// Handle casts from compatible types.
if (R->isBoundable())
- if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
if (CanonPointeeTy == ObjTy)
return R;
@@ -164,7 +175,7 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
// Edge case: we are at 0 bytes off the beginning of baseR. We
// check to see if type we are casting to is the same as the base
// region. If so, just return the base region.
- if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(baseR)) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
if (CanonPointeeTy == ObjTy)
@@ -219,7 +230,7 @@ static bool regionMatchesCXXRecordType(SVal V, QualType Ty) {
if (!MR)
return true;
- const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR);
+ const auto *TVR = dyn_cast<TypedValueRegion>(MR);
if (!TVR)
return true;
@@ -253,11 +264,9 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) {
SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) {
// Walk through the path to create nested CXXBaseRegions.
SVal Result = Derived;
- for (CXXBasePath::const_iterator I = Path.begin(), E = Path.end();
- I != E; ++I) {
- Result = evalDerivedToBase(Result, I->Base->getType(),
- I->Base->isVirtual());
- }
+ for (const auto &I : Path)
+ Result = evalDerivedToBase(Result, I.Base->getType(),
+ I.Base->isVirtual());
return Result;
}
@@ -286,9 +295,9 @@ SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
/// symbolic regions, where the dynamic type is merely bounded (and even then,
/// only ostensibly!), but does not take advantage of any dynamic type info.
static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) {
- if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR))
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(MR))
return TVR->getValueType()->getAsCXXRecordDecl();
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
return SR->getSymbol()->getType()->getPointeeCXXRecordDecl();
return nullptr;
}
@@ -327,7 +336,7 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
return evalDerivedToBase(loc::MemRegionVal(MR), Paths.front());
}
- if (const CXXBaseObjectRegion *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) {
+ if (const auto *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) {
// Drill down the chain to get the derived classes.
MR = BaseR->getSuperRegion();
continue;
@@ -348,7 +357,7 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
const MemRegion *Uncasted = MR->StripCasts(/*IncludeBaseCasts=*/false);
if (Uncasted == MR) {
// We reached the bottom of the hierarchy and did not find the derived
- // class. We we must be casting the base to derived, so the cast should
+ // class. We must be casting the base to derived, so the cast should
// fail.
break;
}
@@ -361,27 +370,27 @@ SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
return UnknownVal();
}
-
/// CastRetrievedVal - Used by subclasses of StoreManager to implement
/// implicit casts that arise from loads from regions that are reinterpreted
/// as another region.
SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
- QualType castTy, bool performTestOnly) {
-
+ QualType castTy) {
if (castTy.isNull() || V.isUnknownOrUndef())
return V;
- ASTContext &Ctx = svalBuilder.getContext();
-
- if (performTestOnly) {
- // Automatically translate references to pointers.
- QualType T = R->getValueType();
- if (const ReferenceType *RT = T->getAs<ReferenceType>())
- T = Ctx.getPointerType(RT->getPointeeType());
-
- assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T));
- return V;
- }
+ // When retrieving symbolic pointer and expecting a non-void pointer,
+ // wrap them into element regions of the expected type if necessary.
+ // SValBuilder::dispatchCast() doesn't do that, but it is necessary to
+ // make sure that the retrieved value makes sense, because there's no other
+ // cast in the AST that would tell us to cast it to the correct pointer type.
+ // We might need to do that for non-void pointers as well.
+ // FIXME: We really need a single good function to perform casts for us
+ // correctly every time we need it.
+ if (castTy->isPointerType() && !castTy->isVoidPointerType())
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion()))
+ if (SR->getSymbol()->getType().getCanonicalType() !=
+ castTy.getCanonicalType())
+ return loc::MemRegionVal(castRegion(SR, castTy));
return svalBuilder.dispatchCast(V, castTy);
}
@@ -421,7 +430,7 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
// NOTE: We must have this check first because ObjCIvarDecl is a subclass
// of FieldDecl.
- if (const ObjCIvarDecl *ID = dyn_cast<ObjCIvarDecl>(D))
+ if (const auto *ID = dyn_cast<ObjCIvarDecl>(D))
return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR));
return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR));
@@ -433,7 +442,6 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
-
// If the base is an unknown or undefined value, just return it back.
// FIXME: For absolute pointer addresses, we just return that value back as
// well, although in reality we should return the offset added to that
@@ -448,13 +456,12 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();
// Pointer of any type can be cast and used as array base.
- const ElementRegion *ElemR = dyn_cast<ElementRegion>(BaseRegion);
+ const auto *ElemR = dyn_cast<ElementRegion>(BaseRegion);
// Convert the offset to the appropriate size and signedness.
Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>();
if (!ElemR) {
- //
// If the base region is not an ElementRegion, create one.
// This can happen in the following example:
//
@@ -462,7 +469,6 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
// p[1] = 8;
//
// Observe that 'p' binds to an AllocaRegion.
- //
return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset,
BaseRegion, Ctx));
}
@@ -499,7 +505,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
Ctx));
}
-StoreManager::BindingsHandler::~BindingsHandler() {}
+StoreManager::BindingsHandler::~BindingsHandler() = default;
bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
Store store,
diff --git a/lib/StaticAnalyzer/Core/SymbolManager.cpp b/lib/StaticAnalyzer/Core/SymbolManager.cpp
index f2d5ee83f3cc..ed197010ebb7 100644
--- a/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -1,4 +1,4 @@
-//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==//
+//===- SymbolManager.h - Management of Symbolic Values --------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -13,15 +13,27 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include <cassert>
using namespace clang;
using namespace ento;
-void SymExpr::anchor() { }
+void SymExpr::anchor() {}
LLVM_DUMP_METHOD void SymExpr::dump() const {
dumpToStream(llvm::errs());
@@ -88,7 +100,7 @@ void SymbolMetadata::dumpToStream(raw_ostream &os) const {
<< getRegion() << ',' << T.getAsString() << '}';
}
-void SymbolData::anchor() { }
+void SymbolData::anchor() {}
void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
os << "reg_$" << getSymbolID()
@@ -138,7 +150,7 @@ void SymExpr::symbol_iterator::expand() {
itr.push_back(cast<IntSymExpr>(SE)->getRHS());
return;
case SymExpr::SymSymExprKind: {
- const SymSymExpr *x = cast<SymSymExpr>(SE);
+ const auto *x = cast<SymSymExpr>(SE);
itr.push_back(x->getLHS());
itr.push_back(x->getRHS());
return;
@@ -147,13 +159,6 @@ void SymExpr::symbol_iterator::expand() {
llvm_unreachable("unhandled expansion case");
}
-unsigned SymExpr::computeComplexity() const {
- unsigned R = 0;
- for (symbol_iterator I = symbol_begin(), E = symbol_end(); I != E; ++I)
- R++;
- return R;
-}
-
const SymbolRegionValue*
SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
llvm::FoldingSetNodeID profile;
@@ -192,7 +197,6 @@ const SymbolConjured* SymbolManager::conjureSymbol(const Stmt *E,
const SymbolDerived*
SymbolManager::getDerivedSymbol(SymbolRef parentSymbol,
const TypedValueRegion *R) {
-
llvm::FoldingSetNodeID profile;
SymbolDerived::Profile(profile, parentSymbol, R);
void *InsertPos;
@@ -227,7 +231,6 @@ const SymbolMetadata *
SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T,
const LocationContext *LCtx,
unsigned Count, const void *SymbolTag) {
-
llvm::FoldingSetNodeID profile;
SymbolMetadata::Profile(profile, R, S, T, LCtx, Count, SymbolTag);
void *InsertPos;
@@ -382,11 +385,10 @@ void SymbolReaper::markDependentsLive(SymbolRef sym) {
LI->second = HaveMarkedDependents;
if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) {
- for (SymbolRefSmallVectorTy::const_iterator I = Deps->begin(),
- E = Deps->end(); I != E; ++I) {
- if (TheLiving.find(*I) != TheLiving.end())
+ for (const auto I : *Deps) {
+ if (TheLiving.find(I) != TheLiving.end())
continue;
- markLive(*I);
+ markLive(I);
}
}
}
@@ -405,7 +407,7 @@ void SymbolReaper::markLive(const MemRegion *region) {
void SymbolReaper::markElementIndicesLive(const MemRegion *region) {
for (auto SR = dyn_cast<SubRegion>(region); SR;
SR = dyn_cast<SubRegion>(SR->getSuperRegion())) {
- if (auto ER = dyn_cast<ElementRegion>(SR)) {
+ if (const auto ER = dyn_cast<ElementRegion>(SR)) {
SVal Idx = ER->getIndex();
for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI)
markLive(*SI);
@@ -432,10 +434,10 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
MR = MR->getBaseRegion();
- if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
return isLive(SR->getSymbol());
- if (const VarRegion *VR = dyn_cast<VarRegion>(MR))
+ if (const auto *VR = dyn_cast<VarRegion>(MR))
return isLive(VR, true);
// FIXME: This is a gross over-approximation. What we really need is a way to
@@ -533,7 +535,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
if (!LCtx)
return false;
- const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame();
+ const StackFrameContext *CurrentContext = LCtx->getStackFrame();
if (VarContext == CurrentContext) {
// If no statement is provided, everything is live.
@@ -547,7 +549,7 @@ bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
return false;
unsigned &cachedQuery =
- const_cast<SymbolReaper*>(this)->includedRegionCache[VR];
+ const_cast<SymbolReaper *>(this)->includedRegionCache[VR];
if (cachedQuery) {
return cachedQuery == 1;
diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp
new file mode 100644
index 000000000000..4b227375da9b
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/WorkList.cpp
@@ -0,0 +1,254 @@
+//===- WorkList.cpp - Analyzer work-list implementation--------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines different worklist implementations for the static analyzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
+#include "llvm/ADT/PriorityQueue.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Statistic.h"
+#include <deque>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "WorkList"
+
+STATISTIC(MaxQueueSize, "Maximum size of the worklist");
+STATISTIC(MaxReachableSize, "Maximum size of auxiliary worklist set");
+
+//===----------------------------------------------------------------------===//
+// Worklist classes for exploration of reachable states.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class DFS : public WorkList {
+ SmallVector<WorkListUnit, 20> Stack;
+
+public:
+ bool hasWork() const override {
+ return !Stack.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ Stack.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ assert(!Stack.empty());
+ const WorkListUnit& U = Stack.back();
+ Stack.pop_back(); // This technically "invalidates" U, but we are fine.
+ return U;
+ }
+};
+
+class BFS : public WorkList {
+ std::deque<WorkListUnit> Queue;
+
+public:
+ bool hasWork() const override {
+ return !Queue.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ Queue.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ WorkListUnit U = Queue.front();
+ Queue.pop_front();
+ return U;
+ }
+};
+
+} // namespace
+
+// Place the dstor for WorkList here because it contains virtual member
+// functions, and we the code for the dstor generated in one compilation unit.
+WorkList::~WorkList() = default;
+
+std::unique_ptr<WorkList> WorkList::makeDFS() {
+ return llvm::make_unique<DFS>();
+}
+
+std::unique_ptr<WorkList> WorkList::makeBFS() {
+ return llvm::make_unique<BFS>();
+}
+
+namespace {
+
+ class BFSBlockDFSContents : public WorkList {
+ std::deque<WorkListUnit> Queue;
+ SmallVector<WorkListUnit, 20> Stack;
+
+ public:
+ bool hasWork() const override {
+ return !Queue.empty() || !Stack.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ if (U.getNode()->getLocation().getAs<BlockEntrance>())
+ Queue.push_front(U);
+ else
+ Stack.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ // Process all basic blocks to completion.
+ if (!Stack.empty()) {
+ const WorkListUnit& U = Stack.back();
+ Stack.pop_back(); // This technically "invalidates" U, but we are fine.
+ return U;
+ }
+
+ assert(!Queue.empty());
+ // Don't use const reference. The subsequent pop_back() might make it
+ // unsafe.
+ WorkListUnit U = Queue.front();
+ Queue.pop_front();
+ return U;
+ }
+ };
+
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() {
+ return llvm::make_unique<BFSBlockDFSContents>();
+}
+
+namespace {
+
+class UnexploredFirstStack : public WorkList {
+ /// Stack of nodes known to have statements we have not traversed yet.
+ SmallVector<WorkListUnit, 20> StackUnexplored;
+
+ /// Stack of all other nodes.
+ SmallVector<WorkListUnit, 20> StackOthers;
+
+ using BlockID = unsigned;
+ using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
+
+ llvm::DenseSet<LocIdentifier> Reachable;
+
+public:
+ bool hasWork() const override {
+ return !(StackUnexplored.empty() && StackOthers.empty());
+ }
+
+ void enqueue(const WorkListUnit &U) override {
+ const ExplodedNode *N = U.getNode();
+ auto BE = N->getLocation().getAs<BlockEntrance>();
+
+ if (!BE) {
+ // Assume the choice of the order of the preceeding block entrance was
+ // correct.
+ StackUnexplored.push_back(U);
+ } else {
+ LocIdentifier LocId = std::make_pair(
+ BE->getBlock()->getBlockID(),
+ N->getLocationContext()->getStackFrame());
+ auto InsertInfo = Reachable.insert(LocId);
+
+ if (InsertInfo.second) {
+ StackUnexplored.push_back(U);
+ } else {
+ StackOthers.push_back(U);
+ }
+ }
+ MaxReachableSize.updateMax(Reachable.size());
+ MaxQueueSize.updateMax(StackUnexplored.size() + StackOthers.size());
+ }
+
+ WorkListUnit dequeue() override {
+ if (!StackUnexplored.empty()) {
+ WorkListUnit &U = StackUnexplored.back();
+ StackUnexplored.pop_back();
+ return U;
+ } else {
+ WorkListUnit &U = StackOthers.back();
+ StackOthers.pop_back();
+ return U;
+ }
+ }
+};
+
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() {
+ return llvm::make_unique<UnexploredFirstStack>();
+}
+
+namespace {
+class UnexploredFirstPriorityQueue : public WorkList {
+ using BlockID = unsigned;
+ using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
+
+ // How many times each location was visited.
+ // Is signed because we negate it later in order to have a reversed
+ // comparison.
+ using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>;
+
+ // Compare by number of times the location was visited first (negated
+ // to prefer less often visited locations), then by insertion time (prefer
+ // expanding nodes inserted sooner first).
+ using QueuePriority = std::pair<int, unsigned long>;
+ using QueueItem = std::pair<WorkListUnit, QueuePriority>;
+
+ struct ExplorationComparator {
+ bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
+ return LHS.second < RHS.second;
+ }
+ };
+
+ // Number of inserted nodes, used to emulate DFS ordering in the priority
+ // queue when insertions are equal.
+ unsigned long Counter = 0;
+
+ // Number of times a current location was reached.
+ VisitedTimesMap NumReached;
+
+ // The top item is the largest one.
+ llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
+ queue;
+
+public:
+ bool hasWork() const override {
+ return !queue.empty();
+ }
+
+ void enqueue(const WorkListUnit &U) override {
+ const ExplodedNode *N = U.getNode();
+ unsigned NumVisited = 0;
+ if (auto BE = N->getLocation().getAs<BlockEntrance>()) {
+ LocIdentifier LocId = std::make_pair(
+ BE->getBlock()->getBlockID(),
+ N->getLocationContext()->getStackFrame());
+ NumVisited = NumReached[LocId]++;
+ }
+
+ queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter)));
+ }
+
+ WorkListUnit dequeue() override {
+ QueueItem U = queue.top();
+ queue.pop();
+ return U.first;
+ }
+};
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() {
+ return llvm::make_unique<UnexploredFirstPriorityQueue>();
+}
diff --git a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
index f9f9057a89cd..7379ded49c80 100644
--- a/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -10,7 +10,11 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSolver.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTSort.h"
#include "clang/Config/config.h"
@@ -21,30 +25,9 @@ using namespace ento;
#include <z3.h>
-// Forward declarations
-namespace {
-class Z3Expr;
-class ConstraintZ3 {};
-} // end anonymous namespace
-
-typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty;
-
-// Expansion of REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, Z3SetPair)
-namespace clang {
-namespace ento {
-template <>
-struct ProgramStateTrait<ConstraintZ3>
- : public ProgramStatePartialTrait<ConstraintZ3Ty> {
- static void *GDMIndex() {
- static int Index;
- return &Index;
- }
-};
-} // end namespace ento
-} // end namespace clang
-
namespace {
+/// Configuration class for Z3
class Z3Config {
friend class Z3Context;
@@ -63,45 +46,60 @@ public:
~Z3Config() { Z3_del_config(Config); }
}; // end class Z3Config
-class Z3Context {
- Z3_context ZC_P;
+// Function used to report errors
+void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) {
+ llvm::report_fatal_error("Z3 error: " +
+ llvm::Twine(Z3_get_error_msg_ex(Context, Error)));
+}
+/// Wrapper for Z3 context
+class Z3Context : public SMTContext {
public:
- static Z3_context ZC;
+ Z3_context Context;
- Z3Context() : ZC_P(Z3_mk_context_rc(Z3Config().Config)) { ZC = ZC_P; }
+ Z3Context() : SMTContext() {
+ Context = Z3_mk_context_rc(Z3Config().Config);
+ // The error function is set here because the context is the first object
+ // created by the backend
+ Z3_set_error_handler(Context, Z3ErrorHandler);
+ }
- ~Z3Context() {
- Z3_del_context(ZC);
- Z3_finalize_memory();
- ZC_P = nullptr;
+ virtual ~Z3Context() {
+ Z3_del_context(Context);
+ Context = nullptr;
}
}; // end class Z3Context
-class Z3Sort {
- friend class Z3Expr;
+/// Wrapper for Z3 Sort
+class Z3Sort : public SMTSort {
+ friend class Z3Solver;
+
+ Z3Context &Context;
Z3_sort Sort;
- Z3Sort() : Sort(nullptr) {}
- Z3Sort(Z3_sort ZS) : Sort(ZS) {
- Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+public:
+ /// Default constructor, mainly used by make_shared
+ Z3Sort(Z3Context &C, Z3_sort ZS) : SMTSort(), Context(C), Sort(ZS) {
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
}
-public:
/// Override implicit copy constructor for correct reference counting.
- Z3Sort(const Z3Sort &Copy) : Sort(Copy.Sort) {
- Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ Z3Sort(const Z3Sort &Copy)
+ : SMTSort(), Context(Copy.Context), Sort(Copy.Sort) {
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
}
/// Provide move constructor
- Z3Sort(Z3Sort &&Move) : Sort(nullptr) { *this = std::move(Move); }
+ Z3Sort(Z3Sort &&Move) : SMTSort(), Context(Move.Context), Sort(nullptr) {
+ *this = std::move(Move);
+ }
/// Provide move assignment constructor
Z3Sort &operator=(Z3Sort &&Move) {
if (this != &Move) {
if (Sort)
- Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
Sort = Move.Sort;
Move.Sort = nullptr;
}
@@ -110,119 +108,78 @@ public:
~Z3Sort() {
if (Sort)
- Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
- }
-
- // Return a boolean sort.
- static Z3Sort getBoolSort() { return Z3Sort(Z3_mk_bool_sort(Z3Context::ZC)); }
-
- // Return an appropriate bitvector sort for the given bitwidth.
- static Z3Sort getBitvectorSort(unsigned BitWidth) {
- return Z3Sort(Z3_mk_bv_sort(Z3Context::ZC, BitWidth));
- }
-
- // Return an appropriate floating-point sort for the given bitwidth.
- static Z3Sort getFloatSort(unsigned BitWidth) {
- Z3_sort Sort;
-
- switch (BitWidth) {
- default:
- llvm_unreachable("Unsupported floating-point bitwidth!");
- break;
- case 16:
- Sort = Z3_mk_fpa_sort_16(Z3Context::ZC);
- break;
- case 32:
- Sort = Z3_mk_fpa_sort_32(Z3Context::ZC);
- break;
- case 64:
- Sort = Z3_mk_fpa_sort_64(Z3Context::ZC);
- break;
- case 128:
- Sort = Z3_mk_fpa_sort_128(Z3Context::ZC);
- break;
- }
- return Z3Sort(Sort);
+ Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
}
- // Return an appropriate sort for the given AST.
- static Z3Sort getSort(Z3_ast AST) {
- return Z3Sort(Z3_get_sort(Z3Context::ZC, AST));
+ bool isBitvectorSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT);
}
- Z3_sort_kind getSortKind() const {
- return Z3_get_sort_kind(Z3Context::ZC, Sort);
+ bool isFloatSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT);
}
- unsigned getBitvectorSortSize() const {
- assert(getSortKind() == Z3_BV_SORT && "Not a bitvector sort!");
- return Z3_get_bv_sort_size(Z3Context::ZC, Sort);
+ bool isBooleanSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT);
}
- unsigned getFloatSortSize() const {
- assert(getSortKind() == Z3_FLOATING_POINT_SORT &&
- "Not a floating-point sort!");
- return Z3_fpa_get_ebits(Z3Context::ZC, Sort) +
- Z3_fpa_get_sbits(Z3Context::ZC, Sort);
+ unsigned getBitvectorSortSizeImpl() const override {
+ return Z3_get_bv_sort_size(Context.Context, Sort);
}
- bool operator==(const Z3Sort &Other) const {
- return Z3_is_eq_sort(Z3Context::ZC, Sort, Other.Sort);
+ unsigned getFloatSortSizeImpl() const override {
+ return Z3_fpa_get_ebits(Context.Context, Sort) +
+ Z3_fpa_get_sbits(Context.Context, Sort);
+ }
+
+ bool equal_to(SMTSort const &Other) const override {
+ return Z3_is_eq_sort(Context.Context, Sort,
+ static_cast<const Z3Sort &>(Other).Sort);
}
Z3Sort &operator=(const Z3Sort &Move) {
- Z3_inc_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Move.Sort));
- Z3_dec_ref(Z3Context::ZC, reinterpret_cast<Z3_ast>(Sort));
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Move.Sort));
+ Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
Sort = Move.Sort;
return *this;
}
- void print(raw_ostream &OS) const {
- OS << Z3_sort_to_string(Z3Context::ZC, Sort);
+ void print(raw_ostream &OS) const override {
+ OS << Z3_sort_to_string(Context.Context, Sort);
}
-
- LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
}; // end class Z3Sort
-class Z3Expr {
- friend class Z3Model;
- friend class Z3Solver;
+static const Z3Sort &toZ3Sort(const SMTSort &S) {
+ return static_cast<const Z3Sort &>(S);
+}
- Z3_ast AST;
+class Z3Expr : public SMTExpr {
+ friend class Z3Solver;
- Z3Expr(Z3_ast ZA) : AST(ZA) { Z3_inc_ref(Z3Context::ZC, AST); }
+ Z3Context &Context;
- // Return an appropriate floating-point rounding mode.
- static Z3Expr getFloatRoundingMode() {
- // TODO: Don't assume nearest ties to even rounding mode
- return Z3Expr(Z3_mk_fpa_rne(Z3Context::ZC));
- }
+ Z3_ast AST;
- // Determine whether two float semantics are equivalent
- static bool areEquivalent(const llvm::fltSemantics &LHS,
- const llvm::fltSemantics &RHS) {
- return (llvm::APFloat::semanticsPrecision(LHS) ==
- llvm::APFloat::semanticsPrecision(RHS)) &&
- (llvm::APFloat::semanticsMinExponent(LHS) ==
- llvm::APFloat::semanticsMinExponent(RHS)) &&
- (llvm::APFloat::semanticsMaxExponent(LHS) ==
- llvm::APFloat::semanticsMaxExponent(RHS)) &&
- (llvm::APFloat::semanticsSizeInBits(LHS) ==
- llvm::APFloat::semanticsSizeInBits(RHS));
+public:
+ Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) {
+ Z3_inc_ref(Context.Context, AST);
}
-public:
/// Override implicit copy constructor for correct reference counting.
- Z3Expr(const Z3Expr &Copy) : AST(Copy.AST) { Z3_inc_ref(Z3Context::ZC, AST); }
+ Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) {
+ Z3_inc_ref(Context.Context, AST);
+ }
/// Provide move constructor
- Z3Expr(Z3Expr &&Move) : AST(nullptr) { *this = std::move(Move); }
+ Z3Expr(Z3Expr &&Move) : SMTExpr(), Context(Move.Context), AST(nullptr) {
+ *this = std::move(Move);
+ }
/// Provide move assignment constructor
Z3Expr &operator=(Z3Expr &&Move) {
if (this != &Move) {
if (AST)
- Z3_dec_ref(Z3Context::ZC, AST);
+ Z3_dec_ref(Context.Context, AST);
AST = Move.AST;
Move.AST = nullptr;
}
@@ -231,1388 +188,854 @@ public:
~Z3Expr() {
if (AST)
- Z3_dec_ref(Z3Context::ZC, AST);
- }
-
- /// Get the corresponding IEEE floating-point type for a given bitwidth.
- static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) {
- switch (BitWidth) {
- default:
- llvm_unreachable("Unsupported floating-point semantics!");
- break;
- case 16:
- return llvm::APFloat::IEEEhalf();
- case 32:
- return llvm::APFloat::IEEEsingle();
- case 64:
- return llvm::APFloat::IEEEdouble();
- case 128:
- return llvm::APFloat::IEEEquad();
- }
+ Z3_dec_ref(Context.Context, AST);
}
- /// Construct a Z3Expr from a unary operator, given a Z3_context.
- static Z3Expr fromUnOp(const UnaryOperator::Opcode Op, const Z3Expr &Exp) {
- Z3_ast AST;
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddInteger(Z3_get_ast_hash(Context.Context, AST));
+ }
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
+ /// Comparison of AST equality, not model equivalence.
+ bool equal_to(SMTExpr const &Other) const override {
+ assert(Z3_is_eq_sort(Context.Context, Z3_get_sort(Context.Context, AST),
+ Z3_get_sort(Context.Context,
+ static_cast<const Z3Expr &>(Other).AST)) &&
+ "AST's must have the same sort");
+ return Z3_is_eq_ast(Context.Context, AST,
+ static_cast<const Z3Expr &>(Other).AST);
+ }
- case UO_Minus:
- AST = Z3_mk_bvneg(Z3Context::ZC, Exp.AST);
- break;
+ /// Override implicit move constructor for correct reference counting.
+ Z3Expr &operator=(const Z3Expr &Move) {
+ Z3_inc_ref(Context.Context, Move.AST);
+ Z3_dec_ref(Context.Context, AST);
+ AST = Move.AST;
+ return *this;
+ }
- case UO_Not:
- AST = Z3_mk_bvnot(Z3Context::ZC, Exp.AST);
- break;
+ void print(raw_ostream &OS) const override {
+ OS << Z3_ast_to_string(Context.Context, AST);
+ }
+}; // end class Z3Expr
- case UO_LNot:
- AST = Z3_mk_not(Z3Context::ZC, Exp.AST);
- break;
- }
+static const Z3Expr &toZ3Expr(const SMTExpr &E) {
+ return static_cast<const Z3Expr &>(E);
+}
- return Z3Expr(AST);
- }
+class Z3Model {
+ friend class Z3Solver;
+
+ Z3Context &Context;
+
+ Z3_model Model;
- /// Construct a Z3Expr from a floating-point unary operator, given a
- /// Z3_context.
- static Z3Expr fromFloatUnOp(const UnaryOperator::Opcode Op,
- const Z3Expr &Exp) {
- Z3_ast AST;
+public:
+ Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) {
+ assert(C.Context != nullptr);
+ Z3_model_inc_ref(Context.Context, Model);
+ }
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Model(const Z3Model &Copy) : Context(Copy.Context), Model(Copy.Model) {
+ Z3_model_inc_ref(Context.Context, Model);
+ }
- case UO_Minus:
- AST = Z3_mk_fpa_neg(Z3Context::ZC, Exp.AST);
- break;
+ /// Provide move constructor
+ Z3Model(Z3Model &&Move) : Context(Move.Context), Model(nullptr) {
+ *this = std::move(Move);
+ }
- case UO_LNot:
- return Z3Expr::fromUnOp(Op, Exp);
+ /// Provide move assignment constructor
+ Z3Model &operator=(Z3Model &&Move) {
+ if (this != &Move) {
+ if (Model)
+ Z3_model_dec_ref(Context.Context, Model);
+ Model = Move.Model;
+ Move.Model = nullptr;
}
+ return *this;
+ }
- return Z3Expr(AST);
+ ~Z3Model() {
+ if (Model)
+ Z3_model_dec_ref(Context.Context, Model);
}
- /// Construct a Z3Expr from a n-ary binary operator.
- static Z3Expr fromNBinOp(const BinaryOperator::Opcode Op,
- const std::vector<Z3_ast> &ASTs) {
- Z3_ast AST;
+ void print(raw_ostream &OS) const {
+ OS << Z3_model_to_string(Context.Context, Model);
+ }
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
+ LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
+}; // end class Z3Model
- case BO_LAnd:
- AST = Z3_mk_and(Z3Context::ZC, ASTs.size(), ASTs.data());
- break;
+/// Get the corresponding IEEE floating-point type for a given bitwidth.
+static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) {
+ switch (BitWidth) {
+ default:
+ llvm_unreachable("Unsupported floating-point semantics!");
+ break;
+ case 16:
+ return llvm::APFloat::IEEEhalf();
+ case 32:
+ return llvm::APFloat::IEEEsingle();
+ case 64:
+ return llvm::APFloat::IEEEdouble();
+ case 128:
+ return llvm::APFloat::IEEEquad();
+ }
+}
- case BO_LOr:
- AST = Z3_mk_or(Z3Context::ZC, ASTs.size(), ASTs.data());
- break;
- }
+// Determine whether two float semantics are equivalent
+static bool areEquivalent(const llvm::fltSemantics &LHS,
+ const llvm::fltSemantics &RHS) {
+ return (llvm::APFloat::semanticsPrecision(LHS) ==
+ llvm::APFloat::semanticsPrecision(RHS)) &&
+ (llvm::APFloat::semanticsMinExponent(LHS) ==
+ llvm::APFloat::semanticsMinExponent(RHS)) &&
+ (llvm::APFloat::semanticsMaxExponent(LHS) ==
+ llvm::APFloat::semanticsMaxExponent(RHS)) &&
+ (llvm::APFloat::semanticsSizeInBits(LHS) ==
+ llvm::APFloat::semanticsSizeInBits(RHS));
+}
- return Z3Expr(AST);
- }
-
- /// Construct a Z3Expr from a binary operator, given a Z3_context.
- static Z3Expr fromBinOp(const Z3Expr &LHS, const BinaryOperator::Opcode Op,
- const Z3Expr &RHS, bool isSigned) {
- Z3_ast AST;
-
- assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) &&
- "AST's must have the same sort!");
-
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
-
- // Multiplicative operators
- case BO_Mul:
- AST = Z3_mk_bvmul(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Div:
- AST = isSigned ? Z3_mk_bvsdiv(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvudiv(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Rem:
- AST = isSigned ? Z3_mk_bvsrem(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvurem(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Additive operators
- case BO_Add:
- AST = Z3_mk_bvadd(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Sub:
- AST = Z3_mk_bvsub(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Bitwise shift operators
- case BO_Shl:
- AST = Z3_mk_bvshl(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Shr:
- AST = isSigned ? Z3_mk_bvashr(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvlshr(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Relational operators
- case BO_LT:
- AST = isSigned ? Z3_mk_bvslt(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvult(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_GT:
- AST = isSigned ? Z3_mk_bvsgt(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvugt(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_LE:
- AST = isSigned ? Z3_mk_bvsle(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvule(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_GE:
- AST = isSigned ? Z3_mk_bvsge(Z3Context::ZC, LHS.AST, RHS.AST)
- : Z3_mk_bvuge(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Equality operators
- case BO_EQ:
- AST = Z3_mk_eq(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_NE:
- return Z3Expr::fromUnOp(UO_LNot,
- Z3Expr::fromBinOp(LHS, BO_EQ, RHS, isSigned));
- break;
-
- // Bitwise operators
- case BO_And:
- AST = Z3_mk_bvand(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Xor:
- AST = Z3_mk_bvxor(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_Or:
- AST = Z3_mk_bvor(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Logical operators
- case BO_LAnd:
- case BO_LOr: {
- std::vector<Z3_ast> Args = {LHS.AST, RHS.AST};
- return Z3Expr::fromNBinOp(Op, Args);
- }
- }
+} // end anonymous namespace
- return Z3Expr(AST);
- }
-
- /// Construct a Z3Expr from a special floating-point binary operator, given
- /// a Z3_context.
- static Z3Expr fromFloatSpecialBinOp(const Z3Expr &LHS,
- const BinaryOperator::Opcode Op,
- const llvm::APFloat::fltCategory &RHS) {
- Z3_ast AST;
-
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
-
- // Equality operators
- case BO_EQ:
- switch (RHS) {
- case llvm::APFloat::fcInfinity:
- AST = Z3_mk_fpa_is_infinite(Z3Context::ZC, LHS.AST);
- break;
- case llvm::APFloat::fcNaN:
- AST = Z3_mk_fpa_is_nan(Z3Context::ZC, LHS.AST);
- break;
- case llvm::APFloat::fcNormal:
- AST = Z3_mk_fpa_is_normal(Z3Context::ZC, LHS.AST);
- break;
- case llvm::APFloat::fcZero:
- AST = Z3_mk_fpa_is_zero(Z3Context::ZC, LHS.AST);
- break;
- }
- break;
- case BO_NE:
- return Z3Expr::fromFloatUnOp(
- UO_LNot, Z3Expr::fromFloatSpecialBinOp(LHS, BO_EQ, RHS));
- break;
- }
+typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty)
- return Z3Expr(AST);
- }
+namespace {
- /// Construct a Z3Expr from a floating-point binary operator, given a
- /// Z3_context.
- static Z3Expr fromFloatBinOp(const Z3Expr &LHS,
- const BinaryOperator::Opcode Op,
- const Z3Expr &RHS) {
- Z3_ast AST;
+class Z3Solver : public SMTSolver {
+ friend class Z3ConstraintManager;
- assert(Z3Sort::getSort(LHS.AST) == Z3Sort::getSort(RHS.AST) &&
- "AST's must have the same sort!");
+ Z3Context Context;
- switch (Op) {
- default:
- llvm_unreachable("Unimplemented opcode");
- break;
+ Z3_solver Solver;
- // Multiplicative operators
- case BO_Mul: {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- AST = Z3_mk_fpa_mul(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
- break;
- }
- case BO_Div: {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- AST = Z3_mk_fpa_div(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
- break;
- }
- case BO_Rem:
- AST = Z3_mk_fpa_rem(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Additive operators
- case BO_Add: {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- AST = Z3_mk_fpa_add(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
- break;
- }
- case BO_Sub: {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- AST = Z3_mk_fpa_sub(Z3Context::ZC, RoundingMode.AST, LHS.AST, RHS.AST);
- break;
- }
+public:
+ Z3Solver() : SMTSolver(), Solver(Z3_mk_simple_solver(Context.Context)) {
+ Z3_solver_inc_ref(Context.Context, Solver);
+ }
+
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Solver(const Z3Solver &Copy)
+ : SMTSolver(), Context(Copy.Context), Solver(Copy.Solver) {
+ Z3_solver_inc_ref(Context.Context, Solver);
+ }
+
+ /// Provide move constructor
+ Z3Solver(Z3Solver &&Move)
+ : SMTSolver(), Context(Move.Context), Solver(nullptr) {
+ *this = std::move(Move);
+ }
- // Relational operators
- case BO_LT:
- AST = Z3_mk_fpa_lt(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_GT:
- AST = Z3_mk_fpa_gt(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_LE:
- AST = Z3_mk_fpa_leq(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_GE:
- AST = Z3_mk_fpa_geq(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
-
- // Equality operators
- case BO_EQ:
- AST = Z3_mk_fpa_eq(Z3Context::ZC, LHS.AST, RHS.AST);
- break;
- case BO_NE:
- return Z3Expr::fromFloatUnOp(UO_LNot,
- Z3Expr::fromFloatBinOp(LHS, BO_EQ, RHS));
- break;
-
- // Logical operators
- case BO_LAnd:
- case BO_LOr:
- return Z3Expr::fromBinOp(LHS, Op, RHS, false);
+ /// Provide move assignment constructor
+ Z3Solver &operator=(Z3Solver &&Move) {
+ if (this != &Move) {
+ if (Solver)
+ Z3_solver_dec_ref(Context.Context, Solver);
+ Solver = Move.Solver;
+ Move.Solver = nullptr;
}
+ return *this;
+ }
- return Z3Expr(AST);
+ ~Z3Solver() {
+ if (Solver)
+ Z3_solver_dec_ref(Context.Context, Solver);
}
- /// Construct a Z3Expr from a SymbolData, given a Z3_context.
- static Z3Expr fromData(const SymbolID ID, bool isBool, bool isFloat,
- uint64_t BitWidth) {
- llvm::Twine Name = "$" + llvm::Twine(ID);
+ void addConstraint(const SMTExprRef &Exp) const override {
+ Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST);
+ }
- Z3Sort Sort;
- if (isBool)
- Sort = Z3Sort::getBoolSort();
- else if (isFloat)
- Sort = Z3Sort::getFloatSort(BitWidth);
- else
- Sort = Z3Sort::getBitvectorSort(BitWidth);
-
- Z3_symbol Symbol = Z3_mk_string_symbol(Z3Context::ZC, Name.str().c_str());
- Z3_ast AST = Z3_mk_const(Z3Context::ZC, Symbol, Sort.Sort);
- return Z3Expr(AST);
- }
-
- /// Construct a Z3Expr from a SymbolCast, given a Z3_context.
- static Z3Expr fromCast(const Z3Expr &Exp, QualType ToTy, uint64_t ToBitWidth,
- QualType FromTy, uint64_t FromBitWidth) {
- Z3_ast AST;
-
- if ((FromTy->isIntegralOrEnumerationType() &&
- ToTy->isIntegralOrEnumerationType()) ||
- (FromTy->isAnyPointerType() ^ ToTy->isAnyPointerType()) ||
- (FromTy->isBlockPointerType() ^ ToTy->isBlockPointerType()) ||
- (FromTy->isReferenceType() ^ ToTy->isReferenceType())) {
- // Special case: Z3 boolean type is distinct from bitvector type, so
- // must use if-then-else expression instead of direct cast
- if (FromTy->isBooleanType()) {
- assert(ToBitWidth > 0 && "BitWidth must be positive!");
- Z3Expr Zero = Z3Expr::fromInt("0", ToBitWidth);
- Z3Expr One = Z3Expr::fromInt("1", ToBitWidth);
- AST = Z3_mk_ite(Z3Context::ZC, Exp.AST, One.AST, Zero.AST);
- } else if (ToBitWidth > FromBitWidth) {
- AST = FromTy->isSignedIntegerOrEnumerationType()
- ? Z3_mk_sign_ext(Z3Context::ZC, ToBitWidth - FromBitWidth,
- Exp.AST)
- : Z3_mk_zero_ext(Z3Context::ZC, ToBitWidth - FromBitWidth,
- Exp.AST);
- } else if (ToBitWidth < FromBitWidth) {
- AST = Z3_mk_extract(Z3Context::ZC, ToBitWidth - 1, 0, Exp.AST);
- } else {
- // Both are bitvectors with the same width, ignore the type cast
- return Exp;
- }
- } else if (FromTy->isRealFloatingType() && ToTy->isRealFloatingType()) {
- if (ToBitWidth != FromBitWidth) {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth);
- AST = Z3_mk_fpa_to_fp_float(Z3Context::ZC, RoundingMode.AST, Exp.AST,
- Sort.Sort);
- } else {
- return Exp;
- }
- } else if (FromTy->isIntegralOrEnumerationType() &&
- ToTy->isRealFloatingType()) {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- Z3Sort Sort = Z3Sort::getFloatSort(ToBitWidth);
- AST = FromTy->isSignedIntegerOrEnumerationType()
- ? Z3_mk_fpa_to_fp_signed(Z3Context::ZC, RoundingMode.AST,
- Exp.AST, Sort.Sort)
- : Z3_mk_fpa_to_fp_unsigned(Z3Context::ZC, RoundingMode.AST,
- Exp.AST, Sort.Sort);
- } else if (FromTy->isRealFloatingType() &&
- ToTy->isIntegralOrEnumerationType()) {
- Z3Expr RoundingMode = Z3Expr::getFloatRoundingMode();
- AST = ToTy->isSignedIntegerOrEnumerationType()
- ? Z3_mk_fpa_to_sbv(Z3Context::ZC, RoundingMode.AST, Exp.AST,
- ToBitWidth)
- : Z3_mk_fpa_to_ubv(Z3Context::ZC, RoundingMode.AST, Exp.AST,
- ToBitWidth);
- } else {
- llvm_unreachable("Unsupported explicit type cast!");
- }
+ SMTSortRef getBoolSort() override {
+ return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context));
+ }
- return Z3Expr(AST);
+ SMTSortRef getBitvectorSort(unsigned BitWidth) override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_bv_sort(Context.Context, BitWidth));
}
- /// Construct a Z3Expr from a boolean, given a Z3_context.
- static Z3Expr fromBoolean(const bool Bool) {
- Z3_ast AST = Bool ? Z3_mk_true(Z3Context::ZC) : Z3_mk_false(Z3Context::ZC);
- return Z3Expr(AST);
+ SMTSortRef getSort(const SMTExprRef &Exp) override {
+ return std::make_shared<Z3Sort>(
+ Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST));
}
- /// Construct a Z3Expr from a finite APFloat, given a Z3_context.
- static Z3Expr fromAPFloat(const llvm::APFloat &Float) {
- Z3_ast AST;
- Z3Sort Sort = Z3Sort::getFloatSort(
- llvm::APFloat::semanticsSizeInBits(Float.getSemantics()));
+ SMTSortRef getFloat16Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_16(Context.Context));
+ }
- llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), true);
- Z3Expr Z3Int = Z3Expr::fromAPSInt(Int);
- AST = Z3_mk_fpa_to_fp_bv(Z3Context::ZC, Z3Int.AST, Sort.Sort);
+ SMTSortRef getFloat32Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_32(Context.Context));
+ }
- return Z3Expr(AST);
+ SMTSortRef getFloat64Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_64(Context.Context));
}
- /// Construct a Z3Expr from an APSInt, given a Z3_context.
- static Z3Expr fromAPSInt(const llvm::APSInt &Int) {
- Z3Sort Sort = Z3Sort::getBitvectorSort(Int.getBitWidth());
- Z3_ast AST =
- Z3_mk_numeral(Z3Context::ZC, Int.toString(10).c_str(), Sort.Sort);
- return Z3Expr(AST);
+ SMTSortRef getFloat128Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_128(Context.Context));
}
- /// Construct a Z3Expr from an integer, given a Z3_context.
- static Z3Expr fromInt(const char *Int, uint64_t BitWidth) {
- Z3Sort Sort = Z3Sort::getBitvectorSort(BitWidth);
- Z3_ast AST = Z3_mk_numeral(Z3Context::ZC, Int, Sort.Sort);
- return Z3Expr(AST);
+ SMTExprRef newExprRef(const SMTExpr &E) const override {
+ return std::make_shared<Z3Expr>(toZ3Expr(E));
}
- /// Construct an APFloat from a Z3Expr, given the AST representation
- static bool toAPFloat(const Z3Sort &Sort, const Z3_ast &AST,
- llvm::APFloat &Float, bool useSemantics = true) {
- assert(Sort.getSortKind() == Z3_FLOATING_POINT_SORT &&
- "Unsupported sort to floating-point!");
+ SMTExprRef mkBVNeg(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST)));
+ }
- llvm::APSInt Int(Sort.getFloatSortSize(), true);
- const llvm::fltSemantics &Semantics =
- Z3Expr::getFloatSemantics(Sort.getFloatSortSize());
- Z3Sort BVSort = Z3Sort::getBitvectorSort(Sort.getFloatSortSize());
- if (!Z3Expr::toAPSInt(BVSort, AST, Int, true)) {
- return false;
- }
+ SMTExprRef mkBVNot(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST)));
+ }
- if (useSemantics &&
- !Z3Expr::areEquivalent(Float.getSemantics(), Semantics)) {
- assert(false && "Floating-point types don't match!");
- return false;
- }
+ SMTExprRef mkNot(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST)));
+ }
- Float = llvm::APFloat(Semantics, Int);
- return true;
+ SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Construct an APSInt from a Z3Expr, given the AST representation
- static bool toAPSInt(const Z3Sort &Sort, const Z3_ast &AST, llvm::APSInt &Int,
- bool useSemantics = true) {
- switch (Sort.getSortKind()) {
- default:
- llvm_unreachable("Unsupported sort to integer!");
- case Z3_BV_SORT: {
- if (useSemantics && Int.getBitWidth() != Sort.getBitvectorSortSize()) {
- assert(false && "Bitvector types don't match!");
- return false;
- }
+ SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- uint64_t Value[2];
- // Force cast because Z3 defines __uint64 to be a unsigned long long
- // type, which isn't compatible with a unsigned long type, even if they
- // are the same size.
- Z3_get_numeral_uint64(Z3Context::ZC, AST,
- reinterpret_cast<__uint64 *>(&Value[0]));
- if (Sort.getBitvectorSortSize() <= 64) {
- Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value[0]), true);
- } else if (Sort.getBitvectorSortSize() == 128) {
- Z3Expr ASTHigh = Z3Expr(Z3_mk_extract(Z3Context::ZC, 127, 64, AST));
- Z3_get_numeral_uint64(Z3Context::ZC, AST,
- reinterpret_cast<__uint64 *>(&Value[1]));
- Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), Value), true);
- } else {
- assert(false && "Bitwidth not supported!");
- return false;
- }
- return true;
- }
- case Z3_BOOL_SORT:
- if (useSemantics && Int.getBitWidth() < 1) {
- assert(false && "Boolean type doesn't match!");
- return false;
- }
- Int = llvm::APSInt(
- llvm::APInt(Int.getBitWidth(),
- Z3_get_bool_value(Z3Context::ZC, AST) == Z3_L_TRUE ? 1
- : 0),
- true);
- return true;
- }
+ SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(Z3_get_ast_hash(Z3Context::ZC, AST));
+ SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsrem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- bool operator<(const Z3Expr &Other) const {
- llvm::FoldingSetNodeID ID1, ID2;
- Profile(ID1);
- Other.Profile(ID2);
- return ID1 < ID2;
+ SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Comparison of AST equality, not model equivalence.
- bool operator==(const Z3Expr &Other) const {
- assert(Z3_is_eq_sort(Z3Context::ZC, Z3_get_sort(Z3Context::ZC, AST),
- Z3_get_sort(Z3Context::ZC, Other.AST)) &&
- "AST's must have the same sort");
- return Z3_is_eq_ast(Z3Context::ZC, AST, Other.AST);
+ SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsdiv(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Override implicit move constructor for correct reference counting.
- Z3Expr &operator=(const Z3Expr &Move) {
- Z3_inc_ref(Z3Context::ZC, Move.AST);
- Z3_dec_ref(Z3Context::ZC, AST);
- AST = Move.AST;
- return *this;
+ SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- void print(raw_ostream &OS) const {
- OS << Z3_ast_to_string(Z3Context::ZC, AST);
+ SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvshl(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
-}; // end class Z3Expr
+ SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-class Z3Model {
- Z3_model Model;
+ SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-public:
- Z3Model(Z3_model ZM) : Model(ZM) { Z3_model_inc_ref(Z3Context::ZC, Model); }
+ SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- /// Override implicit copy constructor for correct reference counting.
- Z3Model(const Z3Model &Copy) : Model(Copy.Model) {
- Z3_model_inc_ref(Z3Context::ZC, Model);
+ SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Provide move constructor
- Z3Model(Z3Model &&Move) : Model(nullptr) { *this = std::move(Move); }
+ SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- /// Provide move assignment constructor
- Z3Model &operator=(Z3Model &&Move) {
- if (this != &Move) {
- if (Model)
- Z3_model_dec_ref(Z3Context::ZC, Model);
- Model = Move.Model;
- Move.Model = nullptr;
- }
- return *this;
+ SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- ~Z3Model() {
- if (Model)
- Z3_model_dec_ref(Z3Context::ZC, Model);
+ SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Given an expression, extract the value of this operand in the model.
- bool getInterpretation(const Z3Expr &Exp, llvm::APSInt &Int) const {
- Z3_func_decl Func =
- Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST));
- if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE)
- return false;
+ SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func);
- Z3Sort Sort = Z3Sort::getSort(Assign);
- return Z3Expr::toAPSInt(Sort, Assign, Int, true);
+ SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Given an expression, extract the value of this operand in the model.
- bool getInterpretation(const Z3Expr &Exp, llvm::APFloat &Float) const {
- Z3_func_decl Func =
- Z3_get_app_decl(Z3Context::ZC, Z3_to_app(Z3Context::ZC, Exp.AST));
- if (Z3_model_has_interp(Z3Context::ZC, Model, Func) != Z3_L_TRUE)
- return false;
+ SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- Z3_ast Assign = Z3_model_get_const_interp(Z3Context::ZC, Model, Func);
- Z3Sort Sort = Z3Sort::getSort(Assign);
- return Z3Expr::toAPFloat(Sort, Assign, Float, true);
+ SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- void print(raw_ostream &OS) const {
- OS << Z3_model_to_string(Z3Context::ZC, Model);
+ SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
-}; // end class Z3Model
+ SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-class Z3Solver {
- friend class Z3ConstraintManager;
+ SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST};
+ return newExprRef(Z3Expr(Context, Z3_mk_and(Context.Context, 2, Args)));
+ }
- Z3_solver Solver;
+ SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST};
+ return newExprRef(Z3Expr(Context, Z3_mk_or(Context.Context, 2, Args)));
+ }
- Z3Solver(Z3_solver ZS) : Solver(ZS) {
- Z3_solver_inc_ref(Z3Context::ZC, Solver);
+ SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
-public:
- /// Override implicit copy constructor for correct reference counting.
- Z3Solver(const Z3Solver &Copy) : Solver(Copy.Solver) {
- Z3_solver_inc_ref(Z3Context::ZC, Solver);
+ SMTExprRef mkFPNeg(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST)));
}
- /// Provide move constructor
- Z3Solver(Z3Solver &&Move) : Solver(nullptr) { *this = std::move(Move); }
+ SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST)));
+ }
- /// Provide move assignment constructor
- Z3Solver &operator=(Z3Solver &&Move) {
- if (this != &Move) {
- if (Solver)
- Z3_solver_dec_ref(Z3Context::ZC, Solver);
- Solver = Move.Solver;
- Move.Solver = nullptr;
- }
- return *this;
+ SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST)));
}
- ~Z3Solver() {
- if (Solver)
- Z3_solver_dec_ref(Z3Context::ZC, Solver);
+ SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST)));
}
- /// Given a constraint, add it to the solver
- void addConstraint(const Z3Expr &Exp) {
- Z3_solver_assert(Z3Context::ZC, Solver, Exp.AST);
+ SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST)));
}
- /// Given a program state, construct the logical conjunction and add it to
- /// the solver
- void addStateConstraints(ProgramStateRef State) {
- // TODO: Don't add all the constraints, only the relevant ones
- ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
- ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end();
+ SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
- // Construct the logical AND of all the constraints
- if (I != IE) {
- std::vector<Z3_ast> ASTs;
+ SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
- while (I != IE)
- ASTs.push_back(I++->second.AST);
+ SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_rem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- Z3Expr Conj = Z3Expr::fromNBinOp(BO_LAnd, ASTs);
- addConstraint(Conj);
- }
+ SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
}
- /// Check if the constraints are satisfiable
- Z3_lbool check() { return Z3_solver_check(Z3Context::ZC, Solver); }
+ SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
- /// Push the current solver state
- void push() { return Z3_solver_push(Z3Context::ZC, Solver); }
+ SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_lt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
- /// Pop the previous solver state
- void pop(unsigned NumStates = 1) {
- assert(Z3_solver_get_num_scopes(Z3Context::ZC, Solver) >= NumStates);
- return Z3_solver_pop(Z3Context::ZC, Solver, NumStates);
+ SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_gt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Get a model from the solver. Caller should check the model is
- /// satisfiable.
- Z3Model getModel() {
- return Z3Model(Z3_solver_get_model(Z3Context::ZC, Solver));
+ SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_leq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
}
- /// Reset the solver and remove all constraints.
- void reset() { Z3_solver_reset(Z3Context::ZC, Solver); }
-}; // end class Z3Solver
+ SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_geq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) {
- llvm::report_fatal_error("Z3 error: " +
- llvm::Twine(Z3_get_error_msg_ex(Context, Error)));
-}
+ SMTExprRef mkFPEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_eq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-class Z3ConstraintManager : public SimpleConstraintManager {
- Z3Context Context;
- mutable Z3Solver Solver;
+ SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T,
+ const SMTExprRef &F) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_ite(Context.Context, toZ3Expr(*Cond).AST,
+ toZ3Expr(*T).AST, toZ3Expr(*F).AST)));
+ }
-public:
- Z3ConstraintManager(SubEngine *SE, SValBuilder &SB)
- : SimpleConstraintManager(SE, SB),
- Solver(Z3_mk_simple_solver(Z3Context::ZC)) {
- Z3_set_error_handler(Z3Context::ZC, Z3ErrorHandler);
+ SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST)));
}
- //===------------------------------------------------------------------===//
- // Implementation for interface from ConstraintManager.
- //===------------------------------------------------------------------===//
+ SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST)));
+ }
- bool canReasonAbout(SVal X) const override;
+ SMTExprRef mkBVExtract(unsigned High, unsigned Low,
+ const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low,
+ toZ3Expr(*Exp).AST)));
+ }
- ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
-
- const llvm::APSInt *getSymVal(ProgramStateRef State,
- SymbolRef Sym) const override;
-
- ProgramStateRef removeDeadBindings(ProgramStateRef St,
- SymbolReaper &SymReaper) override;
-
- void print(ProgramStateRef St, raw_ostream &Out, const char *nl,
- const char *sep) override;
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from SimpleConstraintManager.
- //===------------------------------------------------------------------===//
-
- ProgramStateRef assumeSym(ProgramStateRef state, SymbolRef Sym,
- bool Assumption) override;
-
- ProgramStateRef assumeSymInclusiveRange(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &From,
- const llvm::APSInt &To,
- bool InRange) override;
-
- ProgramStateRef assumeSymUnsupported(ProgramStateRef State, SymbolRef Sym,
- bool Assumption) override;
-
-private:
- //===------------------------------------------------------------------===//
- // Internal implementation.
- //===------------------------------------------------------------------===//
-
- // Check whether a new model is satisfiable, and update the program state.
- ProgramStateRef assumeZ3Expr(ProgramStateRef State, SymbolRef Sym,
- const Z3Expr &Exp);
-
- // Generate and check a Z3 model, using the given constraint.
- Z3_lbool checkZ3Model(ProgramStateRef State, const Z3Expr &Exp) const;
-
- // Generate a Z3Expr that represents the given symbolic expression.
- // Sets the hasComparison parameter if the expression has a comparison
- // operator.
- // Sets the RetTy parameter to the final return type after promotions and
- // casts.
- Z3Expr getZ3Expr(SymbolRef Sym, QualType *RetTy = nullptr,
- bool *hasComparison = nullptr) const;
-
- // Generate a Z3Expr that takes the logical not of an expression.
- Z3Expr getZ3NotExpr(const Z3Expr &Exp) const;
-
- // Generate a Z3Expr that compares the expression to zero.
- Z3Expr getZ3ZeroExpr(const Z3Expr &Exp, QualType RetTy,
- bool Assumption) const;
-
- // Recursive implementation to unpack and generate symbolic expression.
- // Sets the hasComparison and RetTy parameters. See getZ3Expr().
- Z3Expr getZ3SymExpr(SymbolRef Sym, QualType *RetTy,
- bool *hasComparison) const;
-
- // Wrapper to generate Z3Expr from SymbolData.
- Z3Expr getZ3DataExpr(const SymbolID ID, QualType Ty) const;
-
- // Wrapper to generate Z3Expr from SymbolCast.
- Z3Expr getZ3CastExpr(const Z3Expr &Exp, QualType FromTy, QualType Ty) const;
-
- // Wrapper to generate Z3Expr from BinarySymExpr.
- // Sets the hasComparison and RetTy parameters. See getZ3Expr().
- Z3Expr getZ3SymBinExpr(const BinarySymExpr *BSE, bool *hasComparison,
- QualType *RetTy) const;
-
- // Wrapper to generate Z3Expr from unpacked binary symbolic expression.
- // Sets the RetTy parameter. See getZ3Expr().
- Z3Expr getZ3BinExpr(const Z3Expr &LHS, QualType LTy,
- BinaryOperator::Opcode Op, const Z3Expr &RHS,
- QualType RTy, QualType *RetTy) const;
-
- //===------------------------------------------------------------------===//
- // Helper functions.
- //===------------------------------------------------------------------===//
-
- // Recover the QualType of an APSInt.
- // TODO: Refactor to put elsewhere
- QualType getAPSIntType(const llvm::APSInt &Int) const;
-
- // Perform implicit type conversion on binary symbolic expressions.
- // May modify all input parameters.
- // TODO: Refactor to use built-in conversion functions
- void doTypeConversion(Z3Expr &LHS, Z3Expr &RHS, QualType &LTy,
- QualType &RTy) const;
-
- // Perform implicit integer type conversion.
- // May modify all input parameters.
- // TODO: Refactor to use Sema::handleIntegerConversion()
- template <typename T,
- T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
- void doIntTypeConversion(T &LHS, QualType &LTy, T &RHS, QualType &RTy) const;
-
- // Perform implicit floating-point type conversion.
- // May modify all input parameters.
- // TODO: Refactor to use Sema::handleFloatConversion()
- template <typename T,
- T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
- void doFloatTypeConversion(T &LHS, QualType &LTy, T &RHS,
- QualType &RTy) const;
-
- // Callback function for doCast parameter on APSInt type.
- static llvm::APSInt castAPSInt(const llvm::APSInt &V, QualType ToTy,
- uint64_t ToWidth, QualType FromTy,
- uint64_t FromWidth);
-}; // end class Z3ConstraintManager
+ SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
-Z3_context Z3Context::ZC;
+ SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_float(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
-} // end anonymous namespace
+ SMTExprRef mkFPtoSBV(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_signed(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
-ProgramStateRef Z3ConstraintManager::assumeSym(ProgramStateRef State,
- SymbolRef Sym, bool Assumption) {
- QualType RetTy;
- bool hasComparison;
+ SMTExprRef mkFPtoUBV(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_unsigned(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
- Z3Expr Exp = getZ3Expr(Sym, &RetTy, &hasComparison);
- // Create zero comparison for implicit boolean cast, with reversed assumption
- if (!hasComparison && !RetTy->isBooleanType())
- return assumeZ3Expr(State, Sym, getZ3ZeroExpr(Exp, RetTy, !Assumption));
+ SMTExprRef mkSBVtoFP(const SMTExprRef &From, unsigned ToWidth) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, ToWidth)));
+ }
- return assumeZ3Expr(State, Sym, Assumption ? Exp : getZ3NotExpr(Exp));
-}
+ SMTExprRef mkUBVtoFP(const SMTExprRef &From, unsigned ToWidth) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, ToWidth)));
+ }
-ProgramStateRef Z3ConstraintManager::assumeSymInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, bool InRange) {
- QualType RetTy;
- // The expression may be casted, so we cannot call getZ3DataExpr() directly
- Z3Expr Exp = getZ3Expr(Sym, &RetTy);
-
- assert((getAPSIntType(From) == getAPSIntType(To)) &&
- "Range values have different types!");
- QualType RTy = getAPSIntType(From);
- bool isSignedTy = RetTy->isSignedIntegerOrEnumerationType();
- Z3Expr FromExp = Z3Expr::fromAPSInt(From);
- Z3Expr ToExp = Z3Expr::fromAPSInt(To);
-
- // Construct single (in)equality
- if (From == To)
- return assumeZ3Expr(State, Sym,
- getZ3BinExpr(Exp, RetTy, InRange ? BO_EQ : BO_NE,
- FromExp, RTy, nullptr));
-
- // Construct two (in)equalities, and a logical and/or
- Z3Expr LHS =
- getZ3BinExpr(Exp, RetTy, InRange ? BO_GE : BO_LT, FromExp, RTy, nullptr);
- Z3Expr RHS =
- getZ3BinExpr(Exp, RetTy, InRange ? BO_LE : BO_GT, ToExp, RTy, nullptr);
- return assumeZ3Expr(
- State, Sym,
- Z3Expr::fromBinOp(LHS, InRange ? BO_LAnd : BO_LOr, RHS, isSignedTy));
-}
+ SMTExprRef mkBoolean(const bool b) override {
+ return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context)
+ : Z3_mk_false(Context.Context)));
+ }
-ProgramStateRef Z3ConstraintManager::assumeSymUnsupported(ProgramStateRef State,
- SymbolRef Sym,
- bool Assumption) {
- // Skip anything that is unsupported
- return State;
-}
+ SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override {
+ const SMTSortRef Sort = getBitvectorSort(BitWidth);
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(),
+ toZ3Sort(*Sort).Sort)));
+ }
-bool Z3ConstraintManager::canReasonAbout(SVal X) const {
- const TargetInfo &TI = getBasicVals().getContext().getTargetInfo();
+ SMTExprRef mkFloat(const llvm::APFloat Float) override {
+ SMTSortRef Sort =
+ getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics()));
- Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
- if (!SymVal)
- return true;
+ llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false);
+ SMTExprRef Z3Int = mkBitvector(Int, Int.getBitWidth());
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST,
+ toZ3Sort(*Sort).Sort)));
+ }
- const SymExpr *Sym = SymVal->getSymbol();
- do {
- QualType Ty = Sym->getType();
+ SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_const(Context.Context,
+ Z3_mk_string_symbol(Context.Context, Name),
+ toZ3Sort(*Sort).Sort)));
+ }
- // Complex types are not modeled
- if (Ty->isComplexType() || Ty->isComplexIntegerType())
- return false;
+ llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth,
+ bool isUnsigned) override {
+ return llvm::APSInt(llvm::APInt(
+ BitWidth, Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST),
+ 10));
+ }
- // Non-IEEE 754 floating-point types are not modeled
- if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
- (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
- &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
- return false;
+ bool getBoolean(const SMTExprRef &Exp) override {
+ return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE;
+ }
- if (isa<SymbolData>(Sym)) {
- break;
- } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
- Sym = SC->getOperand();
- } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
- if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
- Sym = SIE->getLHS();
- } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
- Sym = ISE->getRHS();
- } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
- return canReasonAbout(nonloc::SymbolVal(SSM->getLHS())) &&
- canReasonAbout(nonloc::SymbolVal(SSM->getRHS()));
- } else {
- llvm_unreachable("Unsupported binary expression to reason about!");
- }
- } else {
- llvm_unreachable("Unsupported expression to reason about!");
- }
- } while (Sym);
+ SMTExprRef getFloatRoundingMode() override {
+ // TODO: Don't assume nearest ties to even rounding mode
+ return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context)));
+ }
- return true;
-}
+ SMTExprRef fromData(const SymbolID ID, const QualType &Ty,
+ uint64_t BitWidth) override {
+ llvm::Twine Name = "$" + llvm::Twine(ID);
+ return mkSymbol(Name.str().c_str(), mkSort(Ty, BitWidth));
+ }
+
+ SMTExprRef fromBoolean(const bool Bool) override {
+ Z3_ast AST =
+ Bool ? Z3_mk_true(Context.Context) : Z3_mk_false(Context.Context);
+ return newExprRef(Z3Expr(Context, AST));
+ }
+
+ SMTExprRef fromAPFloat(const llvm::APFloat &Float) override {
+ SMTSortRef Sort =
+ getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics()));
-ConditionTruthVal Z3ConstraintManager::checkNull(ProgramStateRef State,
- SymbolRef Sym) {
- QualType RetTy;
- // The expression may be casted, so we cannot call getZ3DataExpr() directly
- Z3Expr VarExp = getZ3Expr(Sym, &RetTy);
- Z3Expr Exp = getZ3ZeroExpr(VarExp, RetTy, true);
- // Negate the constraint
- Z3Expr NotExp = getZ3ZeroExpr(VarExp, RetTy, false);
+ llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false);
+ SMTExprRef Z3Int = fromAPSInt(Int);
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST,
+ toZ3Sort(*Sort).Sort)));
+ }
- Solver.reset();
- Solver.addStateConstraints(State);
+ SMTExprRef fromAPSInt(const llvm::APSInt &Int) override {
+ SMTSortRef Sort = getBitvectorSort(Int.getBitWidth());
+ Z3_ast AST = Z3_mk_numeral(Context.Context, Int.toString(10).c_str(),
+ toZ3Sort(*Sort).Sort);
+ return newExprRef(Z3Expr(Context, AST));
+ }
- Solver.push();
- Solver.addConstraint(Exp);
- Z3_lbool isSat = Solver.check();
+ SMTExprRef fromInt(const char *Int, uint64_t BitWidth) override {
+ SMTSortRef Sort = getBitvectorSort(BitWidth);
+ Z3_ast AST = Z3_mk_numeral(Context.Context, Int, toZ3Sort(*Sort).Sort);
+ return newExprRef(Z3Expr(Context, AST));
+ }
- Solver.pop();
- Solver.addConstraint(NotExp);
- Z3_lbool isNotSat = Solver.check();
+ bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST,
+ llvm::APFloat &Float, bool useSemantics) {
+ assert(Sort->isFloatSort() && "Unsupported sort to floating-point!");
- // Zero is the only possible solution
- if (isSat == Z3_L_TRUE && isNotSat == Z3_L_FALSE)
+ llvm::APSInt Int(Sort->getFloatSortSize(), true);
+ const llvm::fltSemantics &Semantics =
+ getFloatSemantics(Sort->getFloatSortSize());
+ SMTSortRef BVSort = getBitvectorSort(Sort->getFloatSortSize());
+ if (!toAPSInt(BVSort, AST, Int, true)) {
+ return false;
+ }
+
+ if (useSemantics && !areEquivalent(Float.getSemantics(), Semantics)) {
+ assert(false && "Floating-point types don't match!");
+ return false;
+ }
+
+ Float = llvm::APFloat(Semantics, Int);
return true;
- // Zero is not a solution
- else if (isSat == Z3_L_FALSE && isNotSat == Z3_L_TRUE)
- return false;
+ }
- // Zero may be a solution
- return ConditionTruthVal();
-}
+ bool toAPSInt(const SMTSortRef &Sort, const SMTExprRef &AST,
+ llvm::APSInt &Int, bool useSemantics) {
+ if (Sort->isBitvectorSort()) {
+ if (useSemantics && Int.getBitWidth() != Sort->getBitvectorSortSize()) {
+ assert(false && "Bitvector types don't match!");
+ return false;
+ }
-const llvm::APSInt *Z3ConstraintManager::getSymVal(ProgramStateRef State,
- SymbolRef Sym) const {
- BasicValueFactory &BV = getBasicVals();
- ASTContext &Ctx = BV.getContext();
+ // FIXME: This function is also used to retrieve floating-point values,
+ // which can be 16, 32, 64 or 128 bits long. Bitvectors can be anything
+ // between 1 and 64 bits long, which is the reason we have this weird
+ // guard. In the future, we need proper calls in the backend to retrieve
+ // floating-points and its special values (NaN, +/-infinity, +/-zero),
+ // then we can drop this weird condition.
+ if (Sort->getBitvectorSortSize() <= 64 ||
+ Sort->getBitvectorSortSize() == 128) {
+ Int = getBitvector(AST, Int.getBitWidth(), Int.isUnsigned());
+ return true;
+ }
- if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
- QualType Ty = Sym->getType();
- assert(!Ty->isRealFloatingType());
- llvm::APSInt Value(Ctx.getTypeSize(Ty),
- !Ty->isSignedIntegerOrEnumerationType());
-
- Z3Expr Exp = getZ3DataExpr(SD->getSymbolID(), Ty);
-
- Solver.reset();
- Solver.addStateConstraints(State);
-
- // Constraints are unsatisfiable
- if (Solver.check() != Z3_L_TRUE)
- return nullptr;
-
- Z3Model Model = Solver.getModel();
- // Model does not assign interpretation
- if (!Model.getInterpretation(Exp, Value))
- return nullptr;
-
- // A value has been obtained, check if it is the only value
- Z3Expr NotExp = Z3Expr::fromBinOp(
- Exp, BO_NE,
- Ty->isBooleanType() ? Z3Expr::fromBoolean(Value.getBoolValue())
- : Z3Expr::fromAPSInt(Value),
- false);
-
- Solver.addConstraint(NotExp);
- if (Solver.check() == Z3_L_TRUE)
- return nullptr;
-
- // This is the only solution, store it
- return &BV.getValue(Value);
- } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
- SymbolRef CastSym = SC->getOperand();
- QualType CastTy = SC->getType();
- // Skip the void type
- if (CastTy->isVoidType())
- return nullptr;
-
- const llvm::APSInt *Value;
- if (!(Value = getSymVal(State, CastSym)))
- return nullptr;
- return &BV.Convert(SC->getType(), *Value);
- } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
- const llvm::APSInt *LHS, *RHS;
- if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
- LHS = getSymVal(State, SIE->getLHS());
- RHS = &SIE->getRHS();
- } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
- LHS = &ISE->getLHS();
- RHS = getSymVal(State, ISE->getRHS());
- } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
- // Early termination to avoid expensive call
- LHS = getSymVal(State, SSM->getLHS());
- RHS = LHS ? getSymVal(State, SSM->getRHS()) : nullptr;
- } else {
- llvm_unreachable("Unsupported binary expression to get symbol value!");
+ assert(false && "Bitwidth not supported!");
+ return false;
}
- if (!LHS || !RHS)
- return nullptr;
+ if (Sort->isBooleanSort()) {
+ if (useSemantics && Int.getBitWidth() < 1) {
+ assert(false && "Boolean type doesn't match!");
+ return false;
+ }
- llvm::APSInt ConvertedLHS = *LHS, ConvertedRHS = *RHS;
- QualType LTy = getAPSIntType(*LHS), RTy = getAPSIntType(*RHS);
- doIntTypeConversion<llvm::APSInt, Z3ConstraintManager::castAPSInt>(
- ConvertedLHS, LTy, ConvertedRHS, RTy);
- return BV.evalAPSInt(BSE->getOpcode(), ConvertedLHS, ConvertedRHS);
- }
+ Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)),
+ Int.isUnsigned());
+ return true;
+ }
- llvm_unreachable("Unsupported expression to get symbol value!");
-}
+ llvm_unreachable("Unsupported sort to integer!");
+ }
-ProgramStateRef
-Z3ConstraintManager::removeDeadBindings(ProgramStateRef State,
- SymbolReaper &SymReaper) {
- ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
- ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>();
+ bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override {
+ Z3Model Model = getModel();
+ Z3_func_decl Func = Z3_get_app_decl(
+ Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST));
+ if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE)
+ return false;
- for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
- if (SymReaper.maybeDead(I->first))
- CZ = CZFactory.remove(CZ, *I);
+ SMTExprRef Assign = newExprRef(
+ Z3Expr(Context,
+ Z3_model_get_const_interp(Context.Context, Model.Model, Func)));
+ SMTSortRef Sort = getSort(Assign);
+ return toAPSInt(Sort, Assign, Int, true);
}
- return State->set<ConstraintZ3>(CZ);
-}
+ bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override {
+ Z3Model Model = getModel();
+ Z3_func_decl Func = Z3_get_app_decl(
+ Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST));
+ if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE)
+ return false;
-//===------------------------------------------------------------------===//
-// Internal implementation.
-//===------------------------------------------------------------------===//
+ SMTExprRef Assign = newExprRef(
+ Z3Expr(Context,
+ Z3_model_get_const_interp(Context.Context, Model.Model, Func)));
+ SMTSortRef Sort = getSort(Assign);
+ return toAPFloat(Sort, Assign, Float, true);
+ }
-ProgramStateRef Z3ConstraintManager::assumeZ3Expr(ProgramStateRef State,
- SymbolRef Sym,
- const Z3Expr &Exp) {
- // Check the model, avoid simplifying AST to save time
- if (checkZ3Model(State, Exp) == Z3_L_TRUE)
- return State->add<ConstraintZ3>(std::make_pair(Sym, Exp));
+ ConditionTruthVal check() const override {
+ Z3_lbool res = Z3_solver_check(Context.Context, Solver);
+ if (res == Z3_L_TRUE)
+ return true;
- return nullptr;
-}
+ if (res == Z3_L_FALSE)
+ return false;
-Z3_lbool Z3ConstraintManager::checkZ3Model(ProgramStateRef State,
- const Z3Expr &Exp) const {
- Solver.reset();
- Solver.addConstraint(Exp);
- Solver.addStateConstraints(State);
- return Solver.check();
-}
+ return ConditionTruthVal();
+ }
-Z3Expr Z3ConstraintManager::getZ3Expr(SymbolRef Sym, QualType *RetTy,
- bool *hasComparison) const {
- if (hasComparison) {
- *hasComparison = false;
+ void push() override { return Z3_solver_push(Context.Context, Solver); }
+
+ void pop(unsigned NumStates = 1) override {
+ assert(Z3_solver_get_num_scopes(Context.Context, Solver) >= NumStates);
+ return Z3_solver_pop(Context.Context, Solver, NumStates);
}
- return getZ3SymExpr(Sym, RetTy, hasComparison);
-}
+ /// Get a model from the solver. Caller should check the model is
+ /// satisfiable.
+ Z3Model getModel() {
+ return Z3Model(Context, Z3_solver_get_model(Context.Context, Solver));
+ }
-Z3Expr Z3ConstraintManager::getZ3NotExpr(const Z3Expr &Exp) const {
- return Z3Expr::fromUnOp(UO_LNot, Exp);
-}
+ /// Reset the solver and remove all constraints.
+ void reset() const override { Z3_solver_reset(Context.Context, Solver); }
-Z3Expr Z3ConstraintManager::getZ3ZeroExpr(const Z3Expr &Exp, QualType Ty,
- bool Assumption) const {
- ASTContext &Ctx = getBasicVals().getContext();
- if (Ty->isRealFloatingType()) {
- llvm::APFloat Zero = llvm::APFloat::getZero(Ctx.getFloatTypeSemantics(Ty));
- return Z3Expr::fromFloatBinOp(Exp, Assumption ? BO_EQ : BO_NE,
- Z3Expr::fromAPFloat(Zero));
- } else if (Ty->isIntegralOrEnumerationType() || Ty->isAnyPointerType() ||
- Ty->isBlockPointerType() || Ty->isReferenceType()) {
- bool isSigned = Ty->isSignedIntegerOrEnumerationType();
- // Skip explicit comparison for boolean types
- if (Ty->isBooleanType())
- return Assumption ? getZ3NotExpr(Exp) : Exp;
- return Z3Expr::fromBinOp(Exp, Assumption ? BO_EQ : BO_NE,
- Z3Expr::fromInt("0", Ctx.getTypeSize(Ty)),
- isSigned);
- }
-
- llvm_unreachable("Unsupported type for zero value!");
-}
+ void print(raw_ostream &OS) const override {
+ OS << Z3_solver_to_string(Context.Context, Solver);
+ }
+}; // end class Z3Solver
-Z3Expr Z3ConstraintManager::getZ3SymExpr(SymbolRef Sym, QualType *RetTy,
- bool *hasComparison) const {
- if (const SymbolData *SD = dyn_cast<SymbolData>(Sym)) {
- if (RetTy)
- *RetTy = Sym->getType();
-
- return getZ3DataExpr(SD->getSymbolID(), Sym->getType());
- } else if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym)) {
- if (RetTy)
- *RetTy = Sym->getType();
-
- QualType FromTy;
- Z3Expr Exp = getZ3SymExpr(SC->getOperand(), &FromTy, hasComparison);
- // Casting an expression with a comparison invalidates it. Note that this
- // must occur after the recursive call above.
- // e.g. (signed char) (x > 0)
- if (hasComparison)
- *hasComparison = false;
- return getZ3CastExpr(Exp, FromTy, Sym->getType());
- } else if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
- Z3Expr Exp = getZ3SymBinExpr(BSE, hasComparison, RetTy);
- // Set the hasComparison parameter, in post-order traversal order.
- if (hasComparison)
- *hasComparison = BinaryOperator::isComparisonOp(BSE->getOpcode());
- return Exp;
- }
-
- llvm_unreachable("Unsupported SymbolRef type!");
-}
+class Z3ConstraintManager : public SMTConstraintManager {
+ SMTSolverRef Solver = CreateZ3Solver();
-Z3Expr Z3ConstraintManager::getZ3DataExpr(const SymbolID ID,
- QualType Ty) const {
- ASTContext &Ctx = getBasicVals().getContext();
- return Z3Expr::fromData(ID, Ty->isBooleanType(), Ty->isRealFloatingType(),
- Ctx.getTypeSize(Ty));
-}
+public:
+ Z3ConstraintManager(SubEngine *SE, SValBuilder &SB)
+ : SMTConstraintManager(SE, SB, Solver) {}
-Z3Expr Z3ConstraintManager::getZ3CastExpr(const Z3Expr &Exp, QualType FromTy,
- QualType ToTy) const {
- ASTContext &Ctx = getBasicVals().getContext();
- return Z3Expr::fromCast(Exp, ToTy, Ctx.getTypeSize(ToTy), FromTy,
- Ctx.getTypeSize(FromTy));
-}
+ void addStateConstraints(ProgramStateRef State) const override {
+ // TODO: Don't add all the constraints, only the relevant ones
+ ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
+ ConstraintZ3Ty::iterator I = CZ.begin(), IE = CZ.end();
-Z3Expr Z3ConstraintManager::getZ3SymBinExpr(const BinarySymExpr *BSE,
- bool *hasComparison,
- QualType *RetTy) const {
- QualType LTy, RTy;
- BinaryOperator::Opcode Op = BSE->getOpcode();
-
- if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE)) {
- RTy = getAPSIntType(SIE->getRHS());
- Z3Expr LHS = getZ3SymExpr(SIE->getLHS(), &LTy, hasComparison);
- Z3Expr RHS = Z3Expr::fromAPSInt(SIE->getRHS());
- return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
- } else if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE)) {
- LTy = getAPSIntType(ISE->getLHS());
- Z3Expr LHS = Z3Expr::fromAPSInt(ISE->getLHS());
- Z3Expr RHS = getZ3SymExpr(ISE->getRHS(), &RTy, hasComparison);
- return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
- } else if (const SymSymExpr *SSM = dyn_cast<SymSymExpr>(BSE)) {
- Z3Expr LHS = getZ3SymExpr(SSM->getLHS(), &LTy, hasComparison);
- Z3Expr RHS = getZ3SymExpr(SSM->getRHS(), &RTy, hasComparison);
- return getZ3BinExpr(LHS, LTy, Op, RHS, RTy, RetTy);
- } else {
- llvm_unreachable("Unsupported BinarySymExpr type!");
- }
-}
+ // Construct the logical AND of all the constraints
+ if (I != IE) {
+ std::vector<SMTExprRef> ASTs;
-Z3Expr Z3ConstraintManager::getZ3BinExpr(const Z3Expr &LHS, QualType LTy,
- BinaryOperator::Opcode Op,
- const Z3Expr &RHS, QualType RTy,
- QualType *RetTy) const {
- Z3Expr NewLHS = LHS;
- Z3Expr NewRHS = RHS;
- doTypeConversion(NewLHS, NewRHS, LTy, RTy);
- // Update the return type parameter if the output type has changed.
- if (RetTy) {
- // A boolean result can be represented as an integer type in C/C++, but at
- // this point we only care about the Z3 type. Set it as a boolean type to
- // avoid subsequent Z3 errors.
- if (BinaryOperator::isComparisonOp(Op) || BinaryOperator::isLogicalOp(Op)) {
- ASTContext &Ctx = getBasicVals().getContext();
- *RetTy = Ctx.BoolTy;
- } else {
- *RetTy = LTy;
- }
+ SMTExprRef Constraint = Solver->newExprRef(I++->second);
+ while (I != IE) {
+ Constraint = Solver->mkAnd(Constraint, Solver->newExprRef(I++->second));
+ }
- // If the two operands are pointers and the operation is a subtraction, the
- // result is of type ptrdiff_t, which is signed
- if (LTy->isAnyPointerType() && LTy == RTy && Op == BO_Sub) {
- ASTContext &Ctx = getBasicVals().getContext();
- *RetTy = Ctx.getIntTypeForBitwidth(Ctx.getTypeSize(LTy), true);
+ Solver->addConstraint(Constraint);
}
}
- return LTy->isRealFloatingType()
- ? Z3Expr::fromFloatBinOp(NewLHS, Op, NewRHS)
- : Z3Expr::fromBinOp(NewLHS, Op, NewRHS,
- LTy->isSignedIntegerOrEnumerationType());
-}
+ bool canReasonAbout(SVal X) const override {
+ const TargetInfo &TI = getBasicVals().getContext().getTargetInfo();
-//===------------------------------------------------------------------===//
-// Helper functions.
-//===------------------------------------------------------------------===//
+ Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ if (!SymVal)
+ return true;
-QualType Z3ConstraintManager::getAPSIntType(const llvm::APSInt &Int) const {
- ASTContext &Ctx = getBasicVals().getContext();
- return Ctx.getIntTypeForBitwidth(Int.getBitWidth(), Int.isSigned());
-}
+ const SymExpr *Sym = SymVal->getSymbol();
+ QualType Ty = Sym->getType();
-void Z3ConstraintManager::doTypeConversion(Z3Expr &LHS, Z3Expr &RHS,
- QualType &LTy, QualType &RTy) const {
- ASTContext &Ctx = getBasicVals().getContext();
-
- // Perform type conversion
- if (LTy->isIntegralOrEnumerationType() &&
- RTy->isIntegralOrEnumerationType()) {
- if (LTy->isArithmeticType() && RTy->isArithmeticType())
- return doIntTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy);
- } else if (LTy->isRealFloatingType() || RTy->isRealFloatingType()) {
- return doFloatTypeConversion<Z3Expr, Z3Expr::fromCast>(LHS, LTy, RHS, RTy);
- } else if ((LTy->isAnyPointerType() || RTy->isAnyPointerType()) ||
- (LTy->isBlockPointerType() || RTy->isBlockPointerType()) ||
- (LTy->isReferenceType() || RTy->isReferenceType())) {
- // TODO: Refactor to Sema::FindCompositePointerType(), and
- // Sema::CheckCompareOperands().
-
- uint64_t LBitWidth = Ctx.getTypeSize(LTy);
- uint64_t RBitWidth = Ctx.getTypeSize(RTy);
-
- // Cast the non-pointer type to the pointer type.
- // TODO: Be more strict about this.
- if ((LTy->isAnyPointerType() ^ RTy->isAnyPointerType()) ||
- (LTy->isBlockPointerType() ^ RTy->isBlockPointerType()) ||
- (LTy->isReferenceType() ^ RTy->isReferenceType())) {
- if (LTy->isNullPtrType() || LTy->isBlockPointerType() ||
- LTy->isReferenceType()) {
- LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
- } else {
- RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- }
- }
+ // Complex types are not modeled
+ if (Ty->isComplexType() || Ty->isComplexIntegerType())
+ return false;
- // Cast the void pointer type to the non-void pointer type.
- // For void types, this assumes that the casted value is equal to the value
- // of the original pointer, and does not account for alignment requirements.
- if (LTy->isVoidPointerType() ^ RTy->isVoidPointerType()) {
- assert((Ctx.getTypeSize(LTy) == Ctx.getTypeSize(RTy)) &&
- "Pointer types have different bitwidths!");
- if (RTy->isVoidPointerType())
- RTy = LTy;
- else
- LTy = RTy;
- }
+ // Non-IEEE 754 floating-point types are not modeled
+ if ((Ty->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ (&TI.getLongDoubleFormat() == &llvm::APFloat::x87DoubleExtended() ||
+ &TI.getLongDoubleFormat() == &llvm::APFloat::PPCDoubleDouble())))
+ return false;
- if (LTy == RTy)
- return;
- }
+ if (isa<SymbolData>(Sym))
+ return true;
- // Fallback: for the solver, assume that these types don't really matter
- if ((LTy.getCanonicalType() == RTy.getCanonicalType()) ||
- (LTy->isObjCObjectPointerType() && RTy->isObjCObjectPointerType())) {
- LTy = RTy;
- return;
- }
+ SValBuilder &SVB = getSValBuilder();
- // TODO: Refine behavior for invalid type casts
-}
+ if (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
+ return canReasonAbout(SVB.makeSymbolVal(SC->getOperand()));
-template <typename T,
- T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
-void Z3ConstraintManager::doIntTypeConversion(T &LHS, QualType &LTy, T &RHS,
- QualType &RTy) const {
- ASTContext &Ctx = getBasicVals().getContext();
-
- uint64_t LBitWidth = Ctx.getTypeSize(LTy);
- uint64_t RBitWidth = Ctx.getTypeSize(RTy);
-
- // Always perform integer promotion before checking type equality.
- // Otherwise, e.g. (bool) a + (bool) b could trigger a backend assertion
- if (LTy->isPromotableIntegerType()) {
- QualType NewTy = Ctx.getPromotedIntegerType(LTy);
- uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
- LHS = (*doCast)(LHS, NewTy, NewBitWidth, LTy, LBitWidth);
- LTy = NewTy;
- LBitWidth = NewBitWidth;
- }
- if (RTy->isPromotableIntegerType()) {
- QualType NewTy = Ctx.getPromotedIntegerType(RTy);
- uint64_t NewBitWidth = Ctx.getTypeSize(NewTy);
- RHS = (*doCast)(RHS, NewTy, NewBitWidth, RTy, RBitWidth);
- RTy = NewTy;
- RBitWidth = NewBitWidth;
- }
-
- if (LTy == RTy)
- return;
-
- // Perform integer type conversion
- // Note: Safe to skip updating bitwidth because this must terminate
- bool isLSignedTy = LTy->isSignedIntegerOrEnumerationType();
- bool isRSignedTy = RTy->isSignedIntegerOrEnumerationType();
-
- int order = Ctx.getIntegerTypeOrder(LTy, RTy);
- if (isLSignedTy == isRSignedTy) {
- // Same signedness; use the higher-ranked type
- if (order == 1) {
- RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- } else {
- LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
- }
- } else if (order != (isLSignedTy ? 1 : -1)) {
- // The unsigned type has greater than or equal rank to the
- // signed type, so use the unsigned type
- if (isRSignedTy) {
- RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- } else {
- LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
- }
- } else if (LBitWidth != RBitWidth) {
- // The two types are different widths; if we are here, that
- // means the signed type is larger than the unsigned type, so
- // use the signed type.
- if (isLSignedTy) {
- RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- } else {
- LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
+ if (const BinarySymExpr *BSE = dyn_cast<BinarySymExpr>(Sym)) {
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(BSE))
+ return canReasonAbout(SVB.makeSymbolVal(SIE->getLHS()));
+
+ if (const IntSymExpr *ISE = dyn_cast<IntSymExpr>(BSE))
+ return canReasonAbout(SVB.makeSymbolVal(ISE->getRHS()));
+
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(BSE))
+ return canReasonAbout(SVB.makeSymbolVal(SSE->getLHS())) &&
+ canReasonAbout(SVB.makeSymbolVal(SSE->getRHS()));
}
- } else {
- // The signed type is higher-ranked than the unsigned type,
- // but isn't actually any bigger (like unsigned int and long
- // on most 32-bit systems). Use the unsigned type corresponding
- // to the signed type.
- QualType NewTy = Ctx.getCorrespondingUnsignedType(isLSignedTy ? LTy : RTy);
- RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = NewTy;
- LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = NewTy;
+
+ llvm_unreachable("Unsupported expression to reason about!");
}
-}
-template <typename T,
- T(doCast)(const T &, QualType, uint64_t, QualType, uint64_t)>
-void Z3ConstraintManager::doFloatTypeConversion(T &LHS, QualType &LTy, T &RHS,
- QualType &RTy) const {
- ASTContext &Ctx = getBasicVals().getContext();
-
- uint64_t LBitWidth = Ctx.getTypeSize(LTy);
- uint64_t RBitWidth = Ctx.getTypeSize(RTy);
-
- // Perform float-point type promotion
- if (!LTy->isRealFloatingType()) {
- LHS = (*doCast)(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
- LBitWidth = RBitWidth;
- }
- if (!RTy->isRealFloatingType()) {
- RHS = (*doCast)(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- RBitWidth = LBitWidth;
- }
-
- if (LTy == RTy)
- return;
-
- // If we have two real floating types, convert the smaller operand to the
- // bigger result
- // Note: Safe to skip updating bitwidth because this must terminate
- int order = Ctx.getFloatingTypeOrder(LTy, RTy);
- if (order > 0) {
- RHS = Z3Expr::fromCast(RHS, LTy, LBitWidth, RTy, RBitWidth);
- RTy = LTy;
- } else if (order == 0) {
- LHS = Z3Expr::fromCast(LHS, RTy, RBitWidth, LTy, LBitWidth);
- LTy = RTy;
- } else {
- llvm_unreachable("Unsupported floating-point type cast!");
+ ProgramStateRef removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) override {
+ ConstraintZ3Ty CZ = State->get<ConstraintZ3>();
+ ConstraintZ3Ty::Factory &CZFactory = State->get_context<ConstraintZ3>();
+
+ for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
+ if (SymReaper.maybeDead(I->first))
+ CZ = CZFactory.remove(CZ, *I);
+ }
+
+ return State->set<ConstraintZ3>(CZ);
}
-}
-llvm::APSInt Z3ConstraintManager::castAPSInt(const llvm::APSInt &V,
- QualType ToTy, uint64_t ToWidth,
- QualType FromTy,
- uint64_t FromWidth) {
- APSIntType TargetType(ToWidth, !ToTy->isSignedIntegerOrEnumerationType());
- return TargetType.convert(V);
-}
+ ProgramStateRef assumeExpr(ProgramStateRef State, SymbolRef Sym,
+ const SMTExprRef &Exp) override {
+ // Check the model, avoid simplifying AST to save time
+ if (checkModel(State, Exp).isConstrainedTrue())
+ return State->add<ConstraintZ3>(std::make_pair(Sym, toZ3Expr(*Exp)));
+
+ return nullptr;
+ }
-//==------------------------------------------------------------------------==/
-// Pretty-printing.
-//==------------------------------------------------------------------------==/
+ //==------------------------------------------------------------------------==/
+ // Pretty-printing.
+ //==------------------------------------------------------------------------==/
-void Z3ConstraintManager::print(ProgramStateRef St, raw_ostream &OS,
- const char *nl, const char *sep) {
+ void print(ProgramStateRef St, raw_ostream &OS, const char *nl,
+ const char *sep) override {
- ConstraintZ3Ty CZ = St->get<ConstraintZ3>();
+ ConstraintZ3Ty CZ = St->get<ConstraintZ3>();
- OS << nl << sep << "Constraints:";
- for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
- OS << nl << ' ' << I->first << " : ";
- I->second.print(OS);
+ OS << nl << sep << "Constraints:";
+ for (ConstraintZ3Ty::iterator I = CZ.begin(), E = CZ.end(); I != E; ++I) {
+ OS << nl << ' ' << I->first << " : ";
+ I->second.print(OS);
+ }
+ OS << nl;
}
- OS << nl;
-}
+}; // end class Z3ConstraintManager
+
+} // end anonymous namespace
#endif
+std::unique_ptr<SMTSolver> clang::ento::CreateZ3Solver() {
+#if CLANG_ANALYZER_WITH_Z3
+ return llvm::make_unique<Z3Solver>();
+#else
+ llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild "
+ "with -DCLANG_ANALYZER_BUILD_Z3=ON",
+ false);
+ return nullptr;
+#endif
+}
+
std::unique_ptr<ConstraintManager>
ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
#if CLANG_ANALYZER_WITH_Z3
return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder());
#else
- llvm::report_fatal_error("Clang was not compiled with Z3 support!", false);
+ llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild "
+ "with -DCLANG_ANALYZER_BUILD_Z3=ON",
+ false);
return nullptr;
#endif
}
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index fccea9ee53bf..44abde5da6d1 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -22,6 +22,7 @@
#include "clang/Analysis/CallGraph.h"
#include "clang/Analysis/CodeInjector.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
@@ -57,6 +58,8 @@ STATISTIC(NumFunctionsAnalyzed,
"with inlining turned on).");
STATISTIC(NumBlocksInAnalyzedFunctions,
"The # of basic blocks in the analyzed functions.");
+STATISTIC(NumVisitedBlocksInAnalyzedFunctions,
+ "The # of visited basic blocks in the analyzed functions.");
STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
@@ -70,7 +73,7 @@ void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
const Preprocessor &PP) {
createHTMLDiagnosticConsumer(AnalyzerOpts, C,
llvm::sys::path::parent_path(prefix), PP);
- createPlistDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
+ createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
}
void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
@@ -161,6 +164,8 @@ class AnalysisConsumer : public AnalysisASTConsumer,
/// Bug Reporter to use while recursively visiting Decls.
BugReporter *RecVisitorBR;
+ std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns;
+
public:
ASTContext *Ctx;
const Preprocessor &PP;
@@ -168,8 +173,9 @@ public:
AnalyzerOptionsRef Opts;
ArrayRef<std::string> Plugins;
CodeInjector *Injector;
+ cross_tu::CrossTranslationUnitContext CTU;
- /// \brief Stores the declarations from the local translation unit.
+ /// Stores the declarations from the local translation unit.
/// Note, we pre-compute the local declarations at parse time as an
/// optimization to make sure we do not deserialize everything from disk.
/// The local declaration to all declarations ratio might be very small when
@@ -186,28 +192,31 @@ public:
std::unique_ptr<AnalysisManager> Mgr;
/// Time the analyzes time of each translation unit.
- static llvm::Timer* TUTotalTimer;
+ std::unique_ptr<llvm::TimerGroup> AnalyzerTimers;
+ std::unique_ptr<llvm::Timer> TUTotalTimer;
/// The information about analyzed functions shared throughout the
/// translation unit.
FunctionSummariesTy FunctionSummaries;
- AnalysisConsumer(const Preprocessor &pp, const std::string &outdir,
+ AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
CodeInjector *injector)
- : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), PP(pp),
- OutDir(outdir), Opts(std::move(opts)), Plugins(plugins),
- Injector(injector) {
+ : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
+ PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
+ Plugins(plugins), Injector(injector), CTU(CI) {
DigestAnalyzerOptions();
- if (Opts->PrintStats) {
- llvm::EnableStatistics(false);
- TUTotalTimer = new llvm::Timer("time", "Analyzer Total Time");
+ if (Opts->PrintStats || Opts->shouldSerializeStats()) {
+ AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>(
+ "analyzer", "Analyzer timers");
+ TUTotalTimer = llvm::make_unique<llvm::Timer>(
+ "time", "Analyzer total time", *AnalyzerTimers);
+ llvm::EnableStatistics(/* PrintOnExit= */ false);
}
}
~AnalysisConsumer() override {
if (Opts->PrintStats) {
- delete TUTotalTimer;
llvm::PrintStatistics();
}
}
@@ -286,32 +295,33 @@ public:
void Initialize(ASTContext &Context) override {
Ctx = &Context;
- checkerMgr = createCheckerManager(*Opts, PP.getLangOpts(), Plugins,
- PP.getDiagnostics());
+ checkerMgr =
+ createCheckerManager(*Opts, PP.getLangOpts(), Plugins,
+ CheckerRegistrationFns, PP.getDiagnostics());
Mgr = llvm::make_unique<AnalysisManager>(
*Ctx, PP.getDiagnostics(), PP.getLangOpts(), PathConsumers,
CreateStoreMgr, CreateConstraintMgr, checkerMgr.get(), *Opts, Injector);
}
- /// \brief Store the top level decls in the set to be processed later on.
+ /// Store the top level decls in the set to be processed later on.
/// (Doing this pre-processing avoids deserialization of data from PCH.)
bool HandleTopLevelDecl(DeclGroupRef D) override;
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
void HandleTranslationUnit(ASTContext &C) override;
- /// \brief Determine which inlining mode should be used when this function is
+ /// Determine which inlining mode should be used when this function is
/// analyzed. This allows to redefine the default inlining policies when
/// analyzing a given function.
ExprEngine::InliningModes
getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited);
- /// \brief Build the call graph for all the top level decls of this TU and
+ /// Build the call graph for all the top level decls of this TU and
/// use it to define the order in which the functions should be visited.
void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize);
- /// \brief Run analyzes(syntax or path sensitive) on the given function.
+ /// Run analyzes(syntax or path sensitive) on the given function.
/// \param Mode - determines if we are requesting syntax only or path
/// sensitive only analysis.
/// \param VisitedCallees - The output parameter, which is populated with the
@@ -378,13 +388,20 @@ public:
PathConsumers.push_back(Consumer);
}
+ void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override {
+ CheckerRegistrationFns.push_back(std::move(Fn));
+ }
+
private:
void storeTopLevelDecls(DeclGroupRef DG);
std::string getFunctionName(const Decl *D);
- /// \brief Check if we should skip (not analyze) the given function.
+ /// Check if we should skip (not analyze) the given function.
AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode);
+ void runAnalysisOnTranslationUnit(ASTContext &C);
+ /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.
+ void reportAnalyzerProgress(StringRef S);
};
} // end anonymous namespace
@@ -392,8 +409,6 @@ private:
//===----------------------------------------------------------------------===//
// AnalysisConsumer implementation.
//===----------------------------------------------------------------------===//
-llvm::Timer* AnalysisConsumer::TUTotalTimer = nullptr;
-
bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
storeTopLevelDecls(DG);
return true;
@@ -508,68 +523,90 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
}
}
+static bool isBisonFile(ASTContext &C) {
+ const SourceManager &SM = C.getSourceManager();
+ FileID FID = SM.getMainFileID();
+ StringRef Buffer = SM.getBuffer(FID)->getBuffer();
+ if (Buffer.startswith("/* A Bison parser, made by"))
+ return true;
+ return false;
+}
+
+void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
+ BugReporter BR(*Mgr);
+ TranslationUnitDecl *TU = C.getTranslationUnitDecl();
+ checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
+
+ // Run the AST-only checks using the order in which functions are defined.
+ // If inlining is not turned on, use the simplest function order for path
+ // sensitive analyzes as well.
+ RecVisitorMode = AM_Syntax;
+ if (!Mgr->shouldInlineCall())
+ RecVisitorMode |= AM_Path;
+ RecVisitorBR = &BR;
+
+ // Process all the top level declarations.
+ //
+ // Note: TraverseDecl may modify LocalTUDecls, but only by appending more
+ // entries. Thus we don't use an iterator, but rely on LocalTUDecls
+ // random access. By doing so, we automatically compensate for iterators
+ // possibly being invalidated, although this is a bit slower.
+ const unsigned LocalTUDeclsSize = LocalTUDecls.size();
+ for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
+ TraverseDecl(LocalTUDecls[i]);
+ }
+
+ if (Mgr->shouldInlineCall())
+ HandleDeclsCallGraph(LocalTUDeclsSize);
+
+ // After all decls handled, run checkers on the entire TranslationUnit.
+ checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
+
+ RecVisitorBR = nullptr;
+}
+
+void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
+ if (Opts->AnalyzerDisplayProgress)
+ llvm::errs() << S;
+}
+
void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
+
// Don't run the actions if an error has occurred with parsing the file.
DiagnosticsEngine &Diags = PP.getDiagnostics();
if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
return;
- // Don't analyze if the user explicitly asked for no checks to be performed
- // on this file.
- if (Opts->DisableAllChecks)
- return;
-
- {
- if (TUTotalTimer) TUTotalTimer->startTimer();
-
- // Introduce a scope to destroy BR before Mgr.
- BugReporter BR(*Mgr);
- TranslationUnitDecl *TU = C.getTranslationUnitDecl();
- checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
-
- // Run the AST-only checks using the order in which functions are defined.
- // If inlining is not turned on, use the simplest function order for path
- // sensitive analyzes as well.
- RecVisitorMode = AM_Syntax;
- if (!Mgr->shouldInlineCall())
- RecVisitorMode |= AM_Path;
- RecVisitorBR = &BR;
-
- // Process all the top level declarations.
- //
- // Note: TraverseDecl may modify LocalTUDecls, but only by appending more
- // entries. Thus we don't use an iterator, but rely on LocalTUDecls
- // random access. By doing so, we automatically compensate for iterators
- // possibly being invalidated, although this is a bit slower.
- const unsigned LocalTUDeclsSize = LocalTUDecls.size();
- for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
- TraverseDecl(LocalTUDecls[i]);
- }
+ if (TUTotalTimer) TUTotalTimer->startTimer();
- if (Mgr->shouldInlineCall())
- HandleDeclsCallGraph(LocalTUDeclsSize);
+ if (isBisonFile(C)) {
+ reportAnalyzerProgress("Skipping bison-generated file\n");
+ } else if (Opts->DisableAllChecks) {
- // After all decls handled, run checkers on the entire TranslationUnit.
- checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
-
- RecVisitorBR = nullptr;
+ // Don't analyze if the user explicitly asked for no checks to be performed
+ // on this file.
+ reportAnalyzerProgress("All checks are disabled using a supplied option\n");
+ } else {
+ // Otherwise, just run the analysis.
+ runAnalysisOnTranslationUnit(C);
}
- // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
- // FIXME: This should be replaced with something that doesn't rely on
- // side-effects in PathDiagnosticConsumer's destructor. This is required when
- // used with option -disable-free.
- Mgr.reset();
-
if (TUTotalTimer) TUTotalTimer->stopTimer();
// Count how many basic blocks we have not covered.
NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
+ NumVisitedBlocksInAnalyzedFunctions =
+ FunctionSummaries.getTotalNumVisitedBasicBlocks();
if (NumBlocksInAnalyzedFunctions > 0)
PercentReachableBlocks =
(FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
NumBlocksInAnalyzedFunctions;
+ // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
+ // FIXME: This should be replaced with something that doesn't rely on
+ // side-effects in PathDiagnosticConsumer's destructor. This is required when
+ // used with option -disable-free.
+ Mgr.reset();
}
std::string AnalysisConsumer::getFunctionName(const Decl *D) {
@@ -648,7 +685,7 @@ AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
SourceLocation SL = Body ? Body->getLocStart() : D->getLocation();
SL = SM.getExpansionLoc(SL);
- if (!Opts->AnalyzeAll && !SM.isWrittenInMainFile(SL)) {
+ if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) {
if (SL.isInvalid() || SM.isInSystemHeader(SL))
return AM_None;
return Mode & ~AM_Path;
@@ -704,7 +741,8 @@ void AnalysisConsumer::ActionExprEngine(Decl *D, bool ObjCGCEnabled,
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;
- ExprEngine Eng(*Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,IMode);
+ ExprEngine Eng(CTU, *Mgr, ObjCGCEnabled, VisitedCallees, &FunctionSummaries,
+ IMode);
// Set the graph auditor.
std::unique_ptr<ExplodedNode::Auditor> Auditor;
@@ -762,7 +800,7 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) {
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
return llvm::make_unique<AnalysisConsumer>(
- CI.getPreprocessor(), CI.getFrontendOpts().OutputFile, analyzerOpts,
+ CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
CI.getFrontendOpts().Plugins,
hasModelPath ? new ModelInjector(CI) : nullptr);
}
@@ -852,9 +890,9 @@ UbigraphViz::~UbigraphViz() {
std::string Ubiviz;
if (auto Path = llvm::sys::findProgramByName("ubiviz"))
Ubiviz = *Path;
- const char *args[] = {Ubiviz.c_str(), Filename.c_str(), nullptr};
+ std::array<StringRef, 2> Args{{Ubiviz, Filename}};
- if (llvm::sys::ExecuteAndWait(Ubiviz, &args[0], nullptr, {}, 0, 0, &ErrMsg)) {
+ if (llvm::sys::ExecuteAndWait(Ubiviz, Args, llvm::None, {}, 0, 0, &ErrMsg)) {
llvm::errs() << "Error viewing graph: " << ErrMsg << "\n";
}
diff --git a/lib/StaticAnalyzer/Frontend/CMakeLists.txt b/lib/StaticAnalyzer/Frontend/CMakeLists.txt
index e3ca91aec9cd..ff0a6e19fc97 100644
--- a/lib/StaticAnalyzer/Frontend/CMakeLists.txt
+++ b/lib/StaticAnalyzer/Frontend/CMakeLists.txt
@@ -7,14 +7,15 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangStaticAnalyzerFrontend
AnalysisConsumer.cpp
CheckerRegistration.cpp
- ModelConsumer.cpp
FrontendActions.cpp
+ ModelConsumer.cpp
ModelInjector.cpp
LINK_LIBS
clangAST
clangAnalysis
clangBasic
+ clangCrossTU
clangFrontend
clangLex
clangStaticAnalyzerCheckers
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index 6792f89876cd..a260c2d85b11 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -111,16 +111,21 @@ getCheckerOptList(const AnalyzerOptions &opts) {
return checkerOpts;
}
-std::unique_ptr<CheckerManager>
-ento::createCheckerManager(AnalyzerOptions &opts, const LangOptions &langOpts,
- ArrayRef<std::string> plugins,
- DiagnosticsEngine &diags) {
+std::unique_ptr<CheckerManager> ento::createCheckerManager(
+ AnalyzerOptions &opts, const LangOptions &langOpts,
+ ArrayRef<std::string> plugins,
+ ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns,
+ DiagnosticsEngine &diags) {
std::unique_ptr<CheckerManager> checkerMgr(
new CheckerManager(langOpts, opts));
SmallVector<CheckerOptInfo, 8> checkerOpts = getCheckerOptList(opts);
ClangCheckerRegistry allCheckers(plugins, &diags);
+
+ for (const auto &Fn : checkerRegistrationFns)
+ Fn(allCheckers);
+
allCheckers.initializeManager(*checkerMgr, checkerOpts);
allCheckers.validateCheckerOptions(opts, diags);
checkerMgr->finishedCheckerRegistration();
diff --git a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
index a65a5ee0a451..60825ef7411d 100644
--- a/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements an ASTConsumer for consuming model files.
+/// This file implements an ASTConsumer for consuming model files.
///
/// This ASTConsumer handles the AST of a parsed model file. All top level
/// function definitions will be collected from that model file for later
diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
index cdb1ed9b3815..c43d30440c8f 100644
--- a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
+++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -10,6 +10,7 @@
#include "ModelInjector.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/Stack.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
@@ -95,11 +96,10 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) {
ParseModelFileAction parseModelFile(Bodies);
- const unsigned ThreadStackSize = 8 << 20;
llvm::CrashRecoveryContext CRC;
CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); },
- ThreadStackSize);
+ DesiredStackSize);
Instance.getPreprocessor().FinalizeForModelFile();
@@ -109,7 +109,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) {
// The preprocessor enters to the main file id when parsing is started, so
// the main file id is changed to the model file during parsing and it needs
- // to be reseted to the former main file id after parsing of the model file
+ // to be reset to the former main file id after parsing of the model file
// is done.
SM.setMainFileID(mainFileID);
}
diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.h b/lib/StaticAnalyzer/Frontend/ModelInjector.h
index 98a5f69d68e8..b1b6de9ef9d9 100644
--- a/lib/StaticAnalyzer/Frontend/ModelInjector.h
+++ b/lib/StaticAnalyzer/Frontend/ModelInjector.h
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file defines the clang::ento::ModelInjector class which implements the
+/// This file defines the clang::ento::ModelInjector class which implements the
/// clang::CodeInjector interface. This class is responsible for injecting
/// function definitions that were synthesized from model files.
///
@@ -43,7 +43,7 @@ public:
Stmt *getBody(const ObjCMethodDecl *D) override;
private:
- /// \brief Synthesize a body for a declaration
+ /// Synthesize a body for a declaration
///
/// This method first looks up the appropriate model file based on the
/// model-path configuration option and the name of the declaration that is