summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp72
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp27
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/CastValueChecker.cpp445
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp43
-rw-r--r--lib/StaticAnalyzer/Checkers/ChrootChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/CloneChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/ConversionChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp106
-rw-r--r--lib/StaticAnalyzer/Checkers/DebugCheckers.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp54
-rw-r--r--lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp264
-rw-r--r--lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp19
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorChecker.cpp51
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp42
-rw-r--r--lib/StaticAnalyzer/Checkers/MIGChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp24
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h6
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp43
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp1287
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/MoveChecker.cpp23
-rw-r--r--lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp31
-rw-r--r--lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp36
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/PaddingChecker.cpp5
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h2
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp61
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h18
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp22
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.h6
-rw-r--r--lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp16
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp3
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp6
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp8
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp4
-rw-r--r--lib/StaticAnalyzer/Checkers/ValistChecker.cpp44
-rw-r--r--lib/StaticAnalyzer/Checkers/VforkChecker.cpp2
-rw-r--r--lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp203
-rwxr-xr-xlib/StaticAnalyzer/Checkers/Yaml.h59
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp19
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp1858
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp660
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp24
-rw-r--r--lib/StaticAnalyzer/Core/Checker.cpp8
-rw-r--r--lib/StaticAnalyzer/Core/CheckerHelpers.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/CommonBugCategories.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/DynamicType.cpp229
-rw-r--r--lib/StaticAnalyzer/Core/DynamicTypeMap.cpp97
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp1
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp120
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp91
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp36
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp50
-rw-r--r--lib/StaticAnalyzer/Core/LoopUnrolling.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp5
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp1466
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp157
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp89
-rw-r--r--lib/StaticAnalyzer/Core/SMTConstraintManager.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/SarifDiagnostics.cpp123
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp2
-rw-r--r--lib/StaticAnalyzer/Core/WorkList.cpp12
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp106
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp23
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp24
-rw-r--r--lib/StaticAnalyzer/Frontend/FrontendActions.cpp2
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.cpp3
115 files changed, 4273 insertions, 4343 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index 58017acb4a246..8d4793e0802ff 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -75,7 +75,8 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
// reference is outside the range.
// Generate a report for this bug.
- auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
report->addRange(LoadS->getSourceRange());
C.emitReport(std::move(report));
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 3bf8a1836b19b..8f3bf138cae4a 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -208,7 +208,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
SVal ByteOffset = rawOffset.getByteOffset();
if (isTainted(state, ByteOffset)) {
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
- llvm::make_unique<TaintBugVisitor>(ByteOffset));
+ std::make_unique<TaintBugVisitor>(ByteOffset));
return;
}
} else if (state_exceedsUpperBound) {
@@ -256,7 +256,7 @@ void ArrayBoundCheckerV2::reportOOB(
break;
}
- auto BR = llvm::make_unique<BugReport>(*BT, os.str(), errorNode);
+ auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode);
BR->addVisitor(std::move(Visitor));
checkerContext.emitReport(std::move(BR));
}
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index e3fb4c3eb523f..325952fe4ed4d 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -211,7 +211,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N,
if (!BT)
BT.reset(new APIMisuse(this, "nil argument"));
- auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
R->addRange(Range);
bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
@@ -520,7 +520,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
if (!BT)
BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
- auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
report->addRange(CE->getArg(2)->getSourceRange());
C.emitReport(std::move(report));
}
@@ -575,7 +575,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
OS << "Null pointer argument in call to "
<< cast<FunctionDecl>(Call.getDecl())->getName();
- auto report = llvm::make_unique<BugReport>(BT, OS.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
report->addRange(Call.getArgSourceRange(0));
bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
C.emitReport(std::move(report));
@@ -635,7 +635,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
"of class '" << Class->getName()
<< "' and not the class directly";
- auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
report->addRange(msg.getSourceRange());
C.emitReport(std::move(report));
}
@@ -788,7 +788,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
ArgTy.print(os, C.getLangOpts());
os << "'";
- auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(),
+ errorNode.getValue());
R->addRange(msg.getArgSourceRange(I));
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 009160fc98157..0eb3c3d1d0e6d 100644
--- a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -126,7 +126,7 @@ bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
- const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
+ const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
auto IdentifierInfo = DRecordDecl->getIdentifier();
if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
return true;
@@ -173,7 +173,8 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
llvm::raw_string_ostream os(msg);
os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
<< "' inside of critical section";
- auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType,
+ os.str(), ErrNode);
R->addRange(Call.getSourceRange());
R->markInteresting(BlockDescSym);
C.emitReport(std::move(R));
diff --git a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index de8763c1b7b57..1423b9c39b266 100644
--- a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -34,7 +34,9 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state,
if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
if (!BT)
BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
- C.emitReport(llvm::make_unique<BugReport>(*BT, BT->getDescription(), N));
+
+ C.emitReport(
+ std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 44f4530781a8e..503c451670b8d 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -48,10 +48,10 @@ public:
DefaultBool CheckCStringBufferOverlap;
DefaultBool CheckCStringNotNullTerm;
- CheckName CheckNameCStringNullArg;
- CheckName CheckNameCStringOutOfBounds;
- CheckName CheckNameCStringBufferOverlap;
- CheckName CheckNameCStringNotNullTerm;
+ CheckerNameRef CheckNameCStringNullArg;
+ CheckerNameRef CheckNameCStringOutOfBounds;
+ CheckerNameRef CheckNameCStringBufferOverlap;
+ CheckerNameRef CheckNameCStringNotNullTerm;
};
CStringChecksFilter Filter;
@@ -198,7 +198,8 @@ public:
ProgramStateRef checkNonNull(CheckerContext &C,
ProgramStateRef state,
const Expr *S,
- SVal l) const;
+ SVal l,
+ unsigned IdxOfArg) const;
ProgramStateRef CheckLocation(CheckerContext &C,
ProgramStateRef state,
const Expr *S,
@@ -277,7 +278,8 @@ CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
ProgramStateRef state,
- const Expr *S, SVal l) const {
+ const Expr *S, SVal l,
+ unsigned IdxOfArg) const {
// If a previous check has failed, propagate the failure.
if (!state)
return nullptr;
@@ -288,11 +290,13 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
if (stateNull && !stateNonNull) {
if (Filter.CheckCStringNullArg) {
SmallString<80> buf;
- llvm::raw_svector_ostream os(buf);
+ llvm::raw_svector_ostream OS(buf);
assert(CurrentFunctionDescription);
- os << "Null pointer argument in call to " << CurrentFunctionDescription;
+ OS << "Null pointer argument in call to " << CurrentFunctionDescription
+ << ' ' << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
+ << " parameter";
- emitNullArgBug(C, stateNull, S, os.str());
+ emitNullArgBug(C, stateNull, S, OS.str());
}
return nullptr;
}
@@ -384,7 +388,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
// Check that the first buffer is non-null.
SVal BufVal = C.getSVal(FirstBuf);
- state = checkNonNull(C, state, FirstBuf, BufVal);
+ state = checkNonNull(C, state, FirstBuf, BufVal, 1);
if (!state)
return nullptr;
@@ -424,7 +428,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
// If there's a second buffer, check it as well.
if (SecondBuf) {
BufVal = state->getSVal(SecondBuf, LCtx);
- state = checkNonNull(C, state, SecondBuf, BufVal);
+ state = checkNonNull(C, state, SecondBuf, BufVal, 2);
if (!state)
return nullptr;
@@ -566,7 +570,7 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
categories::UnixAPI, "Improper arguments"));
// Generate a report for this bug.
- auto report = llvm::make_unique<BugReport>(
+ auto report = std::make_unique<PathSensitiveBugReport>(
*BT_Overlap, "Arguments must not be overlapping buffers", N);
report->addRange(First->getSourceRange());
report->addRange(Second->getSourceRange());
@@ -583,7 +587,7 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
"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);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N);
Report->addRange(S->getSourceRange());
if (const auto *Ex = dyn_cast<Expr>(S))
bugreporter::trackExpressionValue(N, Ex, *Report);
@@ -607,7 +611,7 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
// 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);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -622,7 +626,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
"Argument is not a null-terminated string."));
- auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
Report->addRange(S->getSourceRange());
C.emitReport(std::move(Report));
@@ -644,7 +649,8 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
"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);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
C.emitReport(std::move(Report));
}
}
@@ -1163,7 +1169,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// Ensure the destination is not null. If it is NULL there will be a
// NULL pointer dereference.
- state = checkNonNull(C, state, Dest, destVal);
+ state = checkNonNull(C, state, Dest, destVal, 1);
if (!state)
return;
@@ -1172,7 +1178,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
// Ensure the source is not null. If it is NULL there will be a
// NULL pointer dereference.
- state = checkNonNull(C, state, Source, srcVal);
+ state = checkNonNull(C, state, Source, srcVal, 2);
if (!state)
return;
@@ -1383,7 +1389,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
const Expr *Arg = CE->getArg(0);
SVal ArgVal = state->getSVal(Arg, LCtx);
- state = checkNonNull(C, state, Arg, ArgVal);
+ state = checkNonNull(C, state, Arg, ArgVal, 1);
if (!state)
return;
@@ -1541,14 +1547,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
const Expr *Dst = CE->getArg(0);
SVal DstVal = state->getSVal(Dst, LCtx);
- state = checkNonNull(C, state, Dst, DstVal);
+ state = checkNonNull(C, state, Dst, DstVal, 1);
if (!state)
return;
// Check that the source is non-null.
const Expr *srcExpr = CE->getArg(1);
SVal srcVal = state->getSVal(srcExpr, LCtx);
- state = checkNonNull(C, state, srcExpr, srcVal);
+ state = checkNonNull(C, state, srcExpr, srcVal, 2);
if (!state)
return;
@@ -1904,14 +1910,14 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
// Check that the first string is non-null
const Expr *s1 = CE->getArg(0);
SVal s1Val = state->getSVal(s1, LCtx);
- state = checkNonNull(C, state, s1, s1Val);
+ state = checkNonNull(C, state, s1, s1Val, 1);
if (!state)
return;
// Check that the second string is non-null.
const Expr *s2 = CE->getArg(1);
SVal s2Val = state->getSVal(s2, LCtx);
- state = checkNonNull(C, state, s2, s2Val);
+ state = checkNonNull(C, state, s2, s2Val, 2);
if (!state)
return;
@@ -2038,14 +2044,14 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
// Check that the search string pointer is non-null (though it may point to
// a null string).
SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx);
- State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
+ State = checkNonNull(C, State, SearchStrPtr, SearchStrVal, 1);
if (!State)
return;
// Check that the delimiter string is non-null.
const Expr *DelimStr = CE->getArg(1);
SVal DelimStrVal = State->getSVal(DelimStr, LCtx);
- State = checkNonNull(C, State, DelimStr, DelimStrVal);
+ State = checkNonNull(C, State, DelimStr, DelimStrVal, 2);
if (!State)
return;
@@ -2148,7 +2154,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
// Ensure the memory area is not null.
// If it is NULL there will be a NULL pointer dereference.
- State = checkNonNull(C, StateNonZeroSize, Mem, MemVal);
+ State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1);
if (!State)
return;
@@ -2195,7 +2201,7 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
// Ensure the memory area is not null.
// If it is NULL there will be a NULL pointer dereference.
- State = checkNonNull(C, StateNonZeroSize, Mem, MemVal);
+ State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1);
if (!State)
return;
@@ -2403,14 +2409,12 @@ bool ento::shouldRegisterCStringModeling(const LangOptions &LO) {
void ento::register##name(CheckerManager &mgr) { \
CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
checker->Filter.Check##name = true; \
- checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
- REGISTER_CHECKER(CStringNullArg)
- REGISTER_CHECKER(CStringOutOfBounds)
- REGISTER_CHECKER(CStringBufferOverlap)
+REGISTER_CHECKER(CStringNullArg)
+REGISTER_CHECKER(CStringOutOfBounds)
+REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index b828ac0592363..d84fcc69a4925 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -156,14 +156,21 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
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());
+ const auto *DstArgDRE = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts());
+ const auto *LenArgDRE =
+ dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
uint64_t DstOff = 0;
if (isSizeof(LenArg, DstArg))
return false;
+
// - size_t dstlen = sizeof(dst)
- if (LenArgDecl) {
- const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
+ if (LenArgDRE) {
+ const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDRE->getDecl());
+ // If it's an EnumConstantDecl instead, then we're missing out on something.
+ if (!LenArgVal) {
+ assert(isa<EnumConstantDecl>(LenArgDRE->getDecl()));
+ return false;
+ }
if (LenArgVal->getInit())
LenArg = LenArgVal->getInit();
}
@@ -177,9 +184,10 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
// 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 (!DstArgDRE) {
+ if (const auto *BE =
+ dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) {
+ DstArgDRE = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts());
if (BE->getOpcode() == BO_Add) {
if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) {
DstOff = IL->getValue().getZExtValue();
@@ -187,8 +195,9 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
}
}
}
- if (DstArgDecl) {
- if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
+ if (DstArgDRE) {
+ if (const auto *Buffer =
+ dyn_cast<ConstantArrayType>(DstArgDRE->getType())) {
ASTContext &C = BR.getContext();
uint64_t BufferLen = C.getTypeSize(Buffer) / 8;
auto RemainingBufferLen = BufferLen - DstOff;
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 5a7eba0760fe5..2fcb765cd4eef 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -49,7 +49,7 @@ class CallAndMessageChecker
public:
DefaultBool Check_CallAndMessageUnInitRefArg;
- CheckName CheckName_CallAndMessageUnInitRefArg;
+ CheckerNameRef CheckName_CallAndMessageUnInitRefArg;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -95,7 +95,7 @@ void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
if (!N)
return;
- auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
if (BadE) {
R->addRange(BadE->getSourceRange());
if (BadE->isGLValue())
@@ -175,7 +175,7 @@ bool CallAndMessageChecker::uninitRefOrPointer(
if (PSV.isUndef()) {
if (ExplodedNode *N = C.generateErrorNode()) {
LazyInit_BT(BD, BT);
- auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
bugreporter::trackExpressionValue(N, ArgEx, *R);
@@ -252,7 +252,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
SmallString<200> Buf;
llvm::raw_svector_ostream Os(Buf);
describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
- auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
@@ -295,7 +295,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
}
// Generate a report for this bug.
- auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
R->addRange(ArgRange);
if (ArgEx)
@@ -358,7 +358,7 @@ void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
else
Desc = "Argument to 'delete' is uninitialized";
BugType *BT = BT_cxx_delete_undef.get();
- auto R = llvm::make_unique<BugReport>(*BT, Desc, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N);
bugreporter::trackExpressionValue(N, DE, *R);
C.emitReport(std::move(R));
return;
@@ -420,8 +420,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
<< (Params == 1 ? "" : "s") << " is called with fewer ("
<< Call.getNumArgs() << ")";
- C.emitReport(
- llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N));
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT_call_few_args,
+ os.str(), N));
}
}
@@ -482,7 +482,7 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
}
assert(BT && "Unknown message kind.");
- auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
const ObjCMessageExpr *ME = msg.getOriginExpr();
R->addRange(ME->getReceiverRange());
@@ -525,7 +525,8 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
os << "' that will be garbage";
}
- auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT_msg_ret, os.str(), N);
report->addRange(ME->getReceiverRange());
// FIXME: This won't track "self" in messages to super.
if (const Expr *receiver = ME->getInstanceReceiver()) {
@@ -611,7 +612,7 @@ bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) {
void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) {
CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>();
Checker->Check_CallAndMessageUnInitRefArg = true;
- Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckName();
+ Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckerName();
}
bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) {
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 05ece961467fb..51c1d4409929e 100644
--- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -132,7 +132,8 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
"Cast a region whose size is not a multiple"
" of the destination type size."));
- auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(),
+ errorNode);
R->addRange(CE->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index ff5d12c27c69f..cc1c9a66b90e2 100644
--- a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -6,178 +6,429 @@
//
//===----------------------------------------------------------------------===//
//
-// This defines CastValueChecker which models casts of custom RTTIs.
+// This defines CastValueChecker which models casts of custom RTTIs.
+//
+// TODO list:
+// - It only allows one succesful cast between two types however in the wild
+// the object could be casted to multiple types.
+// - It needs to check the most likely type information from the dynamic type
+// map to increase precision of dynamic casting.
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/DeclTemplate.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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/DynamicType.h"
#include "llvm/ADT/Optional.h"
+#include <utility>
using namespace clang;
using namespace ento;
namespace {
class CastValueChecker : public Checker<eval::Call> {
+ enum class CallKind { Function, Method, InstanceOf };
+
using CastCheck =
- std::function<void(const CastValueChecker *, const CallExpr *,
+ std::function<void(const CastValueChecker *, const CallEvent &Call,
DefinedOrUnknownSVal, CheckerContext &)>;
public:
- // We have three cases to evaluate a cast:
- // 1) The parameter is non-null, the return value is non-null
- // 2) The parameter is non-null, the return value is null
- // 3) The parameter is null, the return value is null
- //
+ // We have five cases to evaluate a cast:
+ // 1) The parameter is non-null, the return value is non-null.
+ // 2) The parameter is non-null, the return value is null.
+ // 3) The parameter is null, the return value is null.
// cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.
+ //
+ // 4) castAs: Has no parameter, the return value is non-null.
+ // 5) getAs: Has no parameter, the return value is null or non-null.
+ //
+ // We have two cases to check the parameter is an instance of the given type.
+ // 1) isa: The parameter is non-null, returns boolean.
+ // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
private:
- // These are known in the LLVM project.
- const CallDescriptionMap<CastCheck> CDM = {
- {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast},
- {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
- {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
+ // These are known in the LLVM project. The pairs are in the following form:
+ // {{{namespace, call}, argument-count}, {callback, kind}}
+ const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
+ {{{"llvm", "cast"}, 1},
+ {&CastValueChecker::evalCast, CallKind::Function}},
+ {{{"llvm", "dyn_cast"}, 1},
+ {&CastValueChecker::evalDynCast, CallKind::Function}},
+ {{{"llvm", "cast_or_null"}, 1},
+ {&CastValueChecker::evalCastOrNull, CallKind::Function}},
{{{"llvm", "dyn_cast_or_null"}, 1},
- &CastValueChecker::evalDynCastOrNull}};
-
- void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
+ {{{"clang", "castAs"}, 0},
+ {&CastValueChecker::evalCastAs, CallKind::Method}},
+ {{{"clang", "getAs"}, 0},
+ {&CastValueChecker::evalGetAs, CallKind::Method}},
+ {{{"llvm", "isa"}, 1},
+ {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
+ {{{"llvm", "isa_and_nonnull"}, 1},
+ {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
+
+ void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
CheckerContext &C) const;
- void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
CheckerContext &C) const;
- void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
CheckerContext &C) const;
- void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const;
+ void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const;
+ void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const;
+ void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const;
+ void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
CheckerContext &C) const;
};
} // namespace
-static std::string getCastName(const Expr *Cast) {
- return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
+static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
+ bool CastSucceeds) {
+ if (!CastInfo)
+ return false;
+
+ return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
}
-static void evalNonNullParamNonNullReturn(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
- CheckerContext &C) {
- ProgramStateRef State = C.getState()->assume(ParamDV, true);
- if (!State)
- return;
+static const NoteTag *getNoteTag(CheckerContext &C,
+ const DynamicCastInfo *CastInfo,
+ QualType CastToTy, const Expr *Object,
+ bool CastSucceeds, bool IsKnownCast) {
+ std::string CastToName =
+ CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString()
+ : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+ Object = Object->IgnoreParenImpCasts();
+
+ return C.getNoteTag(
+ [=]() -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
- State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false);
+ if (!IsKnownCast)
+ Out << "Assuming ";
- std::string CastFromName = getCastName(CE->getArg(0));
- std::string CastToName = getCastName(CE);
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
+ Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
+ } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
+ Out << (IsKnownCast ? "Field '" : "field '")
+ << ME->getMemberDecl()->getNameAsString() << '\'';
+ } else {
+ Out << (IsKnownCast ? "The object" : "the object");
+ }
- const NoteTag *CastTag = C.getNoteTag(
- [CastFromName, CastToName](BugReport &) -> std::string {
- SmallString<128> Msg;
- llvm::raw_svector_ostream Out(Msg);
+ Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
+ << '\'';
- Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
- << CastToName << "' succeeds";
return Out.str();
},
/*IsPrunable=*/true);
+}
- C.addTransition(State, CastTag);
+//===----------------------------------------------------------------------===//
+// Main logic to evaluate a cast.
+//===----------------------------------------------------------------------===//
+
+static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
+ ASTContext &ACtx) {
+ if (alignTowards->isLValueReferenceType() &&
+ alignTowards.isConstQualified()) {
+ toAlign.addConst();
+ return ACtx.getLValueReferenceType(toAlign);
+ } else if (alignTowards->isLValueReferenceType())
+ return ACtx.getLValueReferenceType(toAlign);
+ else if (alignTowards->isRValueReferenceType())
+ return ACtx.getRValueReferenceType(toAlign);
+
+ llvm_unreachable("Must align towards a reference type!");
}
-static void evalNonNullParamNullReturn(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
- CheckerContext &C) {
- ProgramStateRef State = C.getState()->assume(ParamDV, true);
+static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C, bool IsNonNullParam,
+ bool IsNonNullReturn,
+ bool IsCheckedCast = false) {
+ ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
if (!State)
return;
- State = State->BindExpr(CE, C.getLocationContext(),
- C.getSValBuilder().makeNull(), false);
+ const Expr *Object;
+ QualType CastFromTy;
+ QualType CastToTy = Call.getResultType();
+
+ if (Call.getNumArgs() > 0) {
+ Object = Call.getArgExpr(0);
+ CastFromTy = Call.parameters()[0]->getType();
+ } else {
+ Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
+ CastFromTy = Object->getType();
+ if (CastToTy->isPointerType()) {
+ if (!CastFromTy->isPointerType())
+ return;
+ } else {
+ if (!CastFromTy->isReferenceType())
+ return;
+
+ CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
+ }
+ }
+
+ const MemRegion *MR = DV.getAsRegion();
+ const DynamicCastInfo *CastInfo =
+ getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
+
+ // We assume that every checked cast succeeds.
+ bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
+ if (!CastSucceeds) {
+ if (CastInfo)
+ CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
+ else
+ CastSucceeds = IsNonNullReturn;
+ }
+
+ // Check for infeasible casts.
+ if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+ C.generateSink(State, C.getPredecessor());
+ return;
+ }
+
+ // Store the type and the cast information.
+ bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
+ if (!IsKnownCast || IsCheckedCast)
+ State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
+ CastSucceeds);
+
+ SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
+ : C.getSValBuilder().makeNull();
+ C.addTransition(
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
+ getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
+}
- std::string CastFromName = getCastName(CE->getArg(0));
- std::string CastToName = getCastName(CE);
+static void addInstanceOfTransition(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ ProgramStateRef State, CheckerContext &C,
+ bool IsInstanceOf) {
+ const FunctionDecl *FD = Call.getDecl()->getAsFunction();
+ QualType CastFromTy = Call.parameters()[0]->getType();
+ QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
+ if (CastFromTy->isPointerType())
+ CastToTy = C.getASTContext().getPointerType(CastToTy);
+ else if (CastFromTy->isReferenceType())
+ CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
+ else
+ return;
- const NoteTag *CastTag = C.getNoteTag(
- [CastFromName, CastToName](BugReport &) -> std::string {
- SmallString<128> Msg;
- llvm::raw_svector_ostream Out(Msg);
+ const MemRegion *MR = DV.getAsRegion();
+ const DynamicCastInfo *CastInfo =
+ getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
- Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
- << CastToName << "' fails";
- return Out.str();
- },
- /*IsPrunable=*/true);
+ bool CastSucceeds;
+ if (CastInfo)
+ CastSucceeds = IsInstanceOf && CastInfo->succeeds();
+ else
+ CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
- C.addTransition(State, CastTag);
+ if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+ C.generateSink(State, C.getPredecessor());
+ return;
+ }
+
+ // Store the type and the cast information.
+ bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
+ if (!IsKnownCast)
+ State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
+ IsInstanceOf);
+
+ C.addTransition(
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(CastSucceeds)),
+ getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
+ IsKnownCast));
}
-static void evalNullParamNullReturn(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
- CheckerContext &C) {
- ProgramStateRef State = C.getState()->assume(ParamDV, false);
- if (!State)
- return;
+//===----------------------------------------------------------------------===//
+// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
+//===----------------------------------------------------------------------===//
- State = State->BindExpr(CE, C.getLocationContext(),
- C.getSValBuilder().makeNull(), false);
+static void evalNonNullParamNonNullReturn(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C,
+ bool IsCheckedCast = false) {
+ addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
+ /*IsNonNullReturn=*/true, IsCheckedCast);
+}
- const NoteTag *CastTag =
- C.getNoteTag("Assuming null pointer is passed into cast",
- /*IsPrunable=*/true);
+static void evalNonNullParamNullReturn(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C) {
+ addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
+ /*IsNonNullReturn=*/false);
+}
- C.addTransition(State, CastTag);
+static void evalNullParamNullReturn(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C) {
+ if (ProgramStateRef State = C.getState()->assume(DV, false))
+ C.addTransition(State->BindExpr(Call.getOriginExpr(),
+ C.getLocationContext(),
+ C.getSValBuilder().makeNull(), false),
+ C.getNoteTag("Assuming null pointer is passed into cast",
+ /*IsPrunable=*/true));
}
-void CastValueChecker::evalCast(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
CheckerContext &C) const {
- evalNonNullParamNonNullReturn(CE, ParamDV, C);
+ evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
}
-void CastValueChecker::evalDynCast(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalDynCast(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
CheckerContext &C) const {
- evalNonNullParamNonNullReturn(CE, ParamDV, C);
- evalNonNullParamNullReturn(CE, ParamDV, C);
+ evalNonNullParamNonNullReturn(Call, DV, C);
+ evalNonNullParamNullReturn(Call, DV, C);
}
-void CastValueChecker::evalCastOrNull(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalCastOrNull(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
CheckerContext &C) const {
- evalNonNullParamNonNullReturn(CE, ParamDV, C);
- evalNullParamNullReturn(CE, ParamDV, C);
+ evalNonNullParamNonNullReturn(Call, DV, C);
+ evalNullParamNullReturn(Call, DV, C);
}
-void CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
- DefinedOrUnknownSVal ParamDV,
+void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
CheckerContext &C) const {
- evalNonNullParamNonNullReturn(CE, ParamDV, C);
- evalNonNullParamNullReturn(CE, ParamDV, C);
- evalNullParamNullReturn(CE, ParamDV, C);
+ evalNonNullParamNonNullReturn(Call, DV, C);
+ evalNonNullParamNullReturn(Call, DV, C);
+ evalNullParamNullReturn(Call, DV, C);
}
-bool CastValueChecker::evalCall(const CallEvent &Call,
- CheckerContext &C) const {
- const CastCheck *Check = CDM.lookup(Call);
- if (!Check)
- return false;
+//===----------------------------------------------------------------------===//
+// Evaluating castAs, getAs.
+//===----------------------------------------------------------------------===//
- const auto *CE = cast<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return false;
+static void evalZeroParamNonNullReturn(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C,
+ bool IsCheckedCast = false) {
+ addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
+ /*IsNonNullReturn=*/true, IsCheckedCast);
+}
+
+static void evalZeroParamNullReturn(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C) {
+ addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
+ /*IsNonNullReturn=*/false);
+}
+
+void CastValueChecker::evalCastAs(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C) const {
+ evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
+}
+
+void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const {
+ evalZeroParamNonNullReturn(Call, DV, C);
+ evalZeroParamNullReturn(Call, DV, C);
+}
- // If we cannot obtain both of the classes we cannot be sure how to model it.
- if (!CE->getType()->getPointeeCXXRecordDecl() ||
- !CE->getArg(0)->getType()->getPointeeCXXRecordDecl())
+//===----------------------------------------------------------------------===//
+// Evaluating isa, isa_and_nonnull.
+//===----------------------------------------------------------------------===//
+
+void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
+ CheckerContext &C) const {
+ ProgramStateRef NonNullState, NullState;
+ std::tie(NonNullState, NullState) = C.getState()->assume(DV);
+
+ if (NonNullState) {
+ addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
+ addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
+ }
+
+ if (NullState) {
+ C.generateSink(NullState, C.getPredecessor());
+ }
+}
+
+void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
+ DefinedOrUnknownSVal DV,
+ CheckerContext &C) const {
+ ProgramStateRef NonNullState, NullState;
+ std::tie(NonNullState, NullState) = C.getState()->assume(DV);
+
+ if (NonNullState) {
+ addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
+ addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
+ }
+
+ if (NullState) {
+ addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Main logic to evaluate a call.
+//===----------------------------------------------------------------------===//
+
+bool CastValueChecker::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *Lookup = CDM.lookup(Call);
+ if (!Lookup)
return false;
- SVal ParamV = Call.getArgSVal(0);
- auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>();
- if (!ParamDV)
+ const CastCheck &Check = Lookup->first;
+ CallKind Kind = Lookup->second;
+
+ Optional<DefinedOrUnknownSVal> DV;
+
+ switch (Kind) {
+ case CallKind::Function: {
+ // We only model casts from pointers to pointers or from references
+ // to references. Other casts are most likely specialized and we
+ // cannot model them.
+ QualType ParamT = Call.parameters()[0]->getType();
+ QualType ResultT = Call.getResultType();
+ if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
+ !(ParamT->isReferenceType() && ResultT->isReferenceType()))
+ return false;
+
+ DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+ break;
+ }
+ case CallKind::InstanceOf: {
+ // We need to obtain the only template argument to determinte the type.
+ const FunctionDecl *FD = Call.getDecl()->getAsFunction();
+ if (!FD || !FD->getTemplateSpecializationArgs())
+ return false;
+
+ DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
+ break;
+ }
+ case CallKind::Method:
+ const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
+ if (!InstanceCall)
+ return false;
+
+ DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
+ break;
+ }
+
+ if (!DV)
return false;
- (*Check)(this, CE, *ParamDV, C);
+ Check(this, Call, *DV, C);
return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index a7ca814c8f967..50b872bd8682f 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -28,6 +28,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
@@ -36,7 +37,6 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.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/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
@@ -576,9 +576,8 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
OS << " by a synthesized property but not released"
" before '[super dealloc]'";
- std::unique_ptr<BugReport> BR(
- new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
-
+ auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType,
+ OS.str(), ErrNode);
C.emitReport(std::move(BR));
}
@@ -699,8 +698,8 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
OS << " property but was released in 'dealloc'";
}
- std::unique_ptr<BugReport> BR(
- new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
+ auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType,
+ OS.str(), ErrNode);
BR->addRange(M.getOriginExpr()->getSourceRange());
C.emitReport(std::move(BR));
@@ -741,8 +740,8 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
OS << "'" << *PropImpl->getPropertyIvarDecl()
<< "' should be released rather than deallocated";
- std::unique_ptr<BugReport> BR(
- new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
+ auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType,
+ OS.str(), ErrNode);
BR->addRange(M.getOriginExpr()->getSourceRange());
C.emitReport(std::move(BR));
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index a020d33bfd95f..1694c237cda42 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -13,11 +13,11 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Type.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/raw_ostream.h"
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 3f1c213a56478..260a2896e78c8 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -50,19 +50,19 @@ 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;
- CheckName checkName_mkstemp;
- CheckName checkName_strcpy;
- CheckName checkName_DeprecatedOrUnsafeBufferHandling;
- CheckName checkName_rand;
- CheckName checkName_vfork;
- CheckName checkName_FloatLoopCounter;
- CheckName checkName_UncheckedReturn;
+ CheckerNameRef checkName_bcmp;
+ CheckerNameRef checkName_bcopy;
+ CheckerNameRef checkName_bzero;
+ CheckerNameRef checkName_gets;
+ CheckerNameRef checkName_getpw;
+ CheckerNameRef checkName_mktemp;
+ CheckerNameRef checkName_mkstemp;
+ CheckerNameRef checkName_strcpy;
+ CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling;
+ CheckerNameRef checkName_rand;
+ CheckerNameRef checkName_vfork;
+ CheckerNameRef checkName_FloatLoopCounter;
+ CheckerNameRef checkName_UncheckedReturn;
};
class WalkAST : public StmtVisitor<WalkAST> {
@@ -204,6 +204,8 @@ void WalkAST::VisitForStmt(ForStmt *FS) {
// Implements: CERT security coding advisory FLP-30.
//===----------------------------------------------------------------------===//
+// Returns either 'x' or 'y', depending on which one of them is incremented
+// in 'expr', or nullptr if none of them is incremented.
static const DeclRefExpr*
getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
expr = expr->IgnoreParenCasts();
@@ -289,14 +291,15 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
// Does either variable appear in increment?
const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
-
if (!drInc)
return;
+ const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl());
+ assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS));
+
// Emit the error. First figure out which DeclRefExpr in the condition
// referenced the compared variable.
- assert(drInc->getDecl());
- const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
+ const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS;
SmallVector<SourceRange, 2> ranges;
SmallString<256> sbuf;
@@ -1012,14 +1015,12 @@ bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
- SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
+ SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
checker->filter.check_##name = true; \
- checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
+ checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
REGISTER_CHECKER(bcmp)
REGISTER_CHECKER(bcopy)
diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 9fffedfccd871..7a41a7b6b216e 100644
--- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -127,7 +127,7 @@ void ChrootChecker::checkPreCall(const CallEvent &Call,
BT_BreakJail.reset(new BuiltinBug(
this, "Break out of jail", "No call of chdir(\"/\") immediately "
"after chroot"));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_BreakJail, BT_BreakJail->getDescription(), N));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
index 4fc225056d4c7..ce45b5be34c9f 100644
--- a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -114,8 +114,8 @@ void CloneChecker::reportClones(
for (const CloneDetector::CloneGroup &Group : CloneGroups) {
// We group the clones by printing the first as a warning and all others
// as a note.
- auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected",
- makeLocation(Group.front(), Mgr));
+ auto R = std::make_unique<BasicBugReport>(
+ *BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr));
R->addRange(Group.front().getSourceRange());
for (unsigned i = 1; i < Group.size(); ++i)
@@ -169,7 +169,7 @@ void CloneChecker::reportSuspiciousClones(
// which may confuse the user.
// Think how to perform more accurate suggestions?
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<BasicBugReport>(
*BT_Suspicious,
"Potential copy-paste error; did you really mean to use '" +
Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
diff --git a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
index 5058d101b8e59..8dd3132f07e2b 100644
--- a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -121,7 +121,7 @@ void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
// Generate a report for this bug.
- auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index d5baa2bcba6fc..61441889fc649 100644
--- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Lex/Lexer.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
@@ -119,11 +120,20 @@ LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
}
namespace {
+class DeadStoresChecker : public Checker<check::ASTCodeBody> {
+public:
+ bool ShowFixIts = false;
+ bool WarnForDeadNestedAssignments = true;
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+};
+
class DeadStoreObs : public LiveVariables::Observer {
const CFG &cfg;
ASTContext &Ctx;
BugReporter& BR;
- const CheckerBase *Checker;
+ const DeadStoresChecker *Checker;
AnalysisDeclContext* AC;
ParentMap& Parents;
llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
@@ -135,9 +145,10 @@ class DeadStoreObs : public LiveVariables::Observer {
public:
DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
- const CheckerBase *checker, AnalysisDeclContext *ac,
+ const DeadStoresChecker *checker, AnalysisDeclContext *ac,
ParentMap &parents,
- llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
+ llvm::SmallPtrSet<const VarDecl *, 20> &escaped,
+ bool warnForDeadNestedAssignments)
: cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
Escaped(escaped), currentBlock(nullptr) {}
@@ -202,12 +213,32 @@ public:
llvm::raw_svector_ostream os(buf);
const char *BugType = nullptr;
+ SmallVector<FixItHint, 1> Fixits;
+
switch (dsk) {
- case DeadInit:
+ case DeadInit: {
BugType = "Dead initialization";
os << "Value stored to '" << *V
<< "' during its initialization is never read";
+
+ ASTContext &ACtx = V->getASTContext();
+ if (Checker->ShowFixIts) {
+ if (V->getInit()->HasSideEffects(ACtx,
+ /*IncludePossibleEffects=*/true)) {
+ break;
+ }
+ SourceManager &SM = ACtx.getSourceManager();
+ const LangOptions &LO = ACtx.getLangOpts();
+ SourceLocation L1 =
+ Lexer::findNextToken(
+ V->getTypeSourceInfo()->getTypeLoc().getEndLoc(),
+ SM, LO)->getEndLoc();
+ SourceLocation L2 =
+ Lexer::getLocForEndOfToken(V->getInit()->getEndLoc(), 1, SM, LO);
+ Fixits.push_back(FixItHint::CreateRemoval({L1, L2}));
+ }
break;
+ }
case DeadIncrement:
BugType = "Dead increment";
@@ -217,15 +248,20 @@ public:
os << "Value stored to '" << *V << "' is never read";
break;
+ // eg.: f((x = foo()))
case Enclosing:
- // Don't report issues in this case, e.g.: "if (x = foo())",
- // where 'x' is unused later. We have yet to see a case where
- // this is a real bug.
- return;
+ if (!Checker->WarnForDeadNestedAssignments)
+ return;
+ BugType = "Dead nested assignment";
+ os << "Although the value stored to '" << *V
+ << "' is used in the enclosing expression, the value is never "
+ "actually read from '"
+ << *V << "'";
+ break;
}
BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
- L, R);
+ L, R, Fixits);
}
void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
@@ -471,35 +507,37 @@ public:
// DeadStoresChecker
//===----------------------------------------------------------------------===//
-namespace {
-class DeadStoresChecker : public Checker<check::ASTCodeBody> {
-public:
- void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
- BugReporter &BR) const {
-
- // Don't do anything for template instantiations.
- // Proving that code in a template instantiation is "dead"
- // means proving that it is dead in all instantiations.
- // This same problem exists with -Wunreachable-code.
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
- if (FD->isTemplateInstantiation())
- return;
+void DeadStoresChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
+ BugReporter &BR) const {
- if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
- CFG &cfg = *mgr.getCFG(D);
- AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
- ParentMap &pmap = mgr.getParentMap(D);
- FindEscaped FS;
- cfg.VisitBlockStmts(FS);
- DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
- L->runOnAllBlocks(A);
- }
+ // Don't do anything for template instantiations.
+ // Proving that code in a template instantiation is "dead"
+ // means proving that it is dead in all instantiations.
+ // This same problem exists with -Wunreachable-code.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ if (FD->isTemplateInstantiation())
+ return;
+
+ if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
+ CFG &cfg = *mgr.getCFG(D);
+ AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
+ ParentMap &pmap = mgr.getParentMap(D);
+ FindEscaped FS;
+ cfg.VisitBlockStmts(FS);
+ DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped,
+ WarnForDeadNestedAssignments);
+ L->runOnAllBlocks(A);
}
-};
}
-void ento::registerDeadStoresChecker(CheckerManager &mgr) {
- mgr.registerChecker<DeadStoresChecker>();
+void ento::registerDeadStoresChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.registerChecker<DeadStoresChecker>();
+
+ const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
+ Chk->WarnForDeadNestedAssignments =
+ AnOpts.getCheckerBooleanOption(Chk, "WarnForDeadNestedAssignments");
+ Chk->ShowFixIts =
+ AnOpts.getCheckerBooleanOption(Chk, "ShowFixIts");
}
bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {
diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index bb9e8110b6479..0cb4be2c7fdc3 100644
--- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -333,7 +333,8 @@ public:
if (!Node)
return;
- auto Report = llvm::make_unique<BugReport>(BT_stmtLoc, "Statement", Node);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(BT_stmtLoc, "Statement", Node);
C.emitReport(std::move(Report));
}
diff --git a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
index 8bf77c109f8a6..45c1984c5e157 100644
--- a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
@@ -27,7 +27,7 @@
#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/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
using namespace clang;
@@ -45,9 +45,9 @@ class DeleteWithNonVirtualDtorChecker
static int X = 0;
ID.AddPointer(&X);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
bool Satisfied;
@@ -92,23 +92,23 @@ void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
"Logic error"));
ExplodedNode *N = C.generateNonFatalErrorNode();
- auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
// Mark region of problematic base class for later use in the BugVisitor.
R->markInteresting(BaseClassRegion);
- R->addVisitor(llvm::make_unique<DeleteBugVisitor>());
+ R->addVisitor(std::make_unique<DeleteBugVisitor>());
C.emitReport(std::move(R));
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
const ExplodedNode *N, BugReporterContext &BRC,
- BugReport &BR) {
+ PathSensitiveBugReport &BR) {
// Stop traversal after the first conversion was found on a path.
if (Satisfied)
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
@@ -140,8 +140,7 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
OS << "Conversion from derived to base happened here";
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
- nullptr);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
}
void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 2c264833f2a9f..e3de0b4f4a7ff 100644
--- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -179,7 +179,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
break;
}
- auto report = llvm::make_unique<BugReport>(
+ auto report = std::make_unique<PathSensitiveBugReport>(
*BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
@@ -200,8 +200,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
BT_undef.reset(
new BuiltinBug(this, "Dereference of undefined pointer value"));
- auto report =
- llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(
+ *BT_undef, BT_undef->getDescription(), N);
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
C.emitReport(std::move(report));
}
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index 33e8fcd8af7bb..8798bde88dcd6 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -47,7 +47,7 @@ void DivZeroChecker::reportBug(
if (!BT)
BT.reset(new BuiltinBug(this, "Division by zero"));
- auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
R->addVisitor(std::move(Visitor));
bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
C.emitReport(std::move(R));
@@ -88,7 +88,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
bool TaintedD = isTainted(C.getState(), *DV);
if ((stateNotZero && stateZero && TaintedD)) {
reportBug("Division by a tainted value, possibly zero", stateZero, C,
- llvm::make_unique<taint::TaintBugVisitor>(*DV));
+ std::make_unique<taint::TaintBugVisitor>(*DV));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
index 4d979dc9f2400..8cc38f9735f33 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -21,7 +21,7 @@
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
@@ -47,9 +47,9 @@ class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
ID.AddPointer(Reg);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
// The tracked region.
@@ -80,18 +80,16 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType,
QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
llvm::Twine());
OS << "'";
- std::unique_ptr<BugReport> R(
- new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT, OS.str(), C.generateNonFatalErrorNode());
R->markInteresting(Reg);
- R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
+ R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg));
R->addRange(ReportedNode->getSourceRange());
C.emitReport(std::move(R));
}
-std::shared_ptr<PathDiagnosticPiece>
-DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &) {
+PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = N->getFirstPred()->getState();
@@ -105,7 +103,7 @@ DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
@@ -141,8 +139,7 @@ DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
- nullptr);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
}
static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 3cfe4dc82a100..cce3449b8873f 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -20,16 +20,16 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/Builtins.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
using namespace clang;
@@ -83,9 +83,9 @@ class DynamicTypePropagation:
ID.AddPointer(Sym);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
// The tracked symbol.
@@ -113,14 +113,7 @@ public:
void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR,
CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- DynamicTypeMapTy TypeMap = State->get<DynamicTypeMap>();
- for (DynamicTypeMapTy::iterator I = TypeMap.begin(), E = TypeMap.end();
- I != E; ++I) {
- if (!SR.isLiveRegion(I->first)) {
- State = State->remove<DynamicTypeMap>(I->first);
- }
- }
+ ProgramStateRef State = removeDeadTypes(C.getState(), SR);
MostSpecializedTypeArgsMapTy TyArgMap =
State->get<MostSpecializedTypeArgsMap>();
@@ -401,11 +394,11 @@ static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl(
}
const auto *SuperOfTo =
- To->getObjectType()->getSuperClassType()->getAs<ObjCObjectType>();
+ To->getObjectType()->getSuperClassType()->castAs<ObjCObjectType>();
assert(SuperOfTo);
QualType SuperPtrOfToQual =
C.getObjCObjectPointerType(QualType(SuperOfTo, 0));
- const auto *SuperPtrOfTo = SuperPtrOfToQual->getAs<ObjCObjectPointerType>();
+ const auto *SuperPtrOfTo = SuperPtrOfToQual->castAs<ObjCObjectPointerType>();
if (To->isUnspecialized())
return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo,
C);
@@ -834,16 +827,15 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class &&
Sel.getAsString() == "class") {
QualType ReceiverType = MessageExpr->getClassReceiver();
- const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>();
+ const auto *ReceiverClassType = ReceiverType->castAs<ObjCObjectType>();
+ if (!ReceiverClassType->isSpecialized())
+ return;
+
QualType ReceiverClassPointerType =
C.getASTContext().getObjCObjectPointerType(
QualType(ReceiverClassType, 0));
-
- if (!ReceiverClassType->isSpecialized())
- return;
const auto *InferredType =
- ReceiverClassPointerType->getAs<ObjCObjectPointerType>();
- assert(InferredType);
+ ReceiverClassPointerType->castAs<ObjCObjectPointerType>();
State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
C.addTransition(State);
@@ -882,7 +874,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
// When there is an entry available for the return symbol in DynamicTypeMap,
// the call was inlined, and the information in the DynamicTypeMap is should
// be precise.
- if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) {
+ if (RetRegion && !getRawDynamicTypeInfo(State, RetRegion)) {
// TODO: we have duplicated information in DynamicTypeMap and
// MostSpecializedTypeArgsMap. We should only store anything in the later if
// the stored data differs from the one stored in the former.
@@ -919,19 +911,18 @@ void DynamicTypePropagation::reportGenericsBug(
OS << "' to incompatible type '";
QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine());
OS << "'";
- std::unique_ptr<BugReport> R(
- new BugReport(*ObjCGenericsBugType, OS.str(), N));
+ auto R = std::make_unique<PathSensitiveBugReport>(*ObjCGenericsBugType,
+ OS.str(), N);
R->markInteresting(Sym);
- R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<GenericsBugVisitor>(Sym));
if (ReportedNode)
R->addRange(ReportedNode->getSourceRange());
C.emitReport(std::move(R));
}
-std::shared_ptr<PathDiagnosticPiece>
-DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) {
+PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
ProgramStateRef state = N->getState();
ProgramStateRef statePrev = N->getFirstPred()->getState();
@@ -946,7 +937,7 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
@@ -981,8 +972,7 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N,
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
- nullptr);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
}
/// Register checkers.
diff --git a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 736d80ef9ec7e..481a5685a71f6 100644
--- a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -83,7 +83,7 @@ void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
new BuiltinBug(this, "Enum cast out of range",
"The value provided to the cast expression is not in "
"the valid range of values for the enum"));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
N));
}
@@ -91,6 +91,22 @@ void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
CheckerContext &C) const {
+
+ // Only perform enum range check on casts where such checks are valid. For
+ // all other cast kinds (where enum range checks are unnecessary or invalid),
+ // just return immediately. TODO: The set of casts whitelisted for enum
+ // range checking may be incomplete. Better to add a missing cast kind to
+ // enable a missing check than to generate false negatives and have to remove
+ // those later.
+ switch (CE->getCastKind()) {
+ case CK_IntegralCast:
+ break;
+
+ default:
+ return;
+ break;
+ }
+
// Get the value of the expression to cast.
const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index f23f784016d88..17c813962a234 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -148,7 +148,7 @@ ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
if (!BT)
BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
- BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
+ BR.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, Msg, N));
return N;
}
@@ -305,7 +305,7 @@ void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
const SourceManager &SM = C.getSourceManager();
FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
std::string HashContent =
- GetIssueString(SM, FL, getCheckName().getName(), "Category",
+ GetIssueString(SM, FL, getCheckerName().getName(), "Category",
C.getLocationContext()->getDecl(), Opts);
reportBug(HashContent, C);
diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index 94542be7dd7ca..b315a84522859 100644
--- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -55,7 +55,8 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
"Using a fixed address is not portable because that "
"address will probably not be valid in all "
"environments or platforms."));
- auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
R->addRange(B->getRHS()->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index d3ab980332365..d442b26b39594 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -15,16 +15,18 @@
//===----------------------------------------------------------------------===//
#include "Taint.h"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "Yaml.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/Builtins.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
-#include <climits>
-#include <initializer_list>
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <limits>
#include <utility>
using namespace clang;
@@ -44,14 +46,51 @@ public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
- void printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const override;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
-private:
- static const unsigned InvalidArgIndex = UINT_MAX;
+ using ArgVector = SmallVector<unsigned, 2>;
+ using SignedArgVector = SmallVector<int, 2>;
+
+ enum class VariadicType { None, Src, Dst };
+
+ /// Used to parse the configuration file.
+ struct TaintConfiguration {
+ using NameArgsPair = std::pair<std::string, ArgVector>;
+
+ struct Propagation {
+ std::string Name;
+ ArgVector SrcArgs;
+ SignedArgVector DstArgs;
+ VariadicType VarType;
+ unsigned VarIndex;
+ };
+
+ std::vector<Propagation> Propagations;
+ std::vector<NameArgsPair> Filters;
+ std::vector<NameArgsPair> Sinks;
+
+ TaintConfiguration() = default;
+ TaintConfiguration(const TaintConfiguration &) = default;
+ TaintConfiguration(TaintConfiguration &&) = default;
+ TaintConfiguration &operator=(const TaintConfiguration &) = default;
+ TaintConfiguration &operator=(TaintConfiguration &&) = default;
+ };
+
+ /// Convert SignedArgVector to ArgVector.
+ ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option,
+ SignedArgVector Args);
+
+ /// Parse the config.
+ void parseConfiguration(CheckerManager &Mgr, const std::string &Option,
+ TaintConfiguration &&Config);
+
+ static const unsigned InvalidArgIndex{std::numeric_limits<unsigned>::max()};
/// Denotes the return vale.
- static const unsigned ReturnValueIndex = UINT_MAX - 1;
+ static const unsigned ReturnValueIndex{std::numeric_limits<unsigned>::max() -
+ 1};
+private:
mutable std::unique_ptr<BugType> BT;
void initBugType() const {
if (!BT)
@@ -76,28 +115,43 @@ private:
static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg);
/// Check for CWE-134: Uncontrolled Format String.
- static const char MsgUncontrolledFormatString[];
+ static constexpr llvm::StringLiteral MsgUncontrolledFormatString =
+ "Untrusted data is used as a format string "
+ "(CWE-134: Uncontrolled Format String)";
bool checkUncontrolledFormatString(const CallExpr *CE,
CheckerContext &C) const;
/// Check for:
/// CERT/STR02-C. "Sanitize data passed to complex subsystems"
/// CWE-78, "Failure to Sanitize Data into an OS Command"
- static const char MsgSanitizeSystemArgs[];
+ static constexpr llvm::StringLiteral MsgSanitizeSystemArgs =
+ "Untrusted data is passed to a system call "
+ "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
bool checkSystemCall(const CallExpr *CE, StringRef Name,
CheckerContext &C) const;
/// Check if tainted data is used as a buffer size ins strn.. functions,
/// and allocators.
- static const char MsgTaintedBufferSize[];
+ static constexpr llvm::StringLiteral MsgTaintedBufferSize =
+ "Untrusted data is used to specify the buffer size "
+ "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
+ "for character data and the null terminator)";
bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl,
CheckerContext &C) const;
+ /// Check if tainted data is used as a custom sink's parameter.
+ static constexpr llvm::StringLiteral MsgCustomSink =
+ "Untrusted data is passed to a user-defined sink";
+ bool checkCustomSinks(const CallExpr *CE, StringRef Name,
+ CheckerContext &C) const;
+
/// Generate a report if the expression is tainted or points to tainted data.
- bool generateReportIfTainted(const Expr *E, const char Msg[],
+ bool generateReportIfTainted(const Expr *E, StringRef Msg,
CheckerContext &C) const;
- using ArgVector = SmallVector<unsigned, 2>;
+ struct TaintPropagationRule;
+ using NameRuleMap = llvm::StringMap<TaintPropagationRule>;
+ using NameArgMap = llvm::StringMap<ArgVector>;
/// A struct used to specify taint propagation rules for a function.
///
@@ -109,8 +163,6 @@ private:
/// ReturnValueIndex is added to the dst list, the return value will be
/// tainted.
struct TaintPropagationRule {
- enum class VariadicType { None, Src, Dst };
-
using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *,
CheckerContext &C);
@@ -131,8 +183,7 @@ private:
: VariadicIndex(InvalidArgIndex), VarType(VariadicType::None),
PropagationFunc(nullptr) {}
- TaintPropagationRule(std::initializer_list<unsigned> &&Src,
- std::initializer_list<unsigned> &&Dst,
+ TaintPropagationRule(ArgVector &&Src, ArgVector &&Dst,
VariadicType Var = VariadicType::None,
unsigned VarIndex = InvalidArgIndex,
PropagationFuncType Func = nullptr)
@@ -141,7 +192,8 @@ private:
/// Get the propagation rule for a given function.
static TaintPropagationRule
- getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name,
+ getTaintPropagationRule(const NameRuleMap &CustomPropagations,
+ const FunctionDecl *FDecl, StringRef Name,
CheckerContext &C);
void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
@@ -176,25 +228,71 @@ private:
static bool postSocket(bool IsTainted, const CallExpr *CE,
CheckerContext &C);
};
+
+ /// Defines a map between the propagation function's name and
+ /// TaintPropagationRule.
+ NameRuleMap CustomPropagations;
+
+ /// Defines a map between the filter function's name and filtering args.
+ NameArgMap CustomFilters;
+
+ /// Defines a map between the sink function's name and sinking args.
+ NameArgMap CustomSinks;
};
const unsigned GenericTaintChecker::ReturnValueIndex;
const unsigned GenericTaintChecker::InvalidArgIndex;
-const char GenericTaintChecker::MsgUncontrolledFormatString[] =
- "Untrusted data is used as a format string "
- "(CWE-134: Uncontrolled Format String)";
+// FIXME: these lines can be removed in C++17
+constexpr llvm::StringLiteral GenericTaintChecker::MsgUncontrolledFormatString;
+constexpr llvm::StringLiteral GenericTaintChecker::MsgSanitizeSystemArgs;
+constexpr llvm::StringLiteral GenericTaintChecker::MsgTaintedBufferSize;
+constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink;
+} // end of anonymous namespace
-const char GenericTaintChecker::MsgSanitizeSystemArgs[] =
- "Untrusted data is passed to a system call "
- "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
+using TaintConfig = GenericTaintChecker::TaintConfiguration;
-const char GenericTaintChecker::MsgTaintedBufferSize[] =
- "Untrusted data is used to specify the buffer size "
- "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
- "for character data and the null terminator)";
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation)
+LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameArgsPair)
-} // end of anonymous namespace
+namespace llvm {
+namespace yaml {
+template <> struct MappingTraits<TaintConfig> {
+ static void mapping(IO &IO, TaintConfig &Config) {
+ IO.mapOptional("Propagations", Config.Propagations);
+ IO.mapOptional("Filters", Config.Filters);
+ IO.mapOptional("Sinks", Config.Sinks);
+ }
+};
+
+template <> struct MappingTraits<TaintConfig::Propagation> {
+ static void mapping(IO &IO, TaintConfig::Propagation &Propagation) {
+ IO.mapRequired("Name", Propagation.Name);
+ IO.mapOptional("SrcArgs", Propagation.SrcArgs);
+ IO.mapOptional("DstArgs", Propagation.DstArgs);
+ IO.mapOptional("VariadicType", Propagation.VarType,
+ GenericTaintChecker::VariadicType::None);
+ IO.mapOptional("VariadicIndex", Propagation.VarIndex,
+ GenericTaintChecker::InvalidArgIndex);
+ }
+};
+
+template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> {
+ static void enumeration(IO &IO, GenericTaintChecker::VariadicType &Value) {
+ IO.enumCase(Value, "None", GenericTaintChecker::VariadicType::None);
+ IO.enumCase(Value, "Src", GenericTaintChecker::VariadicType::Src);
+ IO.enumCase(Value, "Dst", GenericTaintChecker::VariadicType::Dst);
+ }
+};
+
+template <> struct MappingTraits<TaintConfig::NameArgsPair> {
+ static void mapping(IO &IO, TaintConfig::NameArgsPair &NameArg) {
+ IO.mapRequired("Name", NameArg.first);
+ IO.mapRequired("Args", NameArg.second);
+ }
+};
+} // namespace yaml
+} // namespace llvm
/// A set which is used to pass information from call pre-visit instruction
/// to the call post-visit. The values are unsigned integers, which are either
@@ -202,9 +300,46 @@ const char GenericTaintChecker::MsgTaintedBufferSize[] =
/// points to data, which should be tainted on return.
REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
+GenericTaintChecker::ArgVector GenericTaintChecker::convertToArgVector(
+ CheckerManager &Mgr, const std::string &Option, SignedArgVector Args) {
+ ArgVector Result;
+ for (int Arg : Args) {
+ if (Arg == -1)
+ Result.push_back(ReturnValueIndex);
+ else if (Arg < -1) {
+ Result.push_back(InvalidArgIndex);
+ Mgr.reportInvalidCheckerOptionValue(
+ this, Option,
+ "an argument number for propagation rules greater or equal to -1");
+ } else
+ Result.push_back(static_cast<unsigned>(Arg));
+ }
+ return Result;
+}
+
+void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr,
+ const std::string &Option,
+ TaintConfiguration &&Config) {
+ for (auto &P : Config.Propagations) {
+ GenericTaintChecker::CustomPropagations.try_emplace(
+ P.Name, std::move(P.SrcArgs),
+ convertToArgVector(Mgr, Option, P.DstArgs), P.VarType, P.VarIndex);
+ }
+
+ for (auto &F : Config.Filters) {
+ GenericTaintChecker::CustomFilters.try_emplace(F.first,
+ std::move(F.second));
+ }
+
+ for (auto &S : Config.Sinks) {
+ GenericTaintChecker::CustomSinks.try_emplace(S.first, std::move(S.second));
+ }
+}
+
GenericTaintChecker::TaintPropagationRule
GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
- const FunctionDecl *FDecl, StringRef Name, CheckerContext &C) {
+ const NameRuleMap &CustomPropagations, const FunctionDecl *FDecl,
+ StringRef Name, CheckerContext &C) {
// TODO: Currently, we might lose precision here: we always mark a return
// value as tainted even if it's just a pointer, pointing to tainted data.
@@ -218,7 +353,8 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
.Case("freopen", TaintPropagationRule({}, {ReturnValueIndex}))
.Case("getch", TaintPropagationRule({}, {ReturnValueIndex}))
.Case("getchar", TaintPropagationRule({}, {ReturnValueIndex}))
- .Case("getchar_unlocked", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("getchar_unlocked",
+ TaintPropagationRule({}, {ReturnValueIndex}))
.Case("getenv", TaintPropagationRule({}, {ReturnValueIndex}))
.Case("gets", TaintPropagationRule({}, {0, ReturnValueIndex}))
.Case("scanf", TaintPropagationRule({}, {}, VariadicType::Dst, 1))
@@ -297,6 +433,10 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// or smart memory copy:
// - memccpy - copying until hitting a special character.
+ auto It = CustomPropagations.find(Name);
+ if (It != CustomPropagations.end())
+ return It->getValue();
+
return TaintPropagationRule();
}
@@ -336,8 +476,8 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
return;
// First, try generating a propagation rule for this function.
- TaintPropagationRule Rule =
- TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C);
+ TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule(
+ this->CustomPropagations, FDecl, Name, C);
if (!Rule.isNull()) {
State = Rule.process(CE, C);
if (!State)
@@ -409,6 +549,9 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE,
if (checkTaintedBufferSize(CE, FDecl, C))
return true;
+ if (checkCustomSinks(CE, Name, C))
+ return true;
+
return false;
}
@@ -446,7 +589,8 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
bool IsTainted = true;
for (unsigned ArgNum : SrcArgs) {
if (ArgNum >= CE->getNumArgs())
- return State;
+ continue;
+
if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C)))
break;
}
@@ -454,7 +598,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
// Check for taint in variadic arguments.
if (!IsTainted && VariadicType::Src == VarType) {
// Check if any of the arguments is tainted
- for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) {
+ for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) {
if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C)))
break;
}
@@ -474,8 +618,10 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
continue;
}
+ if (ArgNum >= CE->getNumArgs())
+ continue;
+
// Mark the given argument.
- assert(ArgNum < CE->getNumArgs());
State = State->add<TaintArgsOnPostVisit>(ArgNum);
}
@@ -485,7 +631,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
// If they are not pointing to const data, mark data as tainted.
// TODO: So far we are just going one level down; ideally we'd need to
// recurse here.
- for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) {
+ for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) {
const Expr *Arg = CE->getArg(i);
// Process pointer argument.
const Type *ArgTy = Arg->getType().getTypePtr();
@@ -550,7 +696,7 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
static bool getPrintfFormatArgumentNum(const CallExpr *CE,
const CheckerContext &C,
- unsigned int &ArgNum) {
+ unsigned &ArgNum) {
// Find if the function contains a format string argument.
// Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf,
// vsnprintf, syslog, custom annotated functions.
@@ -572,8 +718,7 @@ static bool getPrintfFormatArgumentNum(const CallExpr *CE,
return false;
}
-bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
- const char Msg[],
+bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg,
CheckerContext &C) const {
assert(E);
@@ -591,9 +736,9 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
// Generate diagnostic.
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
initBugType();
- auto report = llvm::make_unique<BugReport>(*BT, Msg, N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
report->addRange(E->getSourceRange());
- report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal));
+ report->addVisitor(std::make_unique<TaintBugVisitor>(TaintedSVal));
C.emitReport(std::move(report));
return true;
}
@@ -603,7 +748,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
bool GenericTaintChecker::checkUncontrolledFormatString(
const CallExpr *CE, CheckerContext &C) const {
// Check if the function contains a format string argument.
- unsigned int ArgNum = 0;
+ unsigned ArgNum = 0;
if (!getPrintfFormatArgumentNum(CE, C, ArgNum))
return false;
@@ -629,9 +774,9 @@ bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name,
.Case("execvP", 0)
.Case("execve", 0)
.Case("dlopen", 0)
- .Default(UINT_MAX);
+ .Default(InvalidArgIndex);
- if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1))
+ if (ArgNum == InvalidArgIndex || CE->getNumArgs() < (ArgNum + 1))
return false;
return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C);
@@ -676,8 +821,33 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
}
-void ento::registerGenericTaintChecker(CheckerManager &mgr) {
- mgr.registerChecker<GenericTaintChecker>();
+bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, StringRef Name,
+ CheckerContext &C) const {
+ auto It = CustomSinks.find(Name);
+ if (It == CustomSinks.end())
+ return false;
+
+ const GenericTaintChecker::ArgVector &Args = It->getValue();
+ for (unsigned ArgNum : Args) {
+ if (ArgNum >= CE->getNumArgs())
+ continue;
+
+ if (generateReportIfTainted(CE->getArg(ArgNum), MsgCustomSink, C))
+ return true;
+ }
+
+ return false;
+}
+
+void ento::registerGenericTaintChecker(CheckerManager &Mgr) {
+ auto *Checker = Mgr.registerChecker<GenericTaintChecker>();
+ std::string Option{"Config"};
+ StringRef ConfigFile =
+ Mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option);
+ llvm::Optional<TaintConfig> Config =
+ getConfiguration<TaintConfig>(Mgr, Checker, Option, ConfigFile);
+ if (Config)
+ Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue()));
}
bool ento::shouldRegisterGenericTaintChecker(const LangOptions &LO) {
diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index e3270f1f7be27..b0d101c88517f 100644
--- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -54,9 +54,9 @@ public:
ID.AddPointer(getTag());
}
- virtual std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ virtual PathDiagnosticPieceRef
+ VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
// FIXME: Scan the map once in the visitor's constructor and do a direct
// lookup by region.
@@ -261,7 +261,7 @@ namespace ento {
namespace allocation_state {
std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
- return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
+ return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
}
const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
@@ -278,15 +278,13 @@ const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
} // end namespace ento
} // end namespace clang
-std::shared_ptr<PathDiagnosticPiece>
-InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &) {
+PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
if (!isSymbolTracked(N->getState(), PtrToBuf) ||
isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
@@ -301,8 +299,7 @@ InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
<< "' obtained here";
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
- nullptr);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
}
void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
index 6f1060b5f26d9..97ace68569ef2 100644
--- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -71,7 +71,7 @@
#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/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include <utility>
@@ -248,7 +248,7 @@ public:
};
DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckName CheckNames[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -356,14 +356,12 @@ bool isZero(ProgramStateRef State, const NonLoc &Val);
IteratorChecker::IteratorChecker() {
OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs",
- /*SuppressOnSink=*/true));
+ new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
MismatchedBugType.reset(
new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
/*SuppressOnSink=*/true));
InvalidatedBugType.reset(
- new BugType(this, "Iterator invalidated", "Misuse of STL APIs",
- /*SuppressOnSink=*/true));
+ new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
}
void IteratorChecker::checkPreCall(const CallEvent &Call,
@@ -406,13 +404,15 @@ void IteratorChecker::checkPreCall(const CallEvent &Call,
} else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
// Check for out-of-range incrementions and decrementions
- if (Call.getNumArgs() >= 1) {
+ if (Call.getNumArgs() >= 1 &&
+ Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
InstCall->getCXXThisVal(),
Call.getArgSVal(0));
}
} else {
- if (Call.getNumArgs() >= 2) {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
Call.getArgSVal(0), Call.getArgSVal(1));
}
@@ -565,7 +565,8 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
if (Func->isOverloadedOperator()) {
const auto Op = Func->getOverloadedOperator();
if (isAssignmentOperator(Op)) {
- const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
+ // Overloaded 'operator=' must be a non-static member function.
+ const auto *InstCall = cast<CXXInstanceCall>(&Call);
if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
Call.getArgSVal(0));
@@ -590,14 +591,16 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
return;
} else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
- if (Call.getNumArgs() >= 1) {
+ if (Call.getNumArgs() >= 1 &&
+ Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) {
handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
Call.getReturnValue(),
InstCall->getCXXThisVal(), Call.getArgSVal(0));
return;
}
} else {
- if (Call.getNumArgs() >= 2) {
+ if (Call.getNumArgs() >= 2 &&
+ Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1));
@@ -923,7 +926,7 @@ void IteratorChecker::verifyDereference(CheckerContext &C,
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && isPastTheEnd(State, *Pos)) {
- auto *N = C.generateNonFatalErrorNode(State);
+ auto *N = C.generateErrorNode(State);
if (!N)
return;
reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
@@ -935,7 +938,7 @@ void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Val);
if (Pos && !Pos->isValid()) {
- auto *N = C.generateNonFatalErrorNode(State);
+ auto *N = C.generateErrorNode(State);
if (!N) {
return;
}
@@ -1043,14 +1046,14 @@ void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
// The result may be the past-end iterator of the container, but any other
// out of range position is undefined behaviour
if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateNonFatalErrorNode(State);
+ auto *N = C.generateErrorNode(State);
if (!N)
return;
reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
C, N);
}
if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
- auto *N = C.generateNonFatalErrorNode(State);
+ auto *N = C.generateErrorNode(State);
if (!N)
return;
reportOutOfRangeBug("Iterator incremented behind the past-the-end "
@@ -1587,7 +1590,8 @@ IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
const SVal &Val, CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message,
+ ErrNode);
R->markInteresting(Val);
C.emitReport(std::move(R));
}
@@ -1596,7 +1600,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message,
const SVal &Val1, const SVal &Val2,
CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ ErrNode);
R->markInteresting(Val1);
R->markInteresting(Val2);
C.emitReport(std::move(R));
@@ -1606,7 +1611,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message,
const SVal &Val, const MemRegion *Reg,
CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message,
+ ErrNode);
R->markInteresting(Val);
R->markInteresting(Reg);
C.emitReport(std::move(R));
@@ -1615,7 +1621,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message,
void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
const SVal &Val, CheckerContext &C,
ExplodedNode *ErrNode) const {
- auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType,
+ Message, ErrNode);
R->markInteresting(Val);
C.emitReport(std::move(R));
}
@@ -2373,12 +2380,10 @@ bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
auto *checker = Mgr.getChecker<IteratorChecker>(); \
checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
checker->CheckNames[IteratorChecker::CK_##name] = \
- Mgr.getCurrentCheckName(); \
+ Mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
REGISTER_CHECKER(IteratorRangeChecker)
REGISTER_CHECKER(MismatchedIteratorChecker)
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index 2b75f3acc9228..0d64fbd6f62ec 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -48,8 +48,8 @@ struct ChecksFilter {
/// Check that all ivars are invalidated.
DefaultBool check_InstanceVariableInvalidation;
- CheckName checkName_MissingInvalidationMethod;
- CheckName checkName_InstanceVariableInvalidation;
+ CheckerNameRef checkName_MissingInvalidationMethod;
+ CheckerNameRef checkName_InstanceVariableInvalidation;
};
class IvarInvalidationCheckerImpl {
@@ -199,7 +199,7 @@ class IvarInvalidationCheckerImpl {
const ObjCIvarDecl *IvarDecl,
const IvarToPropMapTy &IvarToPopertyMap);
- void reportNoInvalidationMethod(CheckName CheckName,
+ void reportNoInvalidationMethod(CheckerNameRef CheckName,
const ObjCIvarDecl *FirstIvarDecl,
const IvarToPropMapTy &IvarToPopertyMap,
const ObjCInterfaceDecl *InterfaceD,
@@ -526,7 +526,7 @@ visit(const ObjCImplementationDecl *ImplD) const {
}
void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
- CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
+ CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl,
const IvarToPropMapTy &IvarToPopertyMap,
const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
SmallString<128> sbuf;
@@ -748,12 +748,10 @@ bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) {
IvarInvalidationChecker *checker = \
mgr.getChecker<IvarInvalidationChecker>(); \
checker->Filter.check_##name = true; \
- checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
+ checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
REGISTER_CHECKER(InstanceVariableInvalidation)
REGISTER_CHECKER(MissingInvalidationMethod)
diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 46067ecbca991..a81015b6e524b 100644
--- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -120,12 +120,12 @@ class NonLocalizedStringBRVisitor final : public BugReporterVisitor {
public:
NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString)
: NonLocalizedString(NonLocalizedString), Satisfied(false) {
- assert(NonLocalizedString);
+ assert(NonLocalizedString);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.Add(NonLocalizedString);
@@ -761,8 +761,8 @@ void NonLocalizedStringChecker::reportLocalizationError(
return;
// Generate the bug report.
- std::unique_ptr<BugReport> R(new BugReport(
- *BT, "User-facing text should use localized string macro", ErrNode));
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT, "User-facing text should use localized string macro", ErrNode);
if (argumentNumber) {
R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange());
} else {
@@ -772,7 +772,7 @@ void NonLocalizedStringChecker::reportLocalizationError(
const MemRegion *StringRegion = S.getAsRegion();
if (StringRegion)
- R->addVisitor(llvm::make_unique<NonLocalizedStringBRVisitor>(StringRegion));
+ R->addVisitor(std::make_unique<NonLocalizedStringBRVisitor>(StringRegion));
C.emitReport(std::move(R));
}
@@ -882,18 +882,17 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- const Decl *D = Call.getDecl();
- if (D && isa<FunctionDecl>(D)) {
- const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
- auto formals = FD->parameters();
- for (unsigned i = 0,
- ei = std::min(unsigned(formals.size()), Call.getNumArgs());
- i != ei; ++i) {
- if (isAnnotatedAsTakingLocalized(formals[i])) {
- auto actual = Call.getArgSVal(i);
- if (hasNonLocalizedState(actual, C)) {
- reportLocalizationError(actual, Call, C, i + 1);
- }
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
+ return;
+
+ auto formals = FD->parameters();
+ for (unsigned i = 0, ei = std::min(static_cast<unsigned>(formals.size()),
+ Call.getNumArgs()); i != ei; ++i) {
+ if (isAnnotatedAsTakingLocalized(formals[i])) {
+ auto actual = Call.getArgSVal(i);
+ if (hasNonLocalizedState(actual, C)) {
+ reportLocalizationError(actual, Call, C, i + 1);
}
}
}
@@ -998,9 +997,10 @@ void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL,
setNonLocalizedState(sv, C);
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC, BugReport &BR) {
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
if (Satisfied)
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index 6e7776bb484e5..d8fd125f4003d 100644
--- a/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -274,7 +274,7 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const {
if (!N)
return;
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
BT,
"MIG callback fails with error after deallocating argument value. "
"This is a use-after-free vulnerability because the caller will try to "
@@ -282,7 +282,8 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const {
N);
R->addRange(RS->getSourceRange());
- bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false);
+ bugreporter::trackExpressionValue(N, RS->getRetValue(), *R,
+ bugreporter::TrackingKind::Thorough, false);
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
index b250d3f8795e7..bbf2ddec57620 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
@@ -30,8 +30,8 @@ void MPIBugReporter::reportDoubleNonblocking(
ErrorText = "Double nonblocking on request " +
RequestRegion->getDescriptiveName() + ". ";
- auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType,
- ErrorText, ExplNode);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *DoubleNonblockingBugType, ErrorText, ExplNode);
Report->addRange(MPICallEvent.getSourceRange());
SourceRange Range = RequestRegion->sourceRange();
@@ -39,7 +39,7 @@ void MPIBugReporter::reportDoubleNonblocking(
if (Range.isValid())
Report->addRange(Range);
- Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
+ Report->addVisitor(std::make_unique<RequestNodeVisitor>(
RequestRegion, "Request is previously used by nonblocking call here. "));
Report->markInteresting(RequestRegion);
@@ -53,13 +53,13 @@ void MPIBugReporter::reportMissingWait(
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching wait. "};
- auto Report =
- llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*MissingWaitBugType,
+ ErrorText, ExplNode);
SourceRange Range = RequestRegion->sourceRange();
if (Range.isValid())
Report->addRange(Range);
- Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
+ Report->addVisitor(std::make_unique<RequestNodeVisitor>(
RequestRegion, "Request is previously used by nonblocking call here. "));
Report->markInteresting(RequestRegion);
@@ -73,8 +73,8 @@ void MPIBugReporter::reportUnmatchedWait(
std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
" has no matching nonblocking call. "};
- auto Report =
- llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*UnmatchedWaitBugType,
+ ErrorText, ExplNode);
Report->addRange(CE.getSourceRange());
SourceRange Range = RequestRegion->sourceRange();
@@ -84,20 +84,22 @@ void MPIBugReporter::reportUnmatchedWait(
BReporter.emitReport(std::move(Report));
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
- BugReport &BR) {
+ PathSensitiveBugReport &BR) {
if (IsNodeFound)
return nullptr;
const Request *const Req = N->getState()->get<RequestMap>(RequestRegion);
+ assert(Req && "The region must be tracked and alive, given that we've "
+ "just emitted a report against it");
const Request *const PrevReq =
N->getFirstPred()->getState()->get<RequestMap>(RequestRegion);
// Check if request was previously unused or in a different state.
- if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) {
+ if (!PrevReq || (Req->CurrentState != PrevReq->CurrentState)) {
IsNodeFound = true;
ProgramPoint P = N->getFirstPred()->getLocation();
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
index 6fbc30288655c..9871da026b042 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -89,9 +89,9 @@ private:
ID.AddPointer(RequestRegion);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
const MemRegion *const RequestRegion;
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index 32ba9bc8e2ef5..e064ca6bd88f6 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -113,11 +113,14 @@ private:
const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,
CheckerContext &C) const;
- std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
- const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const;
+ std::unique_ptr<PathSensitiveBugReport>
+ generateAllocatedDataNotReleasedReport(const AllocationPair &AP,
+ ExplodedNode *N,
+ CheckerContext &C) const;
/// Mark an AllocationPair interesting for diagnostic reporting.
- void markInteresting(BugReport *R, const AllocationPair &AP) const {
+ void markInteresting(PathSensitiveBugReport *R,
+ const AllocationPair &AP) const {
R->markInteresting(AP.first);
R->markInteresting(AP.second->Region);
}
@@ -139,9 +142,9 @@ private:
ID.AddPointer(Sym);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
};
}
@@ -236,8 +239,8 @@ void MacOSKeychainAPIChecker::
os << "Deallocator doesn't match the allocator: '"
<< FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
- auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
- Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
Report->addRange(ArgExpr->getSourceRange());
markInteresting(Report.get(), AP);
C.emitReport(std::move(Report));
@@ -280,8 +283,9 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
<< "the allocator: missing a call to '"
<< FunctionsToTrack[DIdx].Name
<< "'.";
- auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
- Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
+ Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V));
Report->addRange(ArgExpr->getSourceRange());
Report->markInteresting(AS->Region);
C.emitReport(std::move(Report));
@@ -334,7 +338,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
if (!N)
return;
initBugType();
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*BT, "Trying to free data which has not been allocated.", N);
Report->addRange(ArgExpr->getSourceRange());
if (AS)
@@ -465,7 +469,7 @@ MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
return AllocNode;
}
-std::unique_ptr<BugReport>
+std::unique_ptr<PathSensitiveBugReport>
MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {
const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
@@ -480,18 +484,18 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
// allocated, and only report a single path.
PathDiagnosticLocation LocUsedForUniqueing;
const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
- const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics();
if (AllocStmt)
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
C.getSourceManager(),
AllocNode->getLocationContext());
- auto Report =
- llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
- AllocNode->getLocationContext()->getDecl());
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT, os.str(), N, LocUsedForUniqueing,
+ AllocNode->getLocationContext()->getDecl());
- Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
+ Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first));
markInteresting(Report.get(), AP);
return Report;
}
@@ -613,9 +617,10 @@ ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape(
return State;
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
+ const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
if (!AS)
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index 1c52d20d09914..d964a1668eaaa 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -139,7 +139,8 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
"API Misuse (Apple)"));
- auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
}
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index a79b341890655..a824499518730 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -6,8 +6,41 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines malloc/free checker, which checks for potential memory
-// leaks, double free, and use-after-free problems.
+// This file defines a variety of memory management related checkers, such as
+// leak, double free, and use-after-free.
+//
+// The following checkers are defined here:
+//
+// * MallocChecker
+// Despite its name, it models all sorts of memory allocations and
+// de- or reallocation, including but not limited to malloc, free,
+// relloc, new, delete. It also reports on a variety of memory misuse
+// errors.
+// Many other checkers interact very closely with this checker, in fact,
+// most are merely options to this one. Other checkers may register
+// MallocChecker, but do not enable MallocChecker's reports (more details
+// to follow around its field, ChecksEnabled).
+// It also has a boolean "Optimistic" checker option, which if set to true
+// will cause the checker to model user defined memory management related
+// functions annotated via the attribute ownership_takes, ownership_holds
+// and ownership_returns.
+//
+// * NewDeleteChecker
+// Enables the modeling of new, new[], delete, delete[] in MallocChecker,
+// and checks for related double-free and use-after-free errors.
+//
+// * NewDeleteLeaksChecker
+// Checks for leaks related to new, new[], delete, delete[].
+// Depends on NewDeleteChecker.
+//
+// * MismatchedDeallocatorChecker
+// Enables checking whether memory is deallocated with the correspending
+// allocation function in MallocChecker, such as malloc() allocated
+// regions are only freed by free(), new by delete, new[] by delete[].
+//
+// InnerPointerChecker interacts very closely with MallocChecker, but unlike
+// the above checkers, it has it's own file, hence the many InnerPointerChecker
+// related headers and non-static functions.
//
//===----------------------------------------------------------------------===//
@@ -37,6 +70,10 @@
using namespace clang;
using namespace ento;
+//===----------------------------------------------------------------------===//
+// The types of allocation we're modeling.
+//===----------------------------------------------------------------------===//
+
namespace {
// Used to check correspondence between allocators and deallocators.
@@ -50,57 +87,88 @@ enum AllocationFamily {
AF_InnerBuffer
};
+struct MemFunctionInfoTy;
+
+} // end of anonymous namespace
+
+/// Determine family of a deallocation expression.
+static AllocationFamily
+getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C,
+ const Stmt *S);
+
+/// Print names of allocators and deallocators.
+///
+/// \returns true on success.
+static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
+ const Expr *E);
+
+/// Print expected name of an allocator based on the deallocator's
+/// family derived from the DeallocExpr.
+static void printExpectedAllocName(raw_ostream &os,
+ const MemFunctionInfoTy &MemFunctionInfo,
+ CheckerContext &C, const Expr *E);
+
+/// Print expected name of a deallocator based on the allocator's
+/// family.
+static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family);
+
+//===----------------------------------------------------------------------===//
+// The state of a symbol, in terms of memory management.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
class RefState {
- enum Kind { // Reference to allocated memory.
- Allocated,
- // Reference to zero-allocated memory.
- AllocatedOfSizeZero,
- // Reference to released/freed memory.
- Released,
- // The responsibility for freeing resources has transferred from
- // this reference. A relinquished symbol should not be freed.
- Relinquished,
- // We are no longer guaranteed to have observed all manipulations
- // of this pointer/memory. For example, it could have been
- // passed as a parameter to an opaque function.
- Escaped
+ enum Kind {
+ // Reference to allocated memory.
+ Allocated,
+ // Reference to zero-allocated memory.
+ AllocatedOfSizeZero,
+ // Reference to released/freed memory.
+ Released,
+ // The responsibility for freeing resources has transferred from
+ // this reference. A relinquished symbol should not be freed.
+ Relinquished,
+ // We are no longer guaranteed to have observed all manipulations
+ // of this pointer/memory. For example, it could have been
+ // passed as a parameter to an opaque function.
+ Escaped
};
const Stmt *S;
- unsigned K : 3; // Kind enum, but stored as a bitfield.
- unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
- // family.
- RefState(Kind k, const Stmt *s, unsigned family)
- : S(s), K(k), Family(family) {
+ Kind K;
+ AllocationFamily Family;
+
+ RefState(Kind k, const Stmt *s, AllocationFamily family)
+ : S(s), K(k), Family(family) {
assert(family != AF_None);
}
+
public:
bool isAllocated() const { return K == Allocated; }
bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }
bool isReleased() const { return K == Released; }
bool isRelinquished() const { return K == Relinquished; }
bool isEscaped() const { return K == Escaped; }
- AllocationFamily getAllocationFamily() const {
- return (AllocationFamily)Family;
- }
+ AllocationFamily getAllocationFamily() const { return Family; }
const Stmt *getStmt() const { return S; }
bool operator==(const RefState &X) const {
return K == X.K && S == X.S && Family == X.Family;
}
- static RefState getAllocated(unsigned family, const Stmt *s) {
+ static RefState getAllocated(AllocationFamily family, const Stmt *s) {
return RefState(Allocated, s, family);
}
static RefState getAllocatedOfSizeZero(const RefState *RS) {
return RefState(AllocatedOfSizeZero, RS->getStmt(),
RS->getAllocationFamily());
}
- static RefState getReleased(unsigned family, const Stmt *s) {
+ static RefState getReleased(AllocationFamily family, const Stmt *s) {
return RefState(Released, s, family);
}
- static RefState getRelinquished(unsigned family, const Stmt *s) {
+ static RefState getRelinquished(AllocationFamily family, const Stmt *s) {
return RefState(Relinquished, s, family);
}
static RefState getEscaped(const RefState *RS) {
@@ -113,8 +181,8 @@ public:
ID.AddInteger(Family);
}
- void dump(raw_ostream &OS) const {
- switch (static_cast<Kind>(K)) {
+ LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
+ switch (K) {
#define CASE(ID) case ID: OS << #ID; break;
CASE(Allocated)
CASE(AllocatedOfSizeZero)
@@ -127,24 +195,62 @@ public:
LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
};
-enum ReallocPairKind {
- RPToBeFreedAfterFailure,
- // The symbol has been freed when reallocation failed.
- RPIsFreeOnFailure,
- // The symbol does not need to be freed after reallocation fails.
- RPDoNotTrackAfterFailure
+} // end of anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
+
+/// Check if the memory associated with this symbol was released.
+static bool isReleased(SymbolRef Sym, CheckerContext &C);
+
+/// Update the RefState to reflect the new memory allocation.
+/// The optional \p RetVal parameter specifies the newly allocated pointer
+/// value; if unspecified, the value of expression \p E is used.
+static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc,
+ Optional<SVal> RetVal = None);
+
+//===----------------------------------------------------------------------===//
+// The modeling of memory reallocation.
+//
+// The terminology 'toPtr' and 'fromPtr' will be used:
+// toPtr = realloc(fromPtr, 20);
+//===----------------------------------------------------------------------===//
+
+REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)
+
+namespace {
+
+/// The state of 'fromPtr' after reallocation is known to have failed.
+enum OwnershipAfterReallocKind {
+ // The symbol needs to be freed (e.g.: realloc)
+ OAR_ToBeFreedAfterFailure,
+ // The symbol has been freed (e.g.: reallocf)
+ OAR_FreeOnFailure,
+ // The symbol doesn't have to freed (e.g.: we aren't sure if, how and where
+ // 'fromPtr' was allocated:
+ // void Haha(int *ptr) {
+ // ptr = realloc(ptr, 67);
+ // // ...
+ // }
+ // ).
+ OAR_DoNotTrackAfterFailure
};
-/// \class ReallocPair
-/// Stores information about the symbol being reallocated by a call to
-/// 'realloc' to allow modeling failed reallocation later in the path.
+/// Stores information about the 'fromPtr' symbol after reallocation.
+///
+/// This is important because realloc may fail, and that needs special modeling.
+/// Whether reallocation failed or not will not be known until later, so we'll
+/// store whether upon failure 'fromPtr' will be freed, or needs to be freed
+/// later, etc.
struct ReallocPair {
- // The symbol which realloc reallocated.
+
+ // The 'fromPtr'.
SymbolRef ReallocatedSym;
- ReallocPairKind Kind;
+ OwnershipAfterReallocKind Kind;
- ReallocPair(SymbolRef S, ReallocPairKind K) :
- ReallocatedSym(S), Kind(K) {}
+ ReallocPair(SymbolRef S, OwnershipAfterReallocKind K)
+ : ReallocatedSym(S), Kind(K) {}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(Kind);
ID.AddPointer(ReallocatedSym);
@@ -155,42 +261,88 @@ struct ReallocPair {
}
};
-typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo;
-
-class MallocChecker : public Checker<check::DeadSymbols,
- check::PointerEscape,
- check::ConstPointerEscape,
- check::PreStmt<ReturnStmt>,
- check::EndFunction,
- check::PreCall,
- check::PostStmt<CallExpr>,
- check::PostStmt<CXXNewExpr>,
- check::NewAllocator,
- check::PreStmt<CXXDeleteExpr>,
- check::PostStmt<BlockExpr>,
- check::PostObjCMessage,
- check::Location,
- eval::Assume>
-{
-public:
- MallocChecker()
- : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr),
- II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr),
- II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr),
- II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr),
- II_kfree(nullptr), II_if_nameindex(nullptr),
- II_if_freenameindex(nullptr), II_wcsdup(nullptr),
- II_win_wcsdup(nullptr), II_g_malloc(nullptr), II_g_malloc0(nullptr),
- II_g_realloc(nullptr), II_g_try_malloc(nullptr),
- II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
- II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
- II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
- II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
- II_g_try_realloc_n(nullptr) {}
+} // end of anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
+
+//===----------------------------------------------------------------------===//
+// Kinds of memory operations, information about resource managing functions.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+enum class MemoryOperationKind { MOK_Allocate, MOK_Free, MOK_Any };
+struct MemFunctionInfoTy {
+ /// The value of the MallocChecker:Optimistic is stored in this variable.
+ ///
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
+ /// In optimistic mode, the checker assumes that all user-defined functions
+ /// which might free a pointer are annotated.
+ DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
+
+ // TODO: Change these to CallDescription, and get rid of lazy initialization.
+ mutable IdentifierInfo *II_alloca = nullptr, *II_win_alloca = nullptr,
+ *II_malloc = nullptr, *II_free = nullptr,
+ *II_realloc = nullptr, *II_calloc = nullptr,
+ *II_valloc = nullptr, *II_reallocf = nullptr,
+ *II_strndup = nullptr, *II_strdup = nullptr,
+ *II_win_strdup = nullptr, *II_kmalloc = nullptr,
+ *II_if_nameindex = nullptr,
+ *II_if_freenameindex = nullptr, *II_wcsdup = nullptr,
+ *II_win_wcsdup = nullptr, *II_g_malloc = nullptr,
+ *II_g_malloc0 = nullptr, *II_g_realloc = nullptr,
+ *II_g_try_malloc = nullptr,
+ *II_g_try_malloc0 = nullptr,
+ *II_g_try_realloc = nullptr, *II_g_free = nullptr,
+ *II_g_memdup = nullptr, *II_g_malloc_n = nullptr,
+ *II_g_malloc0_n = nullptr, *II_g_realloc_n = nullptr,
+ *II_g_try_malloc_n = nullptr,
+ *II_g_try_malloc0_n = nullptr, *II_kfree = nullptr,
+ *II_g_try_realloc_n = nullptr;
+
+ void initIdentifierInfo(ASTContext &C) const;
+
+ ///@{
+ /// Check if this is one of the functions which can allocate/reallocate
+ /// memory pointed to by one of its arguments.
+ bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
+ bool isCMemFunction(const FunctionDecl *FD, ASTContext &C,
+ AllocationFamily Family,
+ MemoryOperationKind MemKind) const;
+
+ /// Tells if the callee is one of the builtin new/delete operators, including
+ /// placement operators and other standard overloads.
+ bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
+ ///@}
+};
+
+} // end of anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Definition of the MallocChecker class.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class MallocChecker
+ : public Checker<check::DeadSymbols, check::PointerEscape,
+ check::ConstPointerEscape, check::PreStmt<ReturnStmt>,
+ check::EndFunction, check::PreCall,
+ check::PostStmt<CallExpr>, check::PostStmt<CXXNewExpr>,
+ check::NewAllocator, check::PreStmt<CXXDeleteExpr>,
+ check::PostStmt<BlockExpr>, check::PostObjCMessage,
+ check::Location, eval::Assume> {
+public:
+ MemFunctionInfoTy MemFunctionInfo;
+
+ /// Many checkers are essentially built into this one, so enabling them will
+ /// make MallocChecker perform additional modeling and reporting.
enum CheckKind {
+ /// When a subchecker is enabled but MallocChecker isn't, model memory
+ /// management but do not emit warnings emitted with MallocChecker only
+ /// enabled.
CK_MallocChecker,
CK_NewDeleteChecker,
CK_NewDeleteLeaksChecker,
@@ -199,16 +351,10 @@ public:
CK_NumCheckKinds
};
- enum class MemoryOperationKind {
- MOK_Allocate,
- MOK_Free,
- MOK_Any
- };
-
- DefaultBool IsOptimistic;
+ using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>;
DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckName CheckNames[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
@@ -248,47 +394,9 @@ private:
mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
- mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free,
- *II_realloc, *II_calloc, *II_valloc, *II_reallocf,
- *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc,
- *II_kfree, *II_if_nameindex, *II_if_freenameindex,
- *II_wcsdup, *II_win_wcsdup, *II_g_malloc,
- *II_g_malloc0, *II_g_realloc, *II_g_try_malloc,
- *II_g_try_malloc0, *II_g_try_realloc, *II_g_free,
- *II_g_memdup, *II_g_malloc_n, *II_g_malloc0_n,
- *II_g_realloc_n, *II_g_try_malloc_n,
- *II_g_try_malloc0_n, *II_g_try_realloc_n;
- mutable Optional<uint64_t> KernelZeroFlagVal;
-
- void initIdentifierInfo(ASTContext &C) const;
-
- /// Determine family of a deallocation expression.
- AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const;
-
- /// Print names of allocators and deallocators.
- ///
- /// \returns true on success.
- bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const;
-
- /// 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;
- /// Print expected name of a deallocator based on the allocator's
- /// family.
- void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
- ///@{
- /// Check if this is one of the functions which can allocate/reallocate memory
- /// pointed to by one of its arguments.
- bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
- bool isCMemFunction(const FunctionDecl *FD,
- ASTContext &C,
- AllocationFamily Family,
- MemoryOperationKind MemKind) const;
- bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
- ///@}
+ // TODO: Remove mutable by moving the initializtaion to the registry function.
+ mutable Optional<uint64_t> KernelZeroFlagVal;
/// Process C++ operator new()'s allocation, which is the part of C++
/// new-expression that goes before the constructor.
@@ -296,23 +404,64 @@ private:
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,
- Optional<SVal> RetVal = None) const;
-
+ ///
+ /// \param [in] E The expression that allocates memory.
+ /// \param [in] IndexOfSizeArg Index of the argument that specifies the size
+ /// of the memory that needs to be allocated. E.g. for malloc, this would be
+ /// 0.
+ /// \param [in] RetVal Specifies the newly allocated pointer value;
+ /// if unspecified, the value of expression \p E is used.
+ static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E,
+ const unsigned IndexOfSizeArg,
+ ProgramStateRef State,
+ Optional<SVal> RetVal = None);
+
+ /// Model functions with the ownership_returns attribute.
+ ///
+ /// User-defined function may have the ownership_returns attribute, which
+ /// annotates that the function returns with an object that was allocated on
+ /// the heap, and passes the ownertship to the callee.
+ ///
+ /// void __attribute((ownership_returns(malloc, 1))) *my_malloc(size_t);
+ ///
+ /// It has two parameters:
+ /// - first: name of the resource (e.g. 'malloc')
+ /// - (OPTIONAL) second: size of the allocated region
+ ///
+ /// \param [in] CE The expression that allocates memory.
+ /// \param [in] Att The ownership_returns attribute.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// \returns The ProgramState right after allocation.
ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
const CallExpr *CE,
const OwnershipAttr* Att,
ProgramStateRef State) const;
+
+ /// Models memory allocation.
+ ///
+ /// \param [in] CE The expression that allocates memory.
+ /// \param [in] SizeEx Size of the memory that needs to be allocated.
+ /// \param [in] Init The value the allocated memory needs to be initialized.
+ /// with. For example, \c calloc initializes the allocated memory to 0,
+ /// malloc leaves it undefined.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// \returns The ProgramState right after allocation.
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
const Expr *SizeEx, SVal Init,
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
+
+ /// Models memory allocation.
+ ///
+ /// \param [in] CE The expression that allocates memory.
+ /// \param [in] Size Size of the memory that needs to be allocated.
+ /// \param [in] Init The value the allocated memory needs to be initialized.
+ /// with. For example, \c calloc initializes the allocated memory to 0,
+ /// malloc leaves it undefined.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// \returns The ProgramState right after allocation.
static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
- SVal SizeEx, SVal Init,
+ SVal Size, SVal Init,
ProgramStateRef State,
AllocationFamily Family = AF_Malloc);
@@ -325,54 +474,125 @@ private:
performKernelMalloc(const CallExpr *CE, CheckerContext &C,
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,
- Optional<SVal> RetVal = None);
-
+ /// Model functions with the ownership_takes and ownership_holds attributes.
+ ///
+ /// User-defined function may have the ownership_takes and/or ownership_holds
+ /// attributes, which annotates that the function frees the memory passed as a
+ /// parameter.
+ ///
+ /// void __attribute((ownership_takes(malloc, 1))) my_free(void *);
+ /// void __attribute((ownership_holds(malloc, 1))) my_hold(void *);
+ ///
+ /// They have two parameters:
+ /// - first: name of the resource (e.g. 'malloc')
+ /// - second: index of the parameter the attribute applies to
+ ///
+ /// \param [in] CE The expression that frees memory.
+ /// \param [in] Att The ownership_takes or ownership_holds attribute.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// \returns The ProgramState right after deallocation.
ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
const OwnershipAttr* Att,
ProgramStateRef State) const;
+
+ /// Models memory deallocation.
+ ///
+ /// \param [in] CE The expression that frees memory.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// \param [in] Num Index of the argument that needs to be freed. This is
+ /// normally 0, but for custom free functions it may be different.
+ /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds
+ /// attribute.
+ /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known
+ /// to have been allocated, or in other words, the symbol to be freed was
+ /// registered as allocated by this checker. In the following case, \c ptr
+ /// isn't known to be allocated.
+ /// void Haha(int *ptr) {
+ /// ptr = realloc(ptr, 67);
+ /// // ...
+ /// }
+ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
+ /// we're modeling returns with Null on failure.
+ /// \returns The ProgramState right after deallocation.
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
- ProgramStateRef state, unsigned Num,
- bool Hold,
- bool &ReleasedAllocated,
+ ProgramStateRef State, unsigned Num, bool Hold,
+ bool &IsKnownToBeAllocated,
bool ReturnsNullOnFailure = false) const;
- ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
- const Expr *ParentExpr,
- ProgramStateRef State,
- bool Hold,
- bool &ReleasedAllocated,
+
+ /// Models memory deallocation.
+ ///
+ /// \param [in] ArgExpr The variable who's pointee needs to be freed.
+ /// \param [in] ParentExpr The expression that frees the memory.
+ /// \param [in] State The \c ProgramState right before allocation.
+ /// normally 0, but for custom free functions it may be different.
+ /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds
+ /// attribute.
+ /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known
+ /// to have been allocated, or in other words, the symbol to be freed was
+ /// registered as allocated by this checker. In the following case, \c ptr
+ /// isn't known to be allocated.
+ /// void Haha(int *ptr) {
+ /// ptr = realloc(ptr, 67);
+ /// // ...
+ /// }
+ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function
+ /// we're modeling returns with Null on failure.
+ /// \returns The ProgramState right after deallocation.
+ ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
+ const Expr *ParentExpr, ProgramStateRef State,
+ bool Hold, bool &IsKnownToBeAllocated,
bool ReturnsNullOnFailure = false) const;
+ // TODO: Needs some refactoring, as all other deallocation modeling
+ // functions are suffering from out parameters and messy code due to how
+ // realloc is handled.
+ //
+ /// Models memory reallocation.
+ ///
+ /// \param [in] CE The expression that reallocated memory
+ /// \param [in] ShouldFreeOnFail Whether if reallocation fails, the supplied
+ /// memory should be freed.
+ /// \param [in] State The \c ProgramState right before reallocation.
+ /// \param [in] SuffixWithN Whether the reallocation function we're modeling
+ /// has an '_n' suffix, such as g_realloc_n.
+ /// \returns The ProgramState right after reallocation.
ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
- bool FreesMemOnFailure,
- ProgramStateRef State,
+ bool ShouldFreeOnFail, ProgramStateRef State,
bool SuffixWithN = false) const;
+
+ /// Evaluates the buffer size that needs to be allocated.
+ ///
+ /// \param [in] Blocks The amount of blocks that needs to be allocated.
+ /// \param [in] BlockBytes The size of a block.
+ /// \returns The symbolic value of \p Blocks * \p BlockBytes.
static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
const Expr *BlockBytes);
+
+ /// Models zero initialized array allocation.
+ ///
+ /// \param [in] CE The expression that reallocated memory
+ /// \param [in] State The \c ProgramState right before reallocation.
+ /// \returns The ProgramState right after allocation.
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State);
- /// Check if the memory associated with this symbol was released.
- bool isReleased(SymbolRef Sym, CheckerContext &C) const;
-
/// See if deallocation happens in a suspicious context. If so, escape the
/// pointers that otherwise would have been deallocated and return true.
bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE,
CheckerContext &C) const;
+ /// If in \p S \p Sym is used, check whether \p Sym was already freed.
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
+ /// If in \p S \p Sym is used, check whether \p Sym was allocated as a zero
+ /// sized memory region.
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
const Stmt *S) const;
+ /// If in \p S \p Sym is being freed, check whether \p Sym was already freed.
bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
- /// Check if the function is known free memory, or if it is
+ /// Check if the function is known to free memory, or if it is
/// "interesting" and should be modeled explicitly.
///
/// \param [out] EscapingSymbol A function might not free memory in general,
@@ -386,12 +606,12 @@ private:
ProgramStateRef State,
SymbolRef &EscapingSymbol) const;
- // Implementation of the checkPointerEscape callbacks.
+ /// Implementation of the checkPointerEscape callbacks.
ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool(*CheckRefState)(const RefState*)) const;
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind,
+ bool IsConstPointerEscape) const;
// Implementation of the checkPreStmt and checkEndFunction callbacks.
void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const;
@@ -410,6 +630,7 @@ private:
///@}
static bool SummarizeValue(raw_ostream &os, SVal V);
static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
+
void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
const Expr *DeallocExpr) const;
void ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
@@ -435,143 +656,142 @@ private:
/// Find the location of the allocation for Sym on the path leading to the
/// exploded node N.
- LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C) const;
+ static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &C);
void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
+};
- /// 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 BugReporterVisitor {
- protected:
- enum NotificationMode {
- Normal,
- ReallocationFailed
- };
-
- // The allocated region symbol tracked by the main analysis.
- SymbolRef Sym;
-
- // The mode we are in, i.e. what kind of diagnostics will be emitted.
- NotificationMode Mode;
-
- // 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),
- ReleaseDestructorLC(nullptr), IsLeak(isLeak) {}
-
- static void *getTag() {
- static int Tag = 0;
- return &Tag;
- }
+//===----------------------------------------------------------------------===//
+// Definition of MallocBugVisitor.
+//===----------------------------------------------------------------------===//
- void Profile(llvm::FoldingSetNodeID &ID) const override {
- ID.AddPointer(getTag());
- ID.AddPointer(Sym);
- }
+/// 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 BugReporterVisitor {
+protected:
+ enum NotificationMode { Normal, ReallocationFailed };
- inline bool isAllocated(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> allocated. Other state (released) -> allocated.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
- (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
- (!SPrev || !(SPrev->isAllocated() ||
- SPrev->isAllocatedOfSizeZero())));
- }
+ // The allocated region symbol tracked by the main analysis.
+ SymbolRef Sym;
- inline bool isReleased(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> released. Other state (allocated) -> released.
- // 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;
- }
+ // The mode we are in, i.e. what kind of diagnostics will be emitted.
+ NotificationMode Mode;
- inline bool isRelinquished(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // Did not track -> relinquished. Other state (allocated) -> relinquished.
- return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
- isa<ObjCPropertyRefExpr>(Stmt)) &&
- (S && S->isRelinquished()) &&
- (!SPrev || !SPrev->isRelinquished()));
- }
+ // A symbol from when the primary region should have been reallocated.
+ SymbolRef FailedReallocSymbol;
- inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev,
- const Stmt *Stmt) {
- // If the expression is not a call, and the state change is
- // released -> allocated, it must be the realloc return value
- // check. If we have to handle more cases here, it might be cleaner just
- // to track this extra bit in the state itself.
- return ((!Stmt || !isa<CallExpr>(Stmt)) &&
- (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
- (SPrev && !(SPrev->isAllocated() ||
- SPrev->isAllocatedOfSizeZero())));
- }
+ // A C++ destructor stack frame in which memory was released. Used for
+ // miscellaneous false positive suppression.
+ const StackFrameContext *ReleaseDestructorLC;
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ bool IsLeak;
- std::shared_ptr<PathDiagnosticPiece>
- getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
- BugReport &BR) override {
- if (!IsLeak)
- return nullptr;
+public:
+ MallocBugVisitor(SymbolRef S, bool isLeak = false)
+ : 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 {
+ ID.AddPointer(getTag());
+ ID.AddPointer(Sym);
+ }
+
+ /// Did not track -> allocated. Other state (released) -> allocated.
+ static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev,
+ const Stmt *Stmt) {
+ return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
+ (RSCurr &&
+ (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
+ (!RSPrev ||
+ !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero())));
+ }
+
+ /// Did not track -> released. Other state (allocated) -> released.
+ /// The statement associated with the release might be missing.
+ static inline bool isReleased(const RefState *RSCurr, const RefState *RSPrev,
+ const Stmt *Stmt) {
+ bool IsReleased =
+ (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased());
+ assert(!IsReleased ||
+ (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
+ (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer));
+ return IsReleased;
+ }
+
+ /// Did not track -> relinquished. Other state (allocated) -> relinquished.
+ static inline bool isRelinquished(const RefState *RSCurr,
+ const RefState *RSPrev, const Stmt *Stmt) {
+ return (Stmt &&
+ (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
+ isa<ObjCPropertyRefExpr>(Stmt)) &&
+ (RSCurr && RSCurr->isRelinquished()) &&
+ (!RSPrev || !RSPrev->isRelinquished()));
+ }
+
+ /// If the expression is not a call, and the state change is
+ /// released -> allocated, it must be the realloc return value
+ /// check. If we have to handle more cases here, it might be cleaner just
+ /// to track this extra bit in the state itself.
+ static inline bool hasReallocFailed(const RefState *RSCurr,
+ const RefState *RSPrev,
+ const Stmt *Stmt) {
+ return ((!Stmt || !isa<CallExpr>(Stmt)) &&
+ (RSCurr &&
+ (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
+ (RSPrev &&
+ !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero())));
+ }
+
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
+
+ PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ PathSensitiveBugReport &BR) override {
+ if (!IsLeak)
+ return nullptr;
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode,
- BRC.getSourceManager());
- // Do not add the statement itself as a range in case of leak.
- return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
- false);
- }
+ PathDiagnosticLocation L = BR.getLocation();
+ // Do not add the statement itself as a range in case of leak.
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
+ false);
+ }
- private:
- class StackHintGeneratorForReallocationFailed
- : public StackHintGeneratorForSymbol {
- public:
- StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
+private:
+ class StackHintGeneratorForReallocationFailed
+ : public StackHintGeneratorForSymbol {
+ public:
+ StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
: StackHintGeneratorForSymbol(S, M) {}
- std::string getMessageForArg(const Expr *ArgE,
- unsigned ArgIndex) override {
- // Printed parameters start at 1, not 0.
- ++ArgIndex;
+ std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) override {
+ // Printed parameters start at 1, not 0.
+ ++ArgIndex;
- SmallString<200> buf;
- llvm::raw_svector_ostream os(buf);
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
- os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
- << " parameter failed";
+ os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
+ << " parameter failed";
- return os.str();
- }
+ return os.str();
+ }
- std::string getMessageForReturn(const CallExpr *CallExpr) override {
- return "Reallocation of returned value failed";
- }
- };
+ std::string getMessageForReturn(const CallExpr *CallExpr) override {
+ return "Reallocation of returned value failed";
+ }
};
};
-} // end anonymous namespace
-REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
-REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
-REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)
+} // end anonymous namespace
// A map from the freed symbol to the symbol representing the return value of
// the free function.
@@ -591,7 +811,11 @@ public:
};
} // end anonymous namespace
-void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
+//===----------------------------------------------------------------------===//
+// Methods of MemFunctionInfoTy.
+//===----------------------------------------------------------------------===//
+
+void MemFunctionInfoTy::initIdentifierInfo(ASTContext &Ctx) const {
if (II_malloc)
return;
II_alloca = &Ctx.Idents.get("alloca");
@@ -631,7 +855,8 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
}
-bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
+bool MemFunctionInfoTy::isMemFunction(const FunctionDecl *FD,
+ ASTContext &C) const {
if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any))
return true;
@@ -647,10 +872,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
return false;
}
-bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
- ASTContext &C,
- AllocationFamily Family,
- MemoryOperationKind MemKind) const {
+bool MemFunctionInfoTy::isCMemFunction(const FunctionDecl *FD, ASTContext &C,
+ AllocationFamily Family,
+ MemoryOperationKind MemKind) const {
if (!FD)
return false;
@@ -703,7 +927,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
if (Family != AF_Malloc)
return false;
- if (IsOptimistic && FD->hasAttrs()) {
+ if (ShouldIncludeOwnershipAnnotatedFunctions && FD->hasAttrs()) {
for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
@@ -718,11 +942,8 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
return false;
}
-
-// Tells if the callee is one of the builtin new/delete operators, including
-// placement operators and other standard overloads.
-bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
- ASTContext &C) const {
+bool MemFunctionInfoTy::isStandardNewDelete(const FunctionDecl *FD,
+ ASTContext &C) const {
if (!FD)
return false;
@@ -738,6 +959,10 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
return !L.isValid() || C.getSourceManager().isInSystemHeader(L);
}
+//===----------------------------------------------------------------------===//
+// Methods of MallocChecker and MallocBugVisitor.
+//===----------------------------------------------------------------------===//
+
llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
// 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
@@ -836,28 +1061,35 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
ProgramStateRef State = C.getState();
- bool ReleasedAllocatedMemory = false;
+ bool IsKnownToBeAllocatedMemory = false;
if (FD->getKind() == Decl::Function) {
- initIdentifierInfo(C.getASTContext());
+ MemFunctionInfo.initIdentifierInfo(C.getASTContext());
IdentifierInfo *FunI = FD->getIdentifier();
- if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) {
- if (CE->getNumArgs() < 1)
+ if (FunI == MemFunctionInfo.II_malloc ||
+ FunI == MemFunctionInfo.II_g_malloc ||
+ FunI == MemFunctionInfo.II_g_try_malloc) {
+ switch (CE->getNumArgs()) {
+ default:
return;
- if (CE->getNumArgs() < 3) {
+ case 1:
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ break;
+ case 2:
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- if (CE->getNumArgs() == 1)
- State = ProcessZeroAllocation(C, CE, 0, State);
- } else if (CE->getNumArgs() == 3) {
+ break;
+ case 3:
llvm::Optional<ProgramStateRef> MaybeState =
performKernelMalloc(CE, C, State);
if (MaybeState.hasValue())
State = MaybeState.getValue();
else
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ break;
}
- } else if (FunI == II_kmalloc) {
+ } else if (FunI == MemFunctionInfo.II_kmalloc) {
if (CE->getNumArgs() < 1)
return;
llvm::Optional<ProgramStateRef> MaybeState =
@@ -866,100 +1098,116 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
State = MaybeState.getValue();
else
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- } else if (FunI == II_valloc) {
+ } else if (FunI == MemFunctionInfo.II_valloc) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
- State = ProcessZeroAllocation(C, CE, 0, State);
- } else if (FunI == II_realloc || FunI == II_g_realloc ||
- FunI == II_g_try_realloc) {
- State = ReallocMemAux(C, CE, false, State);
- State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_reallocf) {
- State = ReallocMemAux(C, CE, true, State);
- State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_calloc) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ } else if (FunI == MemFunctionInfo.II_realloc ||
+ FunI == MemFunctionInfo.II_g_realloc ||
+ FunI == MemFunctionInfo.II_g_try_realloc) {
+ State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ } else if (FunI == MemFunctionInfo.II_reallocf) {
+ State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ } else if (FunI == MemFunctionInfo.II_calloc) {
State = CallocMem(C, CE, State);
- State = ProcessZeroAllocation(C, CE, 0, State);
- State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ } else if (FunI == MemFunctionInfo.II_free ||
+ FunI == MemFunctionInfo.II_g_free ||
+ FunI == MemFunctionInfo.II_kfree) {
if (suppressDeallocationsInSuspiciousContexts(CE, C))
return;
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- } else if (FunI == II_strdup || FunI == II_win_strdup ||
- FunI == II_wcsdup || FunI == II_win_wcsdup) {
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
+ } else if (FunI == MemFunctionInfo.II_strdup ||
+ FunI == MemFunctionInfo.II_win_strdup ||
+ FunI == MemFunctionInfo.II_wcsdup ||
+ FunI == MemFunctionInfo.II_win_wcsdup) {
State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == II_strndup) {
+ } else if (FunI == MemFunctionInfo.II_strndup) {
State = MallocUpdateRefState(C, CE, State);
- } else if (FunI == II_alloca || FunI == II_win_alloca) {
+ } else if (FunI == MemFunctionInfo.II_alloca ||
+ FunI == MemFunctionInfo.II_win_alloca) {
if (CE->getNumArgs() < 1)
return;
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_Alloca);
- State = ProcessZeroAllocation(C, CE, 0, State);
- } else if (isStandardNewDelete(FD, C.getASTContext())) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) {
// Process direct calls to operator new/new[]/delete/delete[] functions
// as distinct from new/new[]/delete/delete[] expressions that are
// processed by the checkPostStmt callbacks for CXXNewExpr and
// CXXDeleteExpr.
- OverloadedOperatorKind K = FD->getOverloadedOperator();
- if (K == OO_New) {
+ switch (FD->getOverloadedOperator()) {
+ case OO_New:
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNew);
- State = ProcessZeroAllocation(C, CE, 0, State);
- }
- else if (K == OO_Array_New) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ break;
+ case OO_Array_New:
State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
AF_CXXNewArray);
- State = ProcessZeroAllocation(C, CE, 0, State);
- }
- else if (K == OO_Delete || K == OO_Array_Delete)
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- else
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ break;
+ case OO_Delete:
+ case OO_Array_Delete:
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
+ break;
+ default:
llvm_unreachable("not a new/delete operator");
- } else if (FunI == II_if_nameindex) {
+ }
+ } else if (FunI == MemFunctionInfo.II_if_nameindex) {
// Should we model this differently? We can allocate a fixed number of
// elements with zeros in the last one.
State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State,
AF_IfNameIndex);
- } else if (FunI == II_if_freenameindex) {
- State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
- } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) {
+ } else if (FunI == MemFunctionInfo.II_if_freenameindex) {
+ State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory);
+ } else if (FunI == MemFunctionInfo.II_g_malloc0 ||
+ FunI == MemFunctionInfo.II_g_try_malloc0) {
if (CE->getNumArgs() < 1)
return;
SValBuilder &svalBuilder = C.getSValBuilder();
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State);
- State = ProcessZeroAllocation(C, CE, 0, State);
- } else if (FunI == II_g_memdup) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ } else if (FunI == MemFunctionInfo.II_g_memdup) {
if (CE->getNumArgs() < 2)
return;
State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
- State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
- FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ } else if (FunI == MemFunctionInfo.II_g_malloc_n ||
+ FunI == MemFunctionInfo.II_g_try_malloc_n ||
+ FunI == MemFunctionInfo.II_g_malloc0_n ||
+ FunI == MemFunctionInfo.II_g_try_malloc0_n) {
if (CE->getNumArgs() < 2)
return;
SVal Init = UndefinedVal();
- if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ if (FunI == MemFunctionInfo.II_g_malloc0_n ||
+ FunI == MemFunctionInfo.II_g_try_malloc0_n) {
SValBuilder &SB = C.getSValBuilder();
Init = SB.makeZeroVal(SB.getContext().CharTy);
}
SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
State = MallocMemAux(C, CE, TotalSize, Init, State);
- State = ProcessZeroAllocation(C, CE, 0, State);
- State = ProcessZeroAllocation(C, CE, 1, State);
- } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) {
+ State = ProcessZeroAllocCheck(C, CE, 0, State);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ } else if (FunI == MemFunctionInfo.II_g_realloc_n ||
+ FunI == MemFunctionInfo.II_g_try_realloc_n) {
if (CE->getNumArgs() < 3)
return;
- State = ReallocMemAux(C, CE, false, State, true);
- State = ProcessZeroAllocation(C, CE, 1, State);
- State = ProcessZeroAllocation(C, CE, 2, State);
+ State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State,
+ /*SuffixWithN*/ true);
+ State = ProcessZeroAllocCheck(C, CE, 1, State);
+ State = ProcessZeroAllocCheck(C, CE, 2, State);
}
}
- if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions ||
+ ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
// Check all the attributes, if there are any.
// There can be multiple of these attributes.
if (FD->hasAttrs())
@@ -979,9 +1227,9 @@ 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, Optional<SVal> RetVal) const {
+ProgramStateRef MallocChecker::ProcessZeroAllocCheck(
+ CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg,
+ ProgramStateRef State, Optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -991,7 +1239,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(
const Expr *Arg = nullptr;
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
- Arg = CE->getArg(AllocationSizeArg);
+ Arg = CE->getArg(IndexOfSizeArg);
}
else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
if (NE->isArray())
@@ -1053,7 +1301,9 @@ static QualType getDeepPointeeType(QualType T) {
return Result;
}
-static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
+/// \returns true if the constructor invoked by \p NE has an argument of a
+/// pointer/reference to a record type.
+static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) {
const CXXConstructExpr *ConstructE = NE->getConstructExpr();
if (!ConstructE)
@@ -1083,11 +1333,17 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
CheckerContext &C,
SVal Target) const {
- if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
+ if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(),
+ C.getASTContext()))
return;
- ParentMap &PM = C.getLocationContext()->getParentMap();
- if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE))
+ const ParentMap &PM = C.getLocationContext()->getParentMap();
+
+ // Non-trivial constructors have a chance to escape 'this', but marking all
+ // invocations of trivial constructors as escaped would cause too great of
+ // reduction of true positives, so let's just do that for constructors that
+ // have an argument of a pointer-to-record type.
+ if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE))
return;
ProgramStateRef State = C.getState();
@@ -1098,7 +1354,7 @@ void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
: AF_CXXNew, Target);
State = addExtentSize(C, NE, State, Target);
- State = ProcessZeroAllocation(C, NE, 0, State, Target);
+ State = ProcessZeroAllocCheck(C, NE, 0, State, Target);
C.addTransition(State);
}
@@ -1132,14 +1388,13 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
// Store the extent size for the (symbolic)region
// containing the elements.
Region = Target.getAsRegion()
- ->getAs<SubRegion>()
+ ->castAs<SubRegion>()
->StripCasts()
- ->getAs<SubRegion>();
+ ->castAs<SubRegion>();
} else {
ElementCount = svalBuilder.makeIntVal(1, true);
- Region = Target.getAsRegion()->getAs<SubRegion>();
+ Region = Target.getAsRegion()->castAs<SubRegion>();
}
- assert(Region);
// Set the region's extent equal to the Size in Bytes.
QualType ElementType = NE->getAllocatedType();
@@ -1167,13 +1422,14 @@ void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
checkUseAfterFree(Sym, C, DE->getArgument());
- if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
+ if (!MemFunctionInfo.isStandardNewDelete(DE->getOperatorDelete(),
+ C.getASTContext()))
return;
ProgramStateRef State = C.getState();
- bool ReleasedAllocated;
+ bool IsKnownToBeAllocated;
State = FreeMemAux(C, DE->getArgument(), DE, State,
- /*Hold*/false, ReleasedAllocated);
+ /*Hold*/ false, IsKnownToBeAllocated);
C.addTransition(State);
}
@@ -1213,11 +1469,11 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
if (!*FreeWhenDone)
return;
- bool ReleasedAllocatedMemory;
- ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0),
- Call.getOriginExpr(), C.getState(),
- /*Hold=*/true, ReleasedAllocatedMemory,
- /*ReturnsNullOnFailure=*/true);
+ bool IsKnownToBeAllocatedMemory;
+ ProgramStateRef State =
+ FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(),
+ /*Hold=*/true, IsKnownToBeAllocatedMemory,
+ /*RetNullOnFailure=*/true);
C.addTransition(State);
}
@@ -1229,7 +1485,7 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
if (!State)
return nullptr;
- if (Att->getModule() != II_malloc)
+ if (Att->getModule() != MemFunctionInfo.II_malloc)
return nullptr;
OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
@@ -1295,11 +1551,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
return MallocUpdateRefState(C, CE, State, Family);
}
-ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
- const Expr *E,
- ProgramStateRef State,
- AllocationFamily Family,
- Optional<SVal> RetVal) {
+static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E,
+ ProgramStateRef State,
+ AllocationFamily Family,
+ Optional<SVal> RetVal) {
if (!State)
return nullptr;
@@ -1327,27 +1582,24 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
if (!State)
return nullptr;
- if (Att->getModule() != II_malloc)
+ if (Att->getModule() != MemFunctionInfo.II_malloc)
return nullptr;
- bool ReleasedAllocated = false;
+ bool IsKnownToBeAllocated = false;
for (const auto &Arg : Att->args()) {
ProgramStateRef StateI = FreeMemAux(
C, CE, State, Arg.getASTIndex(),
- Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
+ Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated);
if (StateI)
State = StateI;
}
return State;
}
-ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
- const CallExpr *CE,
- ProgramStateRef State,
- unsigned Num,
- bool Hold,
- bool &ReleasedAllocated,
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State, unsigned Num,
+ bool Hold, bool &IsKnownToBeAllocated,
bool ReturnsNullOnFailure) const {
if (!State)
return nullptr;
@@ -1355,8 +1607,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
if (CE->getNumArgs() < (Num + 1))
return nullptr;
- return FreeMemAux(C, CE->getArg(Num), CE, State, Hold,
- ReleasedAllocated, ReturnsNullOnFailure);
+ return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated,
+ ReturnsNullOnFailure);
}
/// Checks if the previous call to free on the given symbol failed - if free
@@ -1374,8 +1626,10 @@ static bool didPreviousFreeFail(ProgramStateRef State,
return false;
}
-AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
- const Stmt *S) const {
+static AllocationFamily
+getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C,
+ const Stmt *S) {
+
if (!S)
return AF_None;
@@ -1387,10 +1641,11 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
ASTContext &Ctx = C.getASTContext();
- if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any))
+ if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc,
+ MemoryOperationKind::MOK_Any))
return AF_Malloc;
- if (isStandardNewDelete(FD, Ctx)) {
+ if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) {
OverloadedOperatorKind Kind = FD->getOverloadedOperator();
if (Kind == OO_New || Kind == OO_Delete)
return AF_CXXNew;
@@ -1398,10 +1653,12 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
return AF_CXXNewArray;
}
- if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
+ if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
+ MemoryOperationKind::MOK_Any))
return AF_IfNameIndex;
- if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any))
+ if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca,
+ MemoryOperationKind::MOK_Any))
return AF_Alloca;
return AF_None;
@@ -1419,8 +1676,8 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
return AF_None;
}
-bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const {
+static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
+ const Expr *E) {
if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
// FIXME: This doesn't handle indirect calls.
const FunctionDecl *FD = CE->getDirectCallee();
@@ -1459,9 +1716,10 @@ bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
return false;
}
-void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
- const Expr *E) const {
- AllocationFamily Family = getAllocationFamily(C, E);
+static void printExpectedAllocName(raw_ostream &os,
+ const MemFunctionInfoTy &MemFunctionInfo,
+ CheckerContext &C, const Expr *E) {
+ AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E);
switch(Family) {
case AF_Malloc: os << "malloc()"; return;
@@ -1474,8 +1732,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
}
}
-void MallocChecker::printExpectedDeallocName(raw_ostream &os,
- AllocationFamily Family) const {
+static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
switch(Family) {
case AF_Malloc: os << "free()"; return;
case AF_CXXNew: os << "'delete'"; return;
@@ -1490,9 +1747,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os,
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const Expr *ArgExpr,
const Expr *ParentExpr,
- ProgramStateRef State,
- bool Hold,
- bool &ReleasedAllocated,
+ ProgramStateRef State, bool Hold,
+ bool &IsKnownToBeAllocated,
bool ReturnsNullOnFailure) const {
if (!State)
@@ -1566,6 +1822,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const RefState *RsBase = State->get<RegionState>(SymBase);
SymbolRef PreviousRetStatusSymbol = nullptr;
+ IsKnownToBeAllocated =
+ RsBase && (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero());
+
if (RsBase) {
// Memory returned by alloca() shouldn't be freed.
@@ -1588,7 +1847,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
// Check if an expected deallocation function matches the real one.
bool DeallocMatchesAlloc =
- RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
+ RsBase->getAllocationFamily() ==
+ getAllocationFamily(MemFunctionInfo, C, ParentExpr);
if (!DeallocMatchesAlloc) {
ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
ParentExpr, RsBase, SymBase, Hold);
@@ -1614,9 +1874,6 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
return nullptr;
}
- ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
- RsBase->isAllocatedOfSizeZero());
-
// Clean out the info on previous call to free return info.
State = State->remove<FreeReturnValue>(SymBase);
@@ -1631,8 +1888,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
}
}
- AllocationFamily Family = RsBase ? RsBase->getAllocationFamily()
- : getAllocationFamily(C, ParentExpr);
+ AllocationFamily Family =
+ RsBase ? RsBase->getAllocationFamily()
+ : getAllocationFamily(MemFunctionInfo, C, ParentExpr);
// Normal free.
if (Hold)
return State->set<RegionState>(SymBase,
@@ -1682,8 +1940,8 @@ Optional<MallocChecker::CheckKind>
MallocChecker::getCheckIfTracked(CheckerContext &C,
const Stmt *AllocDeallocStmt,
bool IsALeakCheck) const {
- return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt),
- IsALeakCheck);
+ return getCheckIfTracked(
+ getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck);
}
Optional<MallocChecker::CheckKind>
@@ -1821,9 +2079,10 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
else
os << "not memory allocated by ";
- printExpectedAllocName(os, C, DeallocExpr);
+ printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr);
- auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind],
+ os.str(), N);
R->markInteresting(MR);
R->addRange(Range);
C.emitReport(std::move(R));
@@ -1847,7 +2106,7 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
BT_FreeAlloca[*CheckKind].reset(new BugType(
CheckNames[*CheckKind], "Free alloca()", categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BT_FreeAlloca[*CheckKind],
"Memory allocated by alloca() should not be deallocated", N);
R->markInteresting(ArgVal.getAsRegion());
@@ -1903,10 +2162,11 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
os << ", not " << DeallocOs.str();
}
- auto R = llvm::make_unique<BugReport>(*BT_MismatchedDealloc, os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc,
+ os.str(), N);
R->markInteresting(Sym);
R->addRange(Range);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym));
C.emitReport(std::move(R));
}
}
@@ -1962,7 +2222,8 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
else
os << "allocated memory";
- auto R = llvm::make_unique<BugReport>(*BT_OffsetFree[*CheckKind], os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_OffsetFree[*CheckKind],
+ os.str(), N);
R->markInteresting(MR->getBaseRegion());
R->addRange(Range);
C.emitReport(std::move(R));
@@ -1988,15 +2249,16 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
AllocationFamily AF =
C.getState()->get<RegionState>(Sym)->getAllocationFamily();
- auto R = llvm::make_unique<BugReport>(*BT_UseFree[*CheckKind],
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT_UseFree[*CheckKind],
AF == AF_InnerBuffer
- ? "Inner pointer of container used after re/deallocation"
- : "Use of memory after it is freed",
+ ? "Inner pointer of container used after re/deallocation"
+ : "Use of memory after it is freed",
N);
R->markInteresting(Sym);
R->addRange(Range);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym));
if (AF == AF_InnerBuffer)
R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));
@@ -2022,7 +2284,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
BT_DoubleFree[*CheckKind].reset(new BugType(
CheckNames[*CheckKind], "Double free", categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BT_DoubleFree[*CheckKind],
(Released ? "Attempt to free released memory"
: "Attempt to free non-owned memory"),
@@ -2031,7 +2293,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
R->markInteresting(Sym);
if (PrevSym)
R->markInteresting(PrevSym);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym));
C.emitReport(std::move(R));
}
}
@@ -2051,11 +2313,11 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
"Double delete",
categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BT_DoubleDelete, "Attempt to delete released memory", N);
R->markInteresting(Sym);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym));
C.emitReport(std::move(R));
}
}
@@ -2079,13 +2341,13 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
new BugType(CheckNames[*CheckKind], "Use of zero allocated",
categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(*BT_UseZerroAllocated[*CheckKind],
- "Use of zero-allocated memory", N);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N);
R->addRange(Range);
if (Sym) {
R->markInteresting(Sym);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym));
}
C.emitReport(std::move(R));
}
@@ -2119,7 +2381,8 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
Os << " is a function pointer";
- auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], Os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind],
+ Os.str(), N);
R->markInteresting(MR);
R->addRange(Range);
C.emitReport(std::move(R));
@@ -2128,7 +2391,7 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
const CallExpr *CE,
- bool FreesOnFail,
+ bool ShouldFreeOnFail,
ProgramStateRef State,
bool SuffixWithN) const {
if (!State)
@@ -2193,33 +2456,32 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
if (!FromPtr || !ToPtr)
return nullptr;
- bool ReleasedAllocated = false;
+ bool IsKnownToBeAllocated = false;
// If the size is 0, free the memory.
if (SizeIsZero)
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
- false, ReleasedAllocated)){
- // The semantics of the return value are:
- // If size was equal to 0, either NULL or a pointer suitable to be passed
- // to free() is returned. We just free the input pointer and do not add
- // any constrains on the output pointer.
+ // The semantics of the return value are:
+ // If size was equal to 0, either NULL or a pointer suitable to be passed
+ // to free() is returned. We just free the input pointer and do not add
+ // any constrains on the output pointer.
+ if (ProgramStateRef stateFree =
+ FreeMemAux(C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated))
return stateFree;
- }
// Default behavior.
if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
+ FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) {
ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
UnknownVal(), stateFree);
if (!stateRealloc)
return nullptr;
- ReallocPairKind Kind = RPToBeFreedAfterFailure;
- if (FreesOnFail)
- Kind = RPIsFreeOnFailure;
- else if (!ReleasedAllocated)
- Kind = RPDoNotTrackAfterFailure;
+ OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure;
+ if (ShouldFreeOnFail)
+ Kind = OAR_FreeOnFailure;
+ else if (!IsKnownToBeAllocated)
+ Kind = OAR_DoNotTrackAfterFailure;
// Record the info about the reallocated symbol so that we could properly
// process failed reallocation.
@@ -2247,9 +2509,9 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
}
-LeakInfo
-MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
- CheckerContext &C) const {
+MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,
+ SymbolRef Sym,
+ CheckerContext &C) {
const LocationContext *LeakContext = N->getLocationContext();
// Walk the ExplodedGraph backwards and find the first node that referred to
// the tracked symbol.
@@ -2329,7 +2591,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
const MemRegion *Region = nullptr;
std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
- const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics();
if (AllocationStmt)
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
C.getSourceManager(),
@@ -2344,11 +2606,11 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
os << "Potential memory leak";
}
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing,
AllocNode->getLocationContext()->getDecl());
R->markInteresting(Sym);
- R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym, true));
+ R->addVisitor(std::make_unique<MallocBugVisitor>(Sym, true));
C.emitReport(std::move(R));
}
@@ -2430,9 +2692,10 @@ void MallocChecker::checkPreCall(const CallEvent &Call,
ASTContext &Ctx = C.getASTContext();
if (ChecksEnabled[CK_MallocChecker] &&
- (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||
- isCMemFunction(FD, Ctx, AF_IfNameIndex,
- MemoryOperationKind::MOK_Free)))
+ (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc,
+ MemoryOperationKind::MOK_Free) ||
+ MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex,
+ MemoryOperationKind::MOK_Free)))
return;
}
@@ -2535,7 +2798,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE,
C.addTransition(state);
}
-bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
+static bool isReleased(SymbolRef Sym, CheckerContext &C) {
assert(Sym);
const RefState *RS = C.getState()->get<RegionState>(Sym);
return (RS && RS->isReleased());
@@ -2640,13 +2903,17 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
SymbolRef ReallocSym = I.getData().ReallocatedSym;
if (const RefState *RS = state->get<RegionState>(ReallocSym)) {
if (RS->isReleased()) {
- if (I.getData().Kind == RPToBeFreedAfterFailure)
+ switch (I.getData().Kind) {
+ case OAR_ToBeFreedAfterFailure:
state = state->set<RegionState>(ReallocSym,
RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
- else if (I.getData().Kind == RPDoNotTrackAfterFailure)
+ break;
+ case OAR_DoNotTrackAfterFailure:
state = state->remove<RegionState>(ReallocSym);
- else
- assert(I.getData().Kind == RPIsFreeOnFailure);
+ break;
+ default:
+ assert(I.getData().Kind == OAR_FreeOnFailure);
+ }
}
}
state = state->remove<ReallocPairs>(I.getKey());
@@ -2729,7 +2996,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// If it's one of the allocation functions we can reason about, we model
// its behavior explicitly.
- if (isMemFunction(FD, ASTC))
+ if (MemFunctionInfo.isMemFunction(FD, ASTC))
return false;
// If it's not a system call, assume it frees memory.
@@ -2821,35 +3088,32 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return false;
}
-static bool retTrue(const RefState *RS) {
- return true;
-}
-
-static bool checkIfNewOrNewArrayFamily(const RefState *RS) {
- return (RS->getAllocationFamily() == AF_CXXNewArray ||
- RS->getAllocationFamily() == AF_CXXNew);
-}
-
ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const {
- return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue);
+ return checkPointerEscapeAux(State, Escaped, Call, Kind,
+ /*IsConstPointerEscape*/ false);
}
ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
const InvalidatedSymbols &Escaped,
const CallEvent *Call,
PointerEscapeKind Kind) const {
+ // If a const pointer escapes, it may not be freed(), but it could be deleted.
return checkPointerEscapeAux(State, Escaped, Call, Kind,
- &checkIfNewOrNewArrayFamily);
+ /*IsConstPointerEscape*/ true);
}
-ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
- const InvalidatedSymbols &Escaped,
- const CallEvent *Call,
- PointerEscapeKind Kind,
- bool(*CheckRefState)(const RefState*)) const {
+static bool checkIfNewOrNewArrayFamily(const RefState *RS) {
+ return (RS->getAllocationFamily() == AF_CXXNewArray ||
+ RS->getAllocationFamily() == AF_CXXNew);
+}
+
+ProgramStateRef MallocChecker::checkPointerEscapeAux(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind,
+ bool IsConstPointerEscape) const {
// If we know that the call does not free memory, or we want to process the
// call later, keep tracking the top level arguments.
SymbolRef EscapingSymbol = nullptr;
@@ -2868,12 +3132,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
if (EscapingSymbol && EscapingSymbol != sym)
continue;
- if (const RefState *RS = State->get<RegionState>(sym)) {
- if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
- CheckRefState(RS)) {
- State = State->set<RegionState>(sym, RefState::getEscaped(RS));
- }
- }
+ if (const RefState *RS = State->get<RegionState>(sym))
+ if (RS->isAllocated() || RS->isAllocatedOfSizeZero())
+ if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS))
+ State = State->set<RegionState>(sym, RefState::getEscaped(RS));
}
return State;
}
@@ -2883,9 +3145,8 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
ReallocPairsTy currMap = currState->get<ReallocPairs>();
ReallocPairsTy prevMap = prevState->get<ReallocPairs>();
- for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end();
- I != E; ++I) {
- SymbolRef sym = I.getKey();
+ for (const ReallocPairsTy::value_type &Pair : prevMap) {
+ SymbolRef sym = Pair.first;
if (!currMap.lookup(sym))
return sym;
}
@@ -2906,19 +3167,19 @@ static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) {
return false;
}
-std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
-
+PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
ProgramStateRef state = N->getState();
ProgramStateRef statePrev = N->getFirstPred()->getState();
- const RefState *RS = state->get<RegionState>(Sym);
+ const RefState *RSCurr = state->get<RegionState>(Sym);
const RefState *RSPrev = statePrev->get<RegionState>(Sym);
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
// When dealing with containers, we sometimes want to give a note
// even if the statement is missing.
- if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
+ if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer))
return nullptr;
const LocationContext *CurrentLC = N->getLocationContext();
@@ -2948,17 +3209,17 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
// Find out if this is an interesting point and what is the kind.
StringRef Msg;
- StackHintGeneratorForSymbol *StackHint = nullptr;
+ std::unique_ptr<StackHintGeneratorForSymbol> StackHint = nullptr;
SmallString<256> Buf;
llvm::raw_svector_ostream OS(Buf);
if (Mode == Normal) {
- if (isAllocated(RS, RSPrev, S)) {
+ if (isAllocated(RSCurr, RSPrev, S)) {
Msg = "Memory is allocated";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returned allocated memory");
- } else if (isReleased(RS, RSPrev, S)) {
- const auto Family = RS->getAllocationFamily();
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returned allocated memory");
+ } else if (isReleased(RSCurr, RSPrev, S)) {
+ const auto Family = RSCurr->getAllocationFamily();
switch (Family) {
case AF_Alloca:
case AF_Malloc:
@@ -2966,8 +3227,8 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
case AF_CXXNewArray:
case AF_IfNameIndex:
Msg = "Memory is released";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returning; memory was released");
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returning; memory was released");
break;
case AF_InnerBuffer: {
const MemRegion *ObjRegion =
@@ -2978,11 +3239,11 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
OS << "deallocated by call to destructor";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returning; inner buffer was deallocated");
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returning; inner buffer was deallocated");
} else {
OS << "reallocated by call to '";
- const Stmt *S = RS->getStmt();
+ const Stmt *S = RSCurr->getStmt();
if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
OS << MemCallE->getMethodDecl()->getNameAsString();
} else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
@@ -2994,8 +3255,8 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
OS << (D ? D->getNameAsString() : "unknown");
}
OS << "'";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returning; inner buffer was reallocated");
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returning; inner buffer was reallocated");
}
Msg = OS.str();
break;
@@ -3033,14 +3294,14 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
}
}
}
- } else if (isRelinquished(RS, RSPrev, S)) {
+ } else if (isRelinquished(RSCurr, RSPrev, S)) {
Msg = "Memory ownership is transferred";
- StackHint = new StackHintGeneratorForSymbol(Sym, "");
- } else if (isReallocFailedCheck(RS, RSPrev, S)) {
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(Sym, "");
+ } else if (hasReallocFailed(RSCurr, RSPrev, S)) {
Mode = ReallocationFailed;
Msg = "Reallocation failed";
- StackHint = new StackHintGeneratorForReallocationFailed(Sym,
- "Reallocation failed");
+ StackHint = std::make_unique<StackHintGeneratorForReallocationFailed>(
+ Sym, "Reallocation failed");
if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {
// Is it possible to fail two reallocs WITHOUT testing in between?
@@ -3059,21 +3320,24 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
if (!statePrev->get<RegionState>(FailedReallocSymbol)) {
// We're at the reallocation point.
Msg = "Attempt to reallocate memory";
- StackHint = new StackHintGeneratorForSymbol(Sym,
- "Returned reallocated memory");
+ StackHint = std::make_unique<StackHintGeneratorForSymbol>(
+ Sym, "Returned reallocated memory");
FailedReallocSymbol = nullptr;
Mode = Normal;
}
}
- if (Msg.empty())
+ if (Msg.empty()) {
+ assert(!StackHint);
return nullptr;
+ }
+
assert(StackHint);
// Generate the extra diagnostic.
PathDiagnosticLocation Pos;
if (!S) {
- assert(RS->getAllocationFamily() == AF_InnerBuffer);
+ assert(RSCurr->getAllocationFamily() == AF_InnerBuffer);
auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();
if (!PostImplCall)
return nullptr;
@@ -3084,7 +3348,9 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
N->getLocationContext());
}
- return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint);
+ auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
+ BR.addCallStackHint(P, std::move(StackHint));
+ return P;
}
void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
@@ -3131,13 +3397,13 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
MallocChecker *checker = mgr.getChecker<MallocChecker>();
checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
- mgr.getCurrentCheckName();
+ mgr.getCurrentCheckerName();
}
void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
auto *checker = mgr.registerChecker<MallocChecker>();
- checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
- checker, "Optimistic");
+ checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
}
bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) {
@@ -3148,12 +3414,11 @@ bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) {
void ento::register##name(CheckerManager &mgr) { \
MallocChecker *checker = mgr.getChecker<MallocChecker>(); \
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
- checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ checker->CheckNames[MallocChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
} \
\
- bool ento::shouldRegister##name(const LangOptions &LO) { \
- return true; \
- }
+ bool ento::shouldRegister##name(const LangOptions &LO) { return true; }
REGISTER_CHECKER(MallocChecker)
REGISTER_CHECKER(NewDeleteChecker)
diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index 2eb4d7141e284..b5881a9e65338 100644
--- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -183,7 +183,7 @@ public:
QualType CastedType = i->CastedExpr->getType();
if (!CastedType->isPointerType())
continue;
- QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType();
+ QualType PointeeType = CastedType->getPointeeType();
if (PointeeType->isVoidType())
continue;
diff --git a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index 270efede83858..ceea62160545e 100644
--- a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -67,7 +67,7 @@ void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*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);
diff --git a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index d8a9af78536a0..1473c05d7e3fc 100644
--- a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -169,9 +169,9 @@ private:
// in the first place.
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
const MoveChecker &Chk;
@@ -270,9 +270,10 @@ static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) {
return MR;
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
// We need only the last move of the reported object's region.
// The visitor walks the ExplodedGraph backwards.
if (Found)
@@ -288,7 +289,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
// Retrieve the associated statement.
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
Found = true;
@@ -400,7 +401,7 @@ ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
PathDiagnosticLocation LocUsedForUniqueing;
const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
- if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode))
+ if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
@@ -428,10 +429,10 @@ ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
break;
}
- auto R =
- llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing,
- MoveNode->getLocationContext()->getDecl());
- R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT, OS.str(), N, LocUsedForUniqueing,
+ MoveNode->getLocationContext()->getDecl());
+ R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
C.emitReport(std::move(R));
return N;
}
diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index 6fc7c17bc42fb..41b7fe5e43b6f 100644
--- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -67,9 +67,11 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
return;
}
- auto Report = llvm::make_unique<BugReport>(
- *BT, "Use -drain instead of -release when using NSAutoreleasePool and "
- "garbage collection", N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT,
+ "Use -drain instead of -release when using NSAutoreleasePool and "
+ "garbage collection",
+ N);
Report->addRange(msg.getSourceRange());
C.emitReport(std::move(Report));
}
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 5cec012258c15..85370bf133cd7 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -273,7 +273,8 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
CFBT.reset(new CFErrorDerefBug(this));
bug = CFBT.get();
}
- BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode));
+ BR.emitReport(
+ std::make_unique<PathSensitiveBugReport>(*bug, os.str(), event.SinkNode));
}
static bool IsNSError(QualType T, IdentifierInfo *II) {
diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index bf6b3e3e87cf8..6ffc89745365c 100644
--- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -35,9 +35,11 @@ public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
- std::unique_ptr<BugReport>
- genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
- std::unique_ptr<BugReport>
+ std::unique_ptr<PathSensitiveBugReport>
+ genReportNullAttrNonNull(const ExplodedNode *ErrorN,
+ const Expr *ArgE,
+ unsigned IdxOfArg) const;
+ std::unique_ptr<PathSensitiveBugReport>
genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
const Expr *ArgE) const;
};
@@ -143,7 +145,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
std::unique_ptr<BugReport> R;
if (haveAttrNonNull)
- R = genReportNullAttrNonNull(errorNode, ArgE);
+ R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
else if (haveRefTypeParam)
R = genReportReferenceToNullPointer(errorNode, ArgE);
@@ -177,9 +179,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
C.addTransition(state);
}
-std::unique_ptr<BugReport>
+std::unique_ptr<PathSensitiveBugReport>
NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
- const Expr *ArgE) const {
+ const Expr *ArgE,
+ unsigned IdxOfArg) const {
// Lazily allocate the BugType object if it hasn't already been
// created. Ownership is transferred to the BugReporter object once
// the BugReport is passed to 'EmitWarning'.
@@ -187,21 +190,27 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
BTAttrNonNull.reset(new BugType(
this, "Argument with 'nonnull' attribute passed null", "API"));
- auto R = llvm::make_unique<BugReport>(
- *BTAttrNonNull,
- "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
+ llvm::SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Null pointer passed to "
+ << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
+ << " parameter expecting 'nonnull'";
+
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode);
if (ArgE)
bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
return R;
}
-std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
+std::unique_ptr<PathSensitiveBugReport>
+NonNullParamChecker::genReportReferenceToNullPointer(
const ExplodedNode *ErrorNode, const Expr *ArgE) const {
if (!BTNullRefArg)
BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BTNullRefArg, "Forming reference to null pointer", ErrorNode);
if (ArgE) {
const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index af21c84b995b4..4322ac207112c 100644
--- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -112,11 +112,11 @@ public:
DefaultBool CheckNullablePassedToNonnull;
DefaultBool CheckNullableReturnedFromNonnull;
- CheckName CheckNameNullPassedToNonnull;
- CheckName CheckNameNullReturnedFromNonnull;
- CheckName CheckNameNullableDereferenced;
- CheckName CheckNameNullablePassedToNonnull;
- CheckName CheckNameNullableReturnedFromNonnull;
+ CheckerNameRef CheckNameNullPassedToNonnull;
+ CheckerNameRef CheckNameNullReturnedFromNonnull;
+ CheckerNameRef CheckNameNullableDereferenced;
+ CheckerNameRef CheckNameNullablePassedToNonnull;
+ CheckerNameRef CheckNameNullableReturnedFromNonnull;
};
NullabilityChecksFilter Filter;
@@ -137,9 +137,9 @@ private:
ID.AddPointer(Region);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
// The tracked region.
@@ -163,10 +163,10 @@ private:
if (!BT)
BT.reset(new BugType(this, "Nullability", categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
if (Region) {
R->markInteresting(Region);
- R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
+ R->addVisitor(std::make_unique<NullabilityBugVisitor>(Region));
}
if (ValueExpr) {
R->addRange(ValueExpr->getSourceRange());
@@ -290,10 +290,9 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
return dyn_cast<SymbolicRegion>(Region);
}
-std::shared_ptr<PathDiagnosticPiece>
-NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) {
+PathDiagnosticPieceRef NullabilityChecker::NullabilityBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = N->getFirstPred()->getState();
@@ -310,7 +309,7 @@ NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N,
// Retrieve the associated statement.
const Stmt *S = TrackedNullab->getNullabilitySource();
if (!S || S->getBeginLoc().isInvalid()) {
- S = PathDiagnosticLocation::getStmt(N);
+ S = N->getStmtForDiagnostics();
}
if (!S)
@@ -324,8 +323,7 @@ NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N,
// Generate the extra diagnostic.
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true,
- nullptr);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
}
/// Returns true when the value stored at the given location has been
@@ -1203,12 +1201,12 @@ bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) {
void ento::register##name##Checker(CheckerManager &mgr) { \
NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \
checker->Filter.Check##name = true; \
- checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
checker->NeedTracking = checker->NeedTracking || trackingRequired; \
checker->NoDiagnoseCallsToSystemHeaders = \
checker->NoDiagnoseCallsToSystemHeaders || \
mgr.getAnalyzerOptions().getCheckerBooleanOption( \
- checker, "NoDiagnoseCallsToSystemHeaders", true); \
+ checker, "NoDiagnoseCallsToSystemHeaders", true); \
} \
\
bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index bd8cfb14680fe..0e25817c87932 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -46,8 +46,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
if (!BT_undef)
BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex "
"for @synchronized"));
- auto report =
- llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(
+ *BT_undef, BT_undef->getDescription(), N);
bugreporter::trackExpressionValue(N, Ex, *report);
C.emitReport(std::move(report));
}
@@ -70,8 +70,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
BT_null.reset(new BuiltinBug(
this, "Nil value used as mutex for @synchronized() "
"(no synchronization will occur)"));
- auto report =
- llvm::make_unique<BugReport>(*BT_null, BT_null->getDescription(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(
+ *BT_null, BT_null->getDescription(), N);
bugreporter::trackExpressionValue(N, Ex, *report);
C.emitReport(std::move(report));
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index f69a3944a56ca..8abb926d4862f 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -144,10 +144,11 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
if (!N)
return;
initBugType();
- auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT, "Index is out of bounds", N);
R->addRange(IdxExpr->getSourceRange());
- bugreporter::trackExpressionValue(N, IdxExpr, *R,
- /*EnableNullFPSuppression=*/false);
+ bugreporter::trackExpressionValue(
+ N, IdxExpr, *R, bugreporter::TrackingKind::Thorough, false);
C.emitReport(std::move(R));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 33e4d2af000dd..1870c08432de3 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -13,12 +13,12 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "llvm/ADT/SmallSet.h"
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index 767b7bf4063c8..344285750f0e8 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -159,7 +159,7 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
if (!BT)
BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"",
categories::CoreFoundationObjectiveC));
- C.emitReport(llvm::make_unique<BugReport>(*BT, errorStr, N));
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N));
}
void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index f435f00c08e79..0575be8453743 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -67,12 +67,11 @@ class SuperDeallocBRVisitor final : public BugReporterVisitor {
public:
SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
- : ReceiverSymbol(ReceiverSymbol),
- Satisfied(false) {}
+ : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.Add(ReceiverSymbol);
@@ -188,10 +187,10 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym,
Desc = "Use of 'self' after it has been deallocated";
// Generate the report.
- std::unique_ptr<BugReport> BR(
- new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode));
+ auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType,
+ Desc, ErrNode);
BR->addRange(S->getSourceRange());
- BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym));
+ BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym));
C.emitReport(std::move(BR));
}
@@ -243,9 +242,10 @@ ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
return M.getSelector() == SELdealloc;
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC, BugReport &) {
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &) {
if (Satisfied)
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index 4b39a97c7e8d1..cb47704515723 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
@@ -20,7 +21,6 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
using namespace clang;
@@ -94,7 +94,7 @@ static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
}
static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
- SourceManager &SM) {
+ const SourceManager &SM) {
for (const auto *I : C->decls())
if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
SourceLocation L = FD->getBeginLoc();
@@ -148,7 +148,7 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
// FIXME: In the future hopefully we can just use the lexical DeclContext
// to go from the ObjCImplementationDecl to the lexically "nested"
// C functions.
- SourceManager &SM = BR.getSourceManager();
+ const SourceManager &SM = BR.getSourceManager();
Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
// Find ivars that are unused.
diff --git a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 0aa410de15ff2..4a3c2b8cd40e2 100644
--- a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -306,7 +306,7 @@ public:
const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
if (!PaddingBug)
PaddingBug =
- llvm::make_unique<BugType>(this, "Excessive Padding", "Performance");
+ std::make_unique<BugType>(this, "Excessive Padding", "Performance");
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
@@ -335,7 +335,8 @@ public:
PathDiagnosticLocation CELoc =
PathDiagnosticLocation::create(RD, BR->getSourceManager());
- auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);
+ auto Report =
+ std::make_unique<BasicBugReport>(*PaddingBug, Os.str(), CELoc);
Report->setDeclWithIssue(RD);
Report->addRange(RD->getSourceRange());
BR->emitReport(std::move(Report));
diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index 03c3f4dd23570..259f23abdc958 100644
--- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -119,12 +119,12 @@ const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
AllocKind &AKind,
CheckerContext &C) const {
assert(Region);
- while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) {
- Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion();
+ while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) {
+ Region = BaseRegion->getSuperRegion();
Polymorphic = true;
}
- if (Region->getKind() == MemRegion::Kind::ElementRegionKind) {
- Region = Region->getAs<ElementRegion>()->getSuperRegion();
+ if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) {
+ Region = ElemRegion->getSuperRegion();
}
ProgramStateRef State = C.getState();
@@ -137,7 +137,7 @@ const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
}
// When the region is symbolic and we do not have any information about it,
// assume that this is an array to avoid false positives.
- if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
+ if (isa<SymbolicRegion>(Region))
return Region;
// No AllocKind stored and not symbolic, assume that it points to a single
@@ -173,8 +173,8 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
this, "Dangerous pointer arithmetic",
"Pointer arithmetic on a pointer to base class is dangerous "
"because derived and base class may have different size."));
- auto R = llvm::make_unique<BugReport>(*BT_polyArray,
- BT_polyArray->getDescription(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT_polyArray, BT_polyArray->getDescription(), N);
R->addRange(E->getSourceRange());
R->markInteresting(ArrayRegion);
C.emitReport(std::move(R));
@@ -196,8 +196,8 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
"Pointer arithmetic on non-array "
"variables relies on memory layout, "
"which is dangerous."));
- auto R = llvm::make_unique<BugReport>(*BT_pointerArith,
- BT_pointerArith->getDescription(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT_pointerArith, BT_pointerArith->getDescription(), N);
R->addRange(SR);
R->markInteresting(Region);
C.emitReport(std::move(R));
diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index c9512f4fc42ff..88d0eb2ae7484 100644
--- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -63,7 +63,8 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
new BuiltinBug(this, "Pointer subtraction",
"Subtraction of two pointers that do not point to "
"the same memory chunk may cause incorrect result."));
- auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ auto R =
+ std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 33f677e1c2583..8649b8b96dd03 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -240,7 +240,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto report = llvm::make_unique<BugReport>(
+ auto report = std::make_unique<PathSensitiveBugReport>(
*BT_doublelock, "This lock has already been acquired", N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
@@ -305,7 +305,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*BT_doubleunlock, "This lock has already been unlocked", N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
@@ -328,7 +328,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto report = llvm::make_unique<BugReport>(
+ auto report = std::make_unique<PathSensitiveBugReport>(
*BT_lor, "This was not the most recently acquired lock. Possible "
"lock order reversal", N);
report->addRange(CE->getArg(0)->getSourceRange());
@@ -399,7 +399,8 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_destroylock, Message, N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -438,7 +439,8 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_initlock, Message, N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
}
@@ -451,7 +453,7 @@ void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*BT_destroylock, "This lock has already been destroyed", N);
Report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(Report));
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index 4a3a8dae23a7f..6f8cb1432bb11 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -875,7 +875,7 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
if (!N)
return;
- auto report = llvm::make_unique<RefCountReport>(
+ auto report = std::make_unique<RefCountReport>(
errorKindToBugKind(ErrorKind, Sym),
C.getASTContext().getLangOpts(), N, Sym);
report->addRange(ErrorRange);
@@ -1095,7 +1095,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
if (N) {
const LangOptions &LOpts = C.getASTContext().getLangOpts();
auto R =
- llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
+ std::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
C.emitReport(std::move(R));
}
return N;
@@ -1119,7 +1119,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
if (N) {
- auto R = llvm::make_unique<RefCountReport>(
+ auto R = std::make_unique<RefCountReport>(
returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
C.emitReport(std::move(R));
}
@@ -1273,7 +1273,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
os << "has a +" << V.getCount() << " retain count";
const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
- auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym,
+ auto R = std::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym,
os.str());
Ctx.emitReport(std::move(R));
}
@@ -1321,7 +1321,7 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
if (N) {
for (SymbolRef L : Leaked) {
const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn;
- Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
+ Ctx.emitReport(std::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
index 124c0e5040b99..dd79bbef321c3 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -21,12 +21,12 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/RetainSummaryManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Analysis/SelectorExtras.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/CallEvent.h"
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 796fd882ffd5e..9853758f7f2c0 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -325,22 +325,22 @@ public:
ID.AddPointer(Sym);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
- std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
- const ExplodedNode *N,
- BugReport &BR) override;
+ PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ PathSensitiveBugReport &BR) override;
};
class RefLeakReportVisitor : public RefCountReportVisitor {
public:
RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
- std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
- const ExplodedNode *N,
- BugReport &BR) override;
+ PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ PathSensitiveBugReport &BR) override;
};
} // end namespace retaincountchecker
@@ -448,9 +448,9 @@ annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
-std::shared_ptr<PathDiagnosticPiece>
-RefCountReportVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+PathDiagnosticPieceRef
+RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
const auto *Checker =
@@ -709,21 +709,22 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
LeakContext)
FirstBinding = nullptr;
- return AllocationInfo(AllocationNodeInCurrentOrParentContext,
- FirstBinding,
+ return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding,
InterestingMethodContext);
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
- const ExplodedNode *EndN, BugReport &BR) {
+ const ExplodedNode *EndN,
+ PathSensitiveBugReport &BR) {
BR.markInteresting(Sym);
return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
- const ExplodedNode *EndN, BugReport &BR) {
+ const ExplodedNode *EndN,
+ PathSensitiveBugReport &BR) {
// Tell the BugReporterContext to report cases when the tracked symbol is
// assigned to different variables, etc.
@@ -737,13 +738,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
const MemRegion* FirstBinding = AllocI.R;
BR.markInteresting(AllocI.InterestingMethodContext);
- SourceManager& SM = BRC.getSourceManager();
-
- // Compute an actual location for the leak. Sometimes a leak doesn't
- // occur at an actual statement (e.g., transition between blocks; end
- // of function) so we need to walk the graph and compute a real location.
- const ExplodedNode *LeakN = EndN;
- PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
+ PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath();
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
@@ -814,19 +809,19 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
}
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
- ExplodedNode *n, SymbolRef sym,
- bool isLeak)
- : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
+ ExplodedNode *n, SymbolRef sym, bool isLeak)
+ : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym),
+ isLeak(isLeak) {
if (!isLeak)
- addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
+ addVisitor(std::make_unique<RefCountReportVisitor>(sym));
}
RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
StringRef endText)
- : BugReport(D, D.getDescription(), endText, n) {
+ : PathSensitiveBugReport(D, D.getDescription(), endText, n) {
- addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
+ addVisitor(std::make_unique<RefCountReportVisitor>(sym));
}
void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
@@ -873,7 +868,7 @@ void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
// FIXME: This will crash the analyzer if an allocation comes from an
// implicit call (ex: a destructor call).
// (Currently there are no such allocations in Cocoa, though.)
- AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ AllocStmt = AllocNode->getStmtForDiagnostics();
if (!AllocStmt) {
AllocBinding = nullptr;
@@ -918,5 +913,5 @@ RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
createDescription(Ctx);
- addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
+ addVisitor(std::make_unique<RefLeakReportVisitor>(sym));
}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
index ef3c75f87af5d..e9e2777540548 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
@@ -14,10 +14,10 @@
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/RetainSummaryManager.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
namespace clang {
@@ -53,7 +53,7 @@ private:
static StringRef bugTypeToName(RefCountBugType BT);
};
-class RefCountReport : public BugReport {
+class RefCountReport : public PathSensitiveBugReport {
protected:
SymbolRef Sym;
bool isLeak = false;
@@ -67,16 +67,17 @@ public:
ExplodedNode *n, SymbolRef sym,
StringRef endText);
- llvm::iterator_range<ranges_iterator> getRanges() override {
+ ArrayRef<SourceRange> getRanges() const override {
if (!isLeak)
- return BugReport::getRanges();
- return llvm::make_range(ranges_iterator(), ranges_iterator());
+ return PathSensitiveBugReport::getRanges();
+ return {};
}
};
class RefLeakReport : public RefCountReport {
const MemRegion* AllocBinding;
const Stmt *AllocStmt;
+ PathDiagnosticLocation Location;
// Finds the function declaration where a leak warning for the parameter
// 'sym' should be raised.
@@ -89,11 +90,14 @@ class RefLeakReport : public RefCountReport {
public:
RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
SymbolRef sym, CheckerContext &Ctx);
-
- PathDiagnosticLocation getLocation(const SourceManager &SM) const override {
+ PathDiagnosticLocation getLocation() const override {
assert(Location.isValid());
return Location;
}
+
+ PathDiagnosticLocation getEndOfPath() const {
+ return PathSensitiveBugReport::getLocation();
+ }
};
} // end namespace retaincountchecker
diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 9eb47e0526dc5..abd1a074b4871 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -79,7 +79,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
// reference is outside the range.
// Generate a report for this bug.
- auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
report->addRange(RetE->getSourceRange());
C.emitReport(std::move(report));
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index f55c369da67eb..fbd15d864424e 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -83,7 +83,8 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
if (!N)
return;
- auto Report = llvm::make_unique<BugReport>(BT, BT.getDescription(), N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N);
Report->addRange(RetE->getSourceRange());
bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report);
diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index ec5e9622c236f..8193bcbef4cdf 100644
--- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -206,8 +206,8 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
return;
// Generate the report.
- auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
- "Closing a previously closed file stream", ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *DoubleCloseBugType, "Closing a previously closed file stream", ErrNode);
R->addRange(Call.getSourceRange());
R->markInteresting(FileDescSym);
C.emitReport(std::move(R));
@@ -219,8 +219,9 @@ void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
// Attach bug reports to the leak node.
// TODO: Identify the leaked file descriptor.
for (SymbolRef LeakedStream : LeakedStreams) {
- auto R = llvm::make_unique<BugReport>(*LeakBugType,
- "Opened file is never closed; potential resource leak", ErrNode);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *LeakBugType, "Opened file is never closed; potential resource leak",
+ ErrNode);
R->markInteresting(LeakedStream);
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index b93bed5c30973..7285d27495a7c 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -155,14 +155,15 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
if (!N)
return;
if (!BT_returnstack)
- BT_returnstack = llvm::make_unique<BuiltinBug>(
+ BT_returnstack = std::make_unique<BuiltinBug>(
this, "Return of address to stack-allocated memory");
// Generate a report for this bug.
SmallString<128> buf;
llvm::raw_svector_ostream os(buf);
SourceRange range = genName(os, R, C.getASTContext());
os << " returned to caller";
- auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N);
report->addRange(RetE->getSourceRange());
if (range.isValid())
report->addRange(range);
@@ -193,14 +194,14 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
if (!N)
continue;
if (!BT_capturedstackasync)
- BT_capturedstackasync = llvm::make_unique<BuiltinBug>(
+ BT_capturedstackasync = std::make_unique<BuiltinBug>(
this, "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
Out << " is captured by an asynchronously-executed block";
- auto Report =
- llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ *BT_capturedstackasync, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
C.emitReport(std::move(Report));
@@ -216,14 +217,14 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures(
if (!N)
continue;
if (!BT_capturedstackret)
- BT_capturedstackret = llvm::make_unique<BuiltinBug>(
+ BT_capturedstackret = std::make_unique<BuiltinBug>(
this, "Address of stack-allocated memory is captured");
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
SourceRange Range = genName(Out, Region, C.getASTContext());
Out << " is captured by a returned block";
- auto Report =
- llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret,
+ Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
C.emitReport(std::move(Report));
@@ -331,7 +332,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
return;
if (!BT_stackleak)
- BT_stackleak = llvm::make_unique<BuiltinBug>(
+ BT_stackleak = std::make_unique<BuiltinBug>(
this, "Stack address stored into global variable",
"Stack address was saved into a global variable. "
"This is dangerous because the address will become "
@@ -351,7 +352,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
Out << *VR->getDecl()
<< "' upon returning to the caller. This will be a dangling reference";
- auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
if (Range.isValid())
Report->addRange(Range);
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 1ea5e07695133..c254408351c81 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -277,7 +277,7 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
new BuiltinBug(this, "Illegal whence argument",
"The whence argument to fseek() should be "
"SEEK_SET, SEEK_END, or SEEK_CUR."));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_illegalwhence, BT_illegalwhence->getDescription(), N));
}
}
@@ -345,7 +345,7 @@ ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
if (!BT_nullfp)
BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
"Stream pointer might be NULL."));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_nullfp, BT_nullfp->getDescription(), N));
}
return nullptr;
@@ -375,7 +375,7 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
BT_doubleclose.reset(new BuiltinBug(
this, "Double fclose", "Try to close a file Descriptor already"
" closed. Cause undefined behaviour."));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_doubleclose, BT_doubleclose->getDescription(), N));
}
return nullptr;
@@ -405,7 +405,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
BT_ResourceLeak.reset(
new BuiltinBug(this, "Resource Leak",
"Opened File never closed. Potential Resource leak."));
- C.emitReport(llvm::make_unique<BugReport>(
+ C.emitReport(std::make_unique<PathSensitiveBugReport>(
*BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
}
}
diff --git a/lib/StaticAnalyzer/Checkers/Taint.cpp b/lib/StaticAnalyzer/Checkers/Taint.cpp
index bc120949ee4f1..574d4ed1e600c 100644
--- a/lib/StaticAnalyzer/Checkers/Taint.cpp
+++ b/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -204,16 +204,16 @@ bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
return false;
}
-std::shared_ptr<PathDiagnosticPiece>
-TaintBugVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
- BugReport &BR) {
+PathDiagnosticPieceRef TaintBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
// Find the ExplodedNode where the taint was first introduced
if (!isTainted(N->getState(), V) ||
isTainted(N->getFirstPred()->getState(), V))
return nullptr;
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
diff --git a/lib/StaticAnalyzer/Checkers/Taint.h b/lib/StaticAnalyzer/Checkers/Taint.h
index 72cf6a79d52c4..8940916c19331 100644
--- a/lib/StaticAnalyzer/Checkers/Taint.h
+++ b/lib/StaticAnalyzer/Checkers/Taint.h
@@ -89,9 +89,9 @@ 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,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
} // namespace taint
diff --git a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
index 094762e2faf83..f81705304f3ab 100644
--- a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -52,7 +52,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E,
if (isTainted(State, E, C.getLocationContext())) {
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
initBugType();
- auto report = llvm::make_unique<BugReport>(*BT, "tainted",N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, "tainted", N);
report->addRange(E->getSourceRange());
C.emitReport(std::move(report));
}
diff --git a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index 7a33845a6a266..3663b09636924 100644
--- a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -69,9 +69,9 @@ public:
ID.Add(SFC);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
class TestAfterDivZeroChecker
@@ -92,9 +92,9 @@ public:
REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState)
-std::shared_ptr<PathDiagnosticPiece>
-DivisionBRVisitor::VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC, BugReport &BR) {
+PathDiagnosticPieceRef
+DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
if (Satisfied)
return nullptr;
@@ -167,12 +167,12 @@ void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
if (!DivZeroBug)
DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*DivZeroBug, "Value being compared against zero has already been used "
"for division",
N);
- R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
+ R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
C.getStackFrame()));
C.emitReport(std::move(R));
}
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 3a4a1dbf641bb..247cba7dc9333 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -96,7 +96,8 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
Ex = FindIt.FindExpr(Ex);
// Emit the bug report.
- auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ *BT, BT->getDescription(), N);
bugreporter::trackExpressionValue(N, Ex, *R);
R->addRange(Ex->getSourceRange());
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index c787ef58660ab..7b581bef39001 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -83,11 +83,12 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
os << "Variable '" << VD->getName()
<< "' is uninitialized when captured by block";
- auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
R->addRange(Ex->getSourceRange());
- R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *V, VR, /*EnableNullFPSuppression*/ false));
+ R->addVisitor(std::make_unique<FindLastStoreBRVisitor>(
+ *V, VR, /*EnableNullFPSuppression*/ false,
+ bugreporter::TrackingKind::Thorough));
R->disablePathPruning();
// need location of block
C.emitReport(std::move(R));
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 1ae287d39f11a..a2f3e0da13fb0 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -170,7 +170,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
<< "' expression is undefined";
}
}
- auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
if (Ex) {
report->addRange(Ex->getSourceRange());
bugreporter::trackExpressionValue(N, Ex, *report);
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index 4c517d6f05622..2f075eaeb03bf 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -52,7 +52,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
BT.reset(new BuiltinBug(this, "Array subscript is undefined"));
// Generate a report for this bug.
- auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
R->addRange(A->getIdx()->getSourceRange());
bugreporter::trackExpressionValue(N, A->getIdx(), *R);
C.emitReport(std::move(R));
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index d32d2a4042de7..277a8a1433282 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -85,7 +85,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
}
if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
- const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
ex = VD->getInit();
}
@@ -108,7 +108,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
if (OS.str().empty())
OS << DefaultMsg;
- auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
if (ex) {
R->addRange(ex->getSourceRange());
bugreporter::trackExpressionValue(N, ex, *R);
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 9d608c12d19be..020df8a1bb8c7 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -24,7 +24,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
using namespace clang;
using namespace clang::ento;
@@ -187,7 +187,7 @@ void UninitializedObjectChecker::checkEndFunction(
if (Opts.ShouldConvertNotesToWarnings) {
for (const auto &Pair : UninitFields) {
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
Node->getLocationContext()->getDecl());
Context.emitReport(std::move(Report));
@@ -201,7 +201,7 @@ void UninitializedObjectChecker::checkEndFunction(
<< (UninitFields.size() == 1 ? "" : "s")
<< " at the end of the constructor call";
- auto Report = llvm::make_unique<BugReport>(
+ auto Report = std::make_unique<PathSensitiveBugReport>(
*BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
Node->getLocationContext()->getDecl());
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index a5dc250104f32..f0dd0bf813aff 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -18,7 +18,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
using namespace clang;
using namespace clang::ento;
@@ -260,12 +260,13 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
break;
}
- while (R->getAs<CXXBaseObjectRegion>()) {
+ while (isa<CXXBaseObjectRegion>(R)) {
NeedsCastBack = true;
-
- if (!isa<TypedValueRegion>(R->getSuperRegion()))
+ const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion());
+ if (!SuperR)
break;
- R = R->getSuperRegion()->getAs<TypedValueRegion>();
+
+ R = SuperR;
}
return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 2ccb519891f37..f4e225d836f38 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -135,7 +135,7 @@ void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
LazyInitialize(this, BT_open, "Improper use of 'open'");
- auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
Report->addRange(SR);
C.emitReport(std::move(Report));
}
@@ -304,7 +304,8 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
- auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
C.emitReport(std::move(report));
}
@@ -347,7 +348,8 @@ bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
SmallString<256> S;
llvm::raw_svector_ostream os(S);
os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
- auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N);
+ auto report =
+ std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
report->addRange(arg->getSourceRange());
bugreporter::trackExpressionValue(N, arg, *report);
diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index 0b0bf8465c9dd..65dd82675df9b 100644
--- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -55,7 +55,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
const Decl *D = nullptr;
CFG *C = nullptr;
- ParentMap *PM = nullptr;
+ const ParentMap *PM = nullptr;
const LocationContext *LC = nullptr;
// Iterate over ExplodedGraph
for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index 1630896c3b607..b92757312dc65 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -72,7 +72,7 @@ void VLASizeChecker::reportBug(
break;
}
- auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
report->addVisitor(std::move(Visitor));
report->addRange(SizeE->getSourceRange());
bugreporter::trackExpressionValue(N, SizeE, *report);
@@ -110,7 +110,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
// Check if the size is tainted.
if (isTainted(state, sizeV)) {
reportBug(VLA_Tainted, SE, nullptr, C,
- llvm::make_unique<TaintBugVisitor>(sizeV));
+ std::make_unique<TaintBugVisitor>(sizeV));
return;
}
diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index 13ad3d98e8ebb..a3610514a924b 100644
--- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -46,7 +46,7 @@ public:
};
DefaultBool ChecksEnabled[CK_NumCheckKinds];
- CheckName CheckNames[CK_NumCheckKinds];
+ CheckerNameRef CheckNames[CK_NumCheckKinds];
void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -77,20 +77,20 @@ private:
ID.AddPointer(&X);
ID.AddPointer(Reg);
}
- std::shared_ptr<PathDiagnosticPiece>
- getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
- BugReport &BR) override {
+ PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ PathSensitiveBugReport &BR) override {
if (!IsLeak)
return nullptr;
- PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(
- EndPathNode, BRC.getSourceManager());
+ PathDiagnosticLocation L = BR.getLocation();
// Do not add the statement itself as a range in case of leak.
- return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false);
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
+ false);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
private:
const MemRegion *Reg;
@@ -115,7 +115,9 @@ const SmallVector<ValistChecker::VAListAccepter, 15>
// vswprintf is the wide version of vsnprintf,
// vsprintf has no wide version
{{"vswscanf", 3}, 2}};
-const CallDescription ValistChecker::VaStart("__builtin_va_start", 2),
+
+const CallDescription
+ ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1),
ValistChecker::VaCopy("__builtin_va_copy", 2),
ValistChecker::VaEnd("__builtin_va_end", 1);
} // end anonymous namespace
@@ -253,9 +255,9 @@ void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
"Uninitialized va_list",
categories::MemoryError));
- auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N);
+ auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
R->markInteresting(VAList);
- R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList));
+ R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
C.emitReport(std::move(R));
}
}
@@ -282,7 +284,7 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
const ExplodedNode *StartNode = getStartCallSite(N, Reg);
PathDiagnosticLocation LocUsedForUniqueing;
- if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode))
+ if (const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics())
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
@@ -294,11 +296,11 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
OS << " " << VariableName;
OS << Msg2;
- auto R = llvm::make_unique<BugReport>(
+ auto R = std::make_unique<PathSensitiveBugReport>(
*BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
StartNode->getLocationContext()->getDecl());
R->markInteresting(Reg);
- R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true));
+ R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true));
C.emitReport(std::move(R));
}
}
@@ -373,13 +375,12 @@ void ValistChecker::checkVAListEndCall(const CallEvent &Call,
C.addTransition(State);
}
-std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode(
- const ExplodedNode *N, BugReporterContext &BRC,
- BugReport &) {
+PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
ProgramStateRef State = N->getState();
ProgramStateRef StatePrev = N->getFirstPred()->getState();
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S)
return nullptr;
@@ -411,7 +412,8 @@ bool ento::shouldRegisterValistBase(const LangOptions &LO) {
void ento::register##name##Checker(CheckerManager &mgr) { \
ValistChecker *checker = mgr.getChecker<ValistChecker>(); \
checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
- checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ checker->CheckNames[ValistChecker::CK_##name] = \
+ mgr.getCurrentCheckerName(); \
} \
\
bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
diff --git a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 40d14aa5c7d44..6724eead50720 100644
--- a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -132,7 +132,7 @@ void VforkChecker::reportBug(const char *What, CheckerContext &C,
if (Details)
os << "; " << Details;
- auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
// TODO: mark vfork call in BugReportVisitor
C.emitReport(std::move(Report));
}
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 762c9c1c8d7aa..12cee5f8d4f74 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a checker that checks virtual function calls during
+// This file defines a checker that checks virtual method calls during
// construction or destruction of C++ objects.
//
//===----------------------------------------------------------------------===//
@@ -40,11 +40,10 @@ template <> struct FoldingSetTrait<ObjectState> {
namespace {
class VirtualCallChecker
: public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
- mutable std::unique_ptr<BugType> BT;
-
public:
- // The flag to determine if pure virtual functions should be issued only.
- DefaultBool IsPureOnly;
+ // These are going to be null if the respective check is disabled.
+ mutable std::unique_ptr<BugType> BT_Pure, BT_Impure;
+ bool ShowFixIts = false;
void checkBeginFunction(CheckerContext &C) const;
void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
@@ -53,87 +52,13 @@ public:
private:
void registerCtorDtorCallInState(bool IsBeginFunction,
CheckerContext &C) const;
- void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
- CheckerContext &C) const;
-
- class VirtualBugVisitor : public BugReporterVisitor {
- private:
- const MemRegion *ObjectRegion;
- bool Found;
-
- public:
- VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
-
- void Profile(llvm::FoldingSetNodeID &ID) const override {
- static int X = 0;
- ID.AddPointer(&X);
- ID.AddPointer(ObjectRegion);
- }
-
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
- };
};
} // end namespace
// GDM (generic data map) to the memregion of this for the ctor and dtor.
REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
-std::shared_ptr<PathDiagnosticPiece>
-VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &) {
- // We need the last ctor/dtor which call the virtual function.
- // The visitor walks the ExplodedGraph backwards.
- if (Found)
- return nullptr;
-
- ProgramStateRef State = N->getState();
- const LocationContext *LCtx = N->getLocationContext();
- const CXXConstructorDecl *CD =
- dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
- const CXXDestructorDecl *DD =
- dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
-
- if (!CD && !DD)
- return nullptr;
-
- ProgramStateManager &PSM = State->getStateManager();
- auto &SVB = PSM.getSValBuilder();
- const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
- if (!MD)
- return nullptr;
- auto ThiSVal =
- State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
- const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
- if (!Reg)
- return nullptr;
- if (Reg != ObjectRegion)
- return nullptr;
-
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
- if (!S)
- return nullptr;
- Found = true;
-
- std::string InfoText;
- if (CD)
- InfoText = "This constructor of an object of type '" +
- CD->getNameAsString() +
- "' has not returned when the virtual method was called";
- else
- InfoText = "This destructor of an object of type '" +
- DD->getNameAsString() +
- "' has not returned when the virtual method was called";
-
- // Generate the extra diagnostic.
- PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
- N->getLocationContext());
- return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
-}
-
-// The function to check if a callexpr is a virtual function.
+// The function to check if a callexpr is a virtual method call.
static bool isVirtualCall(const CallExpr *CE) {
bool CallIsNonVirtual = false;
@@ -178,11 +103,10 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call,
const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
if (!MD)
return;
- ProgramStateRef State = C.getState();
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (IsPureOnly && !MD->isPure())
- return;
+ ProgramStateRef State = C.getState();
+ // Member calls are always represented by a call-expression.
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
if (!isVirtualCall(CE))
return;
@@ -190,29 +114,51 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call,
const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
if (!ObState)
return;
- // Check if a virtual method is called.
- // The GDM of constructor and destructor should be true.
- if (*ObState == ObjectState::CtorCalled) {
- if (IsPureOnly && MD->isPure())
- reportBug("Call to pure virtual function during construction", true, Reg,
- C);
- else if (!MD->isPure())
- reportBug("Call to virtual function during construction", false, Reg, C);
- else
- reportBug("Call to pure virtual function during construction", false, Reg,
- C);
+
+ bool IsPure = MD->isPure();
+
+ // At this point we're sure that we're calling a virtual method
+ // during construction or destruction, so we'll emit a report.
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream OS(Msg);
+ OS << "Call to ";
+ if (IsPure)
+ OS << "pure ";
+ OS << "virtual method '" << MD->getParent()->getNameAsString()
+ << "::" << MD->getNameAsString() << "' during ";
+ if (*ObState == ObjectState::CtorCalled)
+ OS << "construction ";
+ else
+ OS << "destruction ";
+ if (IsPure)
+ OS << "has undefined behavior";
+ else
+ OS << "bypasses virtual dispatch";
+
+ ExplodedNode *N =
+ IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+
+ const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure;
+ if (!BT) {
+ // The respective check is disabled.
+ return;
}
- if (*ObState == ObjectState::DtorCalled) {
- if (IsPureOnly && MD->isPure())
- reportBug("Call to pure virtual function during destruction", true, Reg,
- C);
- else if (!MD->isPure())
- reportBug("Call to virtual function during destruction", false, Reg, C);
- else
- reportBug("Call to pure virtual function during construction", false, Reg,
- C);
+ auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N);
+
+ if (ShowFixIts && !IsPure) {
+ // FIXME: These hints are valid only when the virtual call is made
+ // directly from the constructor/destructor. Otherwise the dispatch
+ // will work just fine from other callees, and the fix may break
+ // the otherwise correct program.
+ FixItHint Fixit = FixItHint::CreateInsertion(
+ CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");
+ Report->addFixItHint(Fixit);
}
+
+ C.emitReport(std::move(Report));
}
void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
@@ -254,34 +200,37 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
}
}
-void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
- const MemRegion *Reg,
- CheckerContext &C) const {
- ExplodedNode *N;
- if (IsSink)
- N = C.generateErrorNode();
- else
- N = C.generateNonFatalErrorNode();
+void ento::registerVirtualCallModeling(CheckerManager &Mgr) {
+ Mgr.registerChecker<VirtualCallChecker>();
+}
- if (!N)
- return;
- if (!BT)
- BT.reset(new BugType(
- this, "Call to virtual function during construction or destruction",
- "C++ Object Lifecycle"));
-
- auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
- Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
- C.emitReport(std::move(Reporter));
+void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.getChecker<VirtualCallChecker>();
+ Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(),
+ "Pure virtual method call",
+ categories::CXXObjectLifecycle);
}
-void ento::registerVirtualCallChecker(CheckerManager &mgr) {
- VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
+void ento::registerVirtualCallChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.getChecker<VirtualCallChecker>();
+ if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Mgr.getCurrentCheckerName(), "PureOnly")) {
+ Chk->BT_Impure = std::make_unique<BugType>(
+ Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch",
+ categories::CXXObjectLifecycle);
+ Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Mgr.getCurrentCheckerName(), "ShowFixIts");
+ }
+}
+
+bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) {
+ return LO.CPlusPlus;
+}
- checker->IsPureOnly =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly");
+bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) {
+ return LO.CPlusPlus;
}
bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
- return true;
+ return LO.CPlusPlus;
}
diff --git a/lib/StaticAnalyzer/Checkers/Yaml.h b/lib/StaticAnalyzer/Checkers/Yaml.h
new file mode 100755
index 0000000000000..968c50e33f6d6
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/Yaml.h
@@ -0,0 +1,59 @@
+//== Yaml.h ---------------------------------------------------- -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines convenience functions for handling YAML configuration files
+// for checkers/packages.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "llvm/Support/YAMLTraits.h"
+
+namespace clang {
+namespace ento {
+
+/// Read the given file from the filesystem and parse it as a yaml file. The
+/// template parameter must have a yaml MappingTraits.
+/// Emit diagnostic error in case of any failure.
+template <class T, class Checker>
+llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk,
+ StringRef Option, StringRef ConfigFile) {
+ if (ConfigFile.trim().empty())
+ return None;
+
+ llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get();
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer =
+ FS->getBufferForFile(ConfigFile.str());
+
+ if (std::error_code ec = Buffer.getError()) {
+ Mgr.reportInvalidCheckerOptionValue(Chk, Option,
+ "a valid filename instead of '" +
+ std::string(ConfigFile) + "'");
+ return None;
+ }
+
+ llvm::yaml::Input Input(Buffer.get()->getBuffer());
+ T Config;
+ Input >> Config;
+
+ if (std::error_code ec = Input.error()) {
+ Mgr.reportInvalidCheckerOptionValue(Chk, Option,
+ "a valid yaml file: " + ec.message());
+ return None;
+ }
+
+ return Config;
+}
+
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 1b1ffff5ade82..73e1a0d0000ff 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -13,7 +13,7 @@ using namespace ento;
void AnalysisManager::anchor() { }
-AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
+AnalysisManager::AnalysisManager(ASTContext &ASTCtx,
const PathDiagnosticConsumers &PDC,
StoreManagerCreator storemgr,
ConstraintManagerCreator constraintmgr,
@@ -38,7 +38,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
Options.ShouldElideConstructors,
/*addVirtualBaseBranches=*/true,
injector),
- Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()),
+ Ctx(ASTCtx), LangOpts(ASTCtx.getLangOpts()),
PathConsumers(PDC), CreateStoreMgr(storemgr),
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
options(Options) {
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 71abe2ae6c0e8..01ac2bc83bb6b 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -30,25 +30,6 @@ using namespace clang;
using namespace ento;
using namespace llvm;
-std::vector<StringRef>
-AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
- static const StringRef StaticAnalyzerChecks[] = {
-#define GET_CHECKERS
-#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
- FULLNAME,
-#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
-#undef CHECKER
-#undef GET_CHECKERS
- };
- std::vector<StringRef> Result;
- for (StringRef CheckName : StaticAnalyzerChecks) {
- if (!CheckName.startswith("debug.") &&
- (IncludeExperimental || !CheckName.startswith("alpha.")))
- Result.push_back(CheckName);
- }
- return Result;
-}
-
void AnalyzerOptions::printFormattedEntry(
llvm::raw_ostream &Out,
std::pair<StringRef, StringRef> EntryDescPair,
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index e5a0794f10e2c..1864bcef9b873 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -24,6 +24,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
@@ -31,7 +32,6 @@
#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"
@@ -71,6 +71,7 @@
using namespace clang;
using namespace ento;
+using namespace llvm;
#define DEBUG_TYPE "BugReporter"
@@ -85,23 +86,252 @@ BugReporterVisitor::~BugReporterVisitor() = default;
void BugReporterContext::anchor() {}
//===----------------------------------------------------------------------===//
-// Helper routines for walking the ExplodedGraph and fetching statements.
+// PathDiagnosticBuilder and its associated routines and helper objects.
//===----------------------------------------------------------------------===//
-static const Stmt *GetPreviousStmt(const ExplodedNode *N) {
- for (N = N->getFirstPred(); N; N = N->getFirstPred())
- if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
- return S;
+namespace {
- return nullptr;
+/// A (CallPiece, node assiciated with its CallEnter) pair.
+using CallWithEntry =
+ std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
+using CallWithEntryStack = SmallVector<CallWithEntry, 6>;
+
+/// Map from each node to the diagnostic pieces visitors emit for them.
+using VisitorsDiagnosticsTy =
+ llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>;
+
+/// A map from PathDiagnosticPiece to the LocationContext of the inlined
+/// function call it represents.
+using LocationContextMap =
+ llvm::DenseMap<const PathPieces *, const LocationContext *>;
+
+/// A helper class that contains everything needed to construct a
+/// PathDiagnostic object. It does no much more then providing convenient
+/// getters and some well placed asserts for extra security.
+class PathDiagnosticConstruct {
+ /// The consumer we're constructing the bug report for.
+ const PathDiagnosticConsumer *Consumer;
+ /// Our current position in the bug path, which is owned by
+ /// PathDiagnosticBuilder.
+ const ExplodedNode *CurrentNode;
+ /// A mapping from parts of the bug path (for example, a function call, which
+ /// would span backwards from a CallExit to a CallEnter with the nodes in
+ /// between them) with the location contexts it is associated with.
+ LocationContextMap LCM;
+ const SourceManager &SM;
+
+public:
+ /// We keep stack of calls to functions as we're ascending the bug path.
+ /// TODO: PathDiagnostic has a stack doing the same thing, shouldn't we use
+ /// that instead?
+ CallWithEntryStack CallStack;
+ /// The bug report we're constructing. For ease of use, this field is kept
+ /// public, though some "shortcut" getters are provided for commonly used
+ /// methods of PathDiagnostic.
+ std::unique_ptr<PathDiagnostic> PD;
+
+public:
+ PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC,
+ const ExplodedNode *ErrorNode,
+ const PathSensitiveBugReport *R);
+
+ /// \returns the location context associated with the current position in the
+ /// bug path.
+ const LocationContext *getCurrLocationContext() const {
+ assert(CurrentNode && "Already reached the root!");
+ return CurrentNode->getLocationContext();
+ }
+
+ /// Same as getCurrLocationContext (they should always return the same
+ /// location context), but works after reaching the root of the bug path as
+ /// well.
+ const LocationContext *getLocationContextForActivePath() const {
+ return LCM.find(&PD->getActivePath())->getSecond();
+ }
+
+ const ExplodedNode *getCurrentNode() const { return CurrentNode; }
+
+ /// Steps the current node to its predecessor.
+ /// \returns whether we reached the root of the bug path.
+ bool ascendToPrevNode() {
+ CurrentNode = CurrentNode->getFirstPred();
+ return static_cast<bool>(CurrentNode);
+ }
+
+ const ParentMap &getParentMap() const {
+ return getCurrLocationContext()->getParentMap();
+ }
+
+ const SourceManager &getSourceManager() const { return SM; }
+
+ const Stmt *getParent(const Stmt *S) const {
+ return getParentMap().getParent(S);
+ }
+
+ void updateLocCtxMap(const PathPieces *Path, const LocationContext *LC) {
+ assert(Path && LC);
+ LCM[Path] = LC;
+ }
+
+ const LocationContext *getLocationContextFor(const PathPieces *Path) const {
+ assert(LCM.count(Path) &&
+ "Failed to find the context associated with these pieces!");
+ return LCM.find(Path)->getSecond();
+ }
+
+ bool isInLocCtxMap(const PathPieces *Path) const { return LCM.count(Path); }
+
+ PathPieces &getActivePath() { return PD->getActivePath(); }
+ PathPieces &getMutablePieces() { return PD->getMutablePieces(); }
+
+ bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); }
+ bool shouldGenerateDiagnostics() const {
+ return Consumer->shouldGenerateDiagnostics();
+ }
+ bool supportsLogicalOpControlFlow() const {
+ return Consumer->supportsLogicalOpControlFlow();
+ }
+};
+
+/// Contains every contextual information needed for constructing a
+/// PathDiagnostic object for a given bug report. This class and its fields are
+/// immutable, and passes a BugReportConstruct object around during the
+/// construction.
+class PathDiagnosticBuilder : public BugReporterContext {
+ /// A linear path from the error node to the root.
+ std::unique_ptr<const ExplodedGraph> BugPath;
+ /// The bug report we're describing. Visitors create their diagnostics with
+ /// them being the last entities being able to modify it (for example,
+ /// changing interestingness here would cause inconsistencies as to how this
+ /// file and visitors construct diagnostics), hence its const.
+ const PathSensitiveBugReport *R;
+ /// The leaf of the bug path. This isn't the same as the bug reports error
+ /// node, which refers to the *original* graph, not the bug path.
+ const ExplodedNode *const ErrorNode;
+ /// The diagnostic pieces visitors emitted, which is expected to be collected
+ /// by the time this builder is constructed.
+ std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics;
+
+public:
+ /// Find a non-invalidated report for a given equivalence class, and returns
+ /// a PathDiagnosticBuilder able to construct bug reports for different
+ /// consumers. Returns None if no valid report is found.
+ static Optional<PathDiagnosticBuilder>
+ findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports,
+ PathSensitiveBugReporter &Reporter);
+
+ PathDiagnosticBuilder(
+ BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath,
+ PathSensitiveBugReport *r, const ExplodedNode *ErrorNode,
+ std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics);
+
+ /// 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,
+ /// the path is modified to insert artificially generated edges.
+ /// Otherwise, more detailed diagnostics is emitted for block edges,
+ /// explaining the transitions in words.
+ std::unique_ptr<PathDiagnostic>
+ generate(const PathDiagnosticConsumer *PDC) const;
+
+private:
+ void updateStackPiecesWithMessage(PathDiagnosticPieceRef P,
+ const CallWithEntryStack &CallStack) const;
+ void generatePathDiagnosticsForNode(PathDiagnosticConstruct &C,
+ PathDiagnosticLocation &PrevLoc) const;
+
+ void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &C,
+ BlockEdge BE) const;
+
+ PathDiagnosticPieceRef
+ generateDiagForGotoOP(const PathDiagnosticConstruct &C, const Stmt *S,
+ PathDiagnosticLocation &Start) const;
+
+ PathDiagnosticPieceRef
+ generateDiagForSwitchOP(const PathDiagnosticConstruct &C, const CFGBlock *Dst,
+ PathDiagnosticLocation &Start) const;
+
+ PathDiagnosticPieceRef
+ generateDiagForBinaryOP(const PathDiagnosticConstruct &C, const Stmt *T,
+ const CFGBlock *Src, const CFGBlock *DstC) const;
+
+ PathDiagnosticLocation
+ ExecutionContinues(const PathDiagnosticConstruct &C) const;
+
+ PathDiagnosticLocation
+ ExecutionContinues(llvm::raw_string_ostream &os,
+ const PathDiagnosticConstruct &C) const;
+
+ const PathSensitiveBugReport *getBugReport() const { return R; }
+};
+
+} // namespace
+
+//===----------------------------------------------------------------------===//
+// Base implementation of stack hint generators.
+//===----------------------------------------------------------------------===//
+
+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 auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
+ if (!CE)
+ return {};
+
+ // Check if one of the parameters are set to the interesting symbol.
+ unsigned ArgIndex = 0;
+ for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+ E = CE->arg_end(); I != E; ++I, ++ArgIndex){
+ SVal SV = N->getSVal(*I);
+
+ // Check if the variable corresponding to the symbol is passed by value.
+ SymbolRef AS = SV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+
+ // Check if the parameter is a pointer to the symbol.
+ if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
+ // 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);
+ }
+ }
+ }
+
+ // Check if we are returning the interesting symbol.
+ SVal SV = N->getSVal(CE);
+ SymbolRef RetSym = SV.getAsLocSymbol();
+ if (RetSym == Sym) {
+ return getMessageForReturn(CE);
+ }
+
+ return getMessageForSymbolNotFound();
}
-static inline const Stmt*
-GetCurrentOrPreviousStmt(const ExplodedNode *N) {
- if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
- return S;
+std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) {
+ // Printed parameters start at 1, not 0.
+ ++ArgIndex;
- return GetPreviousStmt(N);
+ return (llvm::Twine(Msg) + " via " + std::to_string(ArgIndex) +
+ llvm::getOrdinalSuffix(ArgIndex) + " parameter").str();
}
//===----------------------------------------------------------------------===//
@@ -182,16 +412,12 @@ static void removeRedundantMsgs(PathPieces &path) {
}
}
-/// A map from PathDiagnosticPiece to the LocationContext of the inlined
-/// function call it represents.
-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,
+static bool removeUnneededCalls(const PathDiagnosticConstruct &C,
+ PathPieces &pieces,
+ const PathSensitiveBugReport *R,
bool IsInteresting = false) {
bool containsSomethingInteresting = IsInteresting;
const unsigned N = pieces.size();
@@ -206,9 +432,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
case PathDiagnosticPiece::Call: {
auto &call = cast<PathDiagnosticCallPiece>(*piece);
// Check if the location context is interesting.
- assert(LCM.count(&call.path));
- if (!removeUnneededCalls(call.path, R, LCM,
- R->isInteresting(LCM[&call.path])))
+ if (!removeUnneededCalls(
+ C, call.path, R,
+ R->isInteresting(C.getLocationContextFor(&call.path))))
continue;
containsSomethingInteresting = true;
@@ -216,7 +442,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
}
case PathDiagnosticPiece::Macro: {
auto &macro = cast<PathDiagnosticMacroPiece>(*piece);
- if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting))
+ if (!removeUnneededCalls(C, macro.subPieces, R, IsInteresting))
continue;
containsSomethingInteresting = true;
break;
@@ -345,70 +571,23 @@ static void removePiecesWithInvalidLocations(PathPieces &Pieces) {
}
}
-//===----------------------------------------------------------------------===//
-// PathDiagnosticBuilder and its associated routines and helper objects.
-//===----------------------------------------------------------------------===//
-
-namespace {
-
-class PathDiagnosticBuilder : public BugReporterContext {
- BugReport *R;
- PathDiagnosticConsumer *PDC;
-
-public:
- const LocationContext *LC;
-
- PathDiagnosticBuilder(GRBugReporter &br,
- BugReport *r, InterExplodedGraphMap &Backmap,
- PathDiagnosticConsumer *pdc)
- : BugReporterContext(br, Backmap), R(r), PDC(pdc),
- LC(r->getErrorNode()->getLocationContext()) {}
+PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(
+ const PathDiagnosticConstruct &C) const {
+ if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
+ return PathDiagnosticLocation(S, getSourceManager(),
+ C.getCurrLocationContext());
- PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
-
- PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os,
- const ExplodedNode *N);
-
- BugReport *getBugReport() { return R; }
-
- Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
-
- ParentMap& getParentMap() { return LC->getParentMap(); }
-
- const Stmt *getParent(const Stmt *S) {
- return getParentMap().getParent(S);
- }
-
- PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
-
- PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const {
- return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal;
- }
-
- bool supportsLogicalOpControlFlow() const {
- return PDC ? PDC->supportsLogicalOpControlFlow() : true;
- }
-};
-
-} // namespace
-
-PathDiagnosticLocation
-PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
- if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N))
- return PathDiagnosticLocation(S, getSourceManager(), LC);
-
- return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(),
+ return PathDiagnosticLocation::createDeclEnd(C.getCurrLocationContext(),
getSourceManager());
}
-PathDiagnosticLocation
-PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
- const ExplodedNode *N) {
+PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues(
+ llvm::raw_string_ostream &os, const PathDiagnosticConstruct &C) const {
// Slow, but probably doesn't matter.
if (os.str().empty())
os << ' ';
- const PathDiagnosticLocation &Loc = ExecutionContinues(N);
+ const PathDiagnosticLocation &Loc = ExecutionContinues(C);
if (Loc.asStmt())
os << "Execution continues on line "
@@ -416,7 +595,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
<< '.';
else {
os << "Execution jumps to the end of the ";
- const Decl *D = N->getLocationContext()->getDecl();
+ const Decl *D = C.getCurrLocationContext()->getDecl();
if (isa<ObjCMethodDecl>(D))
os << "method";
else if (isa<FunctionDecl>(D))
@@ -454,12 +633,14 @@ static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) {
}
static PathDiagnosticLocation
-getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P,
- const LocationContext *LC, bool allowNestedContexts) {
+getEnclosingStmtLocation(const Stmt *S, const LocationContext *LC,
+ bool allowNestedContexts = false) {
if (!S)
return {};
- while (const Stmt *Parent = getEnclosingParent(S, P)) {
+ const SourceManager &SMgr = LC->getDecl()->getASTContext().getSourceManager();
+
+ while (const Stmt *Parent = getEnclosingParent(S, LC->getParentMap())) {
switch (Parent->getStmtClass()) {
case Stmt::BinaryOperatorClass: {
const auto *B = cast<BinaryOperator>(Parent);
@@ -520,59 +701,51 @@ getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P,
return PathDiagnosticLocation(S, SMgr, LC);
}
-PathDiagnosticLocation
-PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
- assert(S && "Null Stmt passed to getEnclosingStmtLocation");
- return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC,
- /*allowNestedContexts=*/false);
-}
-
//===----------------------------------------------------------------------===//
// "Minimal" path diagnostic generation algorithm.
//===----------------------------------------------------------------------===//
-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 (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) {
- if (ep->hasCallStackHint())
- 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
- // one. Since we traverse the path backwards, do not add the message
- // if one has been previously added.
- if (!CP->hasCallStackMessage())
- CP->setCallStackMessage(stackMsg);
- }
- }
+
+/// If the piece contains a special message, add it to all the call pieces on
+/// the active stack. For example, my_malloc allocated memory, so MallocChecker
+/// will construct an event at the call to malloc(), and add a stack hint that
+/// an allocated memory was returned. We'll use this hint to construct a message
+/// when returning from the call to my_malloc
+///
+/// void *my_malloc() { return malloc(sizeof(int)); }
+/// void fishy() {
+/// void *ptr = my_malloc(); // returned allocated memory
+/// } // leak
+void PathDiagnosticBuilder::updateStackPiecesWithMessage(
+ PathDiagnosticPieceRef P, const CallWithEntryStack &CallStack) const {
+ if (R->hasCallStackHint(P))
+ for (const auto &I : CallStack) {
+ PathDiagnosticCallPiece *CP = I.first;
+ const ExplodedNode *N = I.second;
+ std::string stackMsg = R->getCallStackMessage(P, N);
+
+ // The last message on the path to final bug is the most important
+ // one. Since we traverse the path backwards, do not add the message
+ // if one has been previously added.
+ if (!CP->hasCallStackMessage())
+ CP->setCallStackMessage(stackMsg);
+ }
}
static void CompactMacroExpandedPieces(PathPieces &path,
const SourceManager& SM);
+PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForSwitchOP(
+ const PathDiagnosticConstruct &C, const CFGBlock *Dst,
+ PathDiagnosticLocation &Start) const {
-std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
- const ExplodedNode *N,
- const CFGBlock *Dst,
- const SourceManager &SM,
- const LocationContext *LC,
- PathDiagnosticBuilder &PDB,
- PathDiagnosticLocation &Start
- ) {
+ const SourceManager &SM = getSourceManager();
// Figure out what case arm we took.
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
PathDiagnosticLocation End;
if (const Stmt *S = Dst->getLabel()) {
- End = PathDiagnosticLocation(S, SM, LC);
+ End = PathDiagnosticLocation(S, SM, C.getCurrLocationContext());
switch (S->getStmtClass()) {
default:
@@ -605,7 +778,7 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
}
if (GetRawInt)
- os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
+ os << LHS->EvaluateKnownConstInt(getASTContext());
os << ":' at line " << End.asLocation().getExpansionLineNumber();
break;
@@ -613,33 +786,29 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
}
} else {
os << "'Default' branch taken. ";
- End = PDB.ExecutionContinues(os, N);
+ End = ExecutionContinues(os, C);
}
return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
os.str());
}
+PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForGotoOP(
+ const PathDiagnosticConstruct &C, const Stmt *S,
+ PathDiagnosticLocation &Start) const {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ const PathDiagnosticLocation &End =
+ getEnclosingStmtLocation(S, C.getCurrLocationContext());
+ os << "Control jumps to line " << End.asLocation().getExpansionLineNumber();
+ 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());
+PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForBinaryOP(
+ const PathDiagnosticConstruct &C, const Stmt *T, const CFGBlock *Src,
+ const CFGBlock *Dst) const {
-}
+ const SourceManager &SM = getSourceManager();
-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);
@@ -652,13 +821,14 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP(
if (*(Src->succ_begin() + 1) == Dst) {
os << "false";
- End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
Start =
PathDiagnosticLocation::createOperatorLoc(B, SM);
} else {
os << "true";
- Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
- End = PDB.ExecutionContinues(N);
+ Start =
+ PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
+ End = ExecutionContinues(C);
}
} else {
assert(B->getOpcode() == BO_LOr);
@@ -667,11 +837,12 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP(
if (*(Src->succ_begin() + 1) == Dst) {
os << "false";
- Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
- End = PDB.ExecutionContinues(N);
+ Start =
+ PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
+ End = ExecutionContinues(C);
} else {
os << "true";
- End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext());
Start =
PathDiagnosticLocation::createOperatorLoc(B, SM);
}
@@ -680,11 +851,10 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP(
os.str());
}
-void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
- const SourceManager &SM,
- PathDiagnosticBuilder &PDB,
- PathDiagnostic &PD) {
- const LocationContext *LC = N->getLocationContext();
+void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge(
+ PathDiagnosticConstruct &C, BlockEdge BE) const {
+ const SourceManager &SM = getSourceManager();
+ const LocationContext *LC = C.getCurrLocationContext();
const CFGBlock *Src = BE.getSrc();
const CFGBlock *Dst = BE.getDst();
const Stmt *T = Src->getTerminatorStmt();
@@ -698,14 +868,13 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
case Stmt::GotoStmtClass:
case Stmt::IndirectGotoStmtClass: {
- if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N))
- PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start));
+ if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics())
+ C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start));
break;
}
case Stmt::SwitchStmtClass: {
- PD.getActivePath().push_front(
- generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start));
+ C.getActivePath().push_front(generateDiagForSwitchOP(C, Dst, Start));
break;
}
@@ -713,8 +882,8 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
case Stmt::ContinueStmtClass: {
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
- PD.getActivePath().push_front(
+ PathDiagnosticLocation End = ExecutionContinues(os, C);
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
break;
}
@@ -731,24 +900,22 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
else
os << "true";
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ PathDiagnosticLocation End = ExecutionContinues(C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
- PD.getActivePath().push_front(
+ C.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())
+ if (!C.supportsLogicalOpControlFlow())
break;
- std::shared_ptr<PathDiagnosticControlFlowPiece> Diag =
- generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC);
- PD.getActivePath().push_front(Diag);
+ C.getActivePath().push_front(generateDiagForBinaryOP(C, T, Src, Dst));
break;
}
@@ -758,21 +925,21 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
llvm::raw_string_ostream os(sbuf);
os << "Loop condition is true. ";
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ PathDiagnosticLocation End = ExecutionContinues(os, C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
os.str()));
} else {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ PathDiagnosticLocation End = ExecutionContinues(C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(
Start, End, "Loop condition is false. Exiting loop"));
}
@@ -785,19 +952,19 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
llvm::raw_string_ostream os(sbuf);
os << "Loop condition is false. ";
- PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ PathDiagnosticLocation End = ExecutionContinues(os, C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
os.str()));
} else {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ PathDiagnosticLocation End = ExecutionContinues(C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(
Start, End, "Loop condition is true. Entering loop body"));
}
@@ -805,17 +972,17 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
break;
case Stmt::IfStmtClass: {
- PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ PathDiagnosticLocation End = ExecutionContinues(C);
if (const Stmt *S = End.asStmt())
- End = PDB.getEnclosingStmtLocation(S);
+ End = getEnclosingStmtLocation(S, C.getCurrLocationContext());
if (*(Src->succ_begin() + 1) == Dst)
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(
Start, End, "Taking false branch"));
else
- PD.getActivePath().push_front(
+ C.getActivePath().push_front(
std::make_shared<PathDiagnosticControlFlowPiece>(
Start, End, "Taking true branch"));
@@ -824,75 +991,6 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
}
}
-// Cone-of-influence: support the reverse propagation of "interesting" symbols
-// and values by tracing interesting calculations backwards through evaluated
-// expressions along a path. This is probably overly complicated, but the idea
-// is that if an expression computed an "interesting" value, the child
-// expressions are also likely to be "interesting" as well (which then
-// propagates to the values they in turn compute). This reverse propagation
-// is needed to track interesting correlations across function call boundaries,
-// where formal arguments bind to actual arguments, etc. This is also needed
-// because the constraint solver sometimes simplifies certain symbolic values
-// into constants when appropriate, and this complicates reasoning about
-// interesting values.
-using InterestingExprs = llvm::DenseSet<const Expr *>;
-
-static void reversePropagateIntererstingSymbols(BugReport &R,
- InterestingExprs &IE,
- const ProgramState *State,
- const Expr *Ex,
- const LocationContext *LCtx) {
- SVal V = State->getSVal(Ex, LCtx);
- if (!(R.isInteresting(V) || IE.count(Ex)))
- return;
-
- switch (Ex->getStmtClass()) {
- default:
- if (!isa<CastExpr>(Ex))
- break;
- LLVM_FALLTHROUGH;
- case Stmt::BinaryOperatorClass:
- case Stmt::UnaryOperatorClass: {
- for (const Stmt *SubStmt : Ex->children()) {
- if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) {
- IE.insert(child);
- SVal ChildV = State->getSVal(child, LCtx);
- R.markInteresting(ChildV);
- }
- }
- break;
- }
- }
-
- R.markInteresting(V);
-}
-
-static void reversePropagateInterestingSymbols(BugReport &R,
- InterestingExprs &IE,
- const ProgramState *State,
- const LocationContext *CalleeCtx)
-{
- // FIXME: Handle non-CallExpr-based CallEvents.
- const StackFrameContext *Callee = CalleeCtx->getStackFrame();
- const Stmt *CallSite = Callee->getCallSite();
- 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();
- for (; AI != AE && PI != PE; ++AI, ++PI) {
- if (const Expr *ArgE = *AI) {
- if (const ParmVarDecl *PD = *PI) {
- Loc LV = State->getLValue(PD, CalleeCtx);
- if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV)))
- IE.insert(ArgE);
- }
- }
- }
- }
- }
-}
-
//===----------------------------------------------------------------------===//
// Functions for determining if a loop was executed 0 times.
//===----------------------------------------------------------------------===//
@@ -916,7 +1014,8 @@ static bool isJumpToFalseBranch(const BlockEdge *BE) {
return (*(Src->succ_begin()+1) == BE->getDst());
}
-static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) {
+static bool isContainedByStmt(const ParentMap &PM, const Stmt *S,
+ const Stmt *SubS) {
while (SubS) {
if (SubS == S)
return true;
@@ -925,7 +1024,7 @@ static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) {
return false;
}
-static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term,
+static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term,
const ExplodedNode *N) {
while (N) {
Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
@@ -939,7 +1038,7 @@ static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term,
return nullptr;
}
-static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
+static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term) {
const Stmt *LoopBody = nullptr;
switch (Term->getStmtClass()) {
case Stmt::CXXForRangeStmtClass: {
@@ -1007,30 +1106,20 @@ static const Stmt *getTerminatorCondition(const CFGBlock *B) {
return S;
}
-static const char StrEnteringLoop[] = "Entering loop body";
-static const char StrLoopBodyZero[] = "Loop body executed 0 times";
-static const char StrLoopRangeEmpty[] =
- "Loop body skipped when range is empty";
-static const char StrLoopCollectionEmpty[] =
- "Loop body skipped when collection is empty";
+constexpr llvm::StringLiteral StrEnteringLoop = "Entering loop body";
+constexpr llvm::StringLiteral StrLoopBodyZero = "Loop body executed 0 times";
+constexpr llvm::StringLiteral StrLoopRangeEmpty =
+ "Loop body skipped when range is empty";
+constexpr llvm::StringLiteral StrLoopCollectionEmpty =
+ "Loop body skipped when collection is empty";
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();
+findExecutedLines(const SourceManager &SM, const ExplodedNode *N);
+
+void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
+ PathDiagnosticConstruct &C, PathDiagnosticLocation &PrevLoc) const {
+ ProgramPoint P = C.getCurrentNode()->getLocation();
+ const SourceManager &SM = getSourceManager();
// Have we encountered an entrance to a call? It may be
// the case that we have not encountered a matching
@@ -1038,7 +1127,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
// terminated within the call itself.
if (auto CE = P.getAs<CallEnter>()) {
- if (AddPathEdges) {
+ if (C.shouldAddPathEdges()) {
// Add an edge to the start of the function.
const StackFrameContext *CalleeLC = CE->getCalleeContext();
const Decl *D = CalleeLC->getDecl();
@@ -1049,139 +1138,105 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
// 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));
+ addEdgeToPath(C.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createBegin(D, SM));
}
// Did we visit an entire call?
- bool VisitedEntireCall = PD.isWithinCall();
- PD.popActivePath();
+ bool VisitedEntireCall = C.PD->isWithinCall();
+ C.PD->popActivePath();
- PathDiagnosticCallPiece *C;
+ PathDiagnosticCallPiece *Call;
if (VisitedEntireCall) {
- C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get());
+ Call = cast<PathDiagnosticCallPiece>(C.getActivePath().front().get());
} else {
+ // The path terminated within a nested location context, create a new
+ // call piece to encapsulate the rest of the path pieces.
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;
- }
-
- // 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);
+ Call = PathDiagnosticCallPiece::construct(C.getActivePath(), Caller);
+ assert(C.getActivePath().size() == 1 &&
+ C.getActivePath().front().get() == Call);
+
+ // Since we just transferred the path over to the call piece, reset the
+ // mapping of the active path to the current location context.
+ assert(C.isInLocCtxMap(&C.getActivePath()) &&
+ "When we ascend to a previously unvisited call, the active path's "
+ "address shouldn't change, but rather should be compacted into "
+ "a single CallEvent!");
+ C.updateLocCtxMap(&C.getActivePath(), C.getCurrLocationContext());
+
+ // Record the location context mapping for the path within the call.
+ assert(!C.isInLocCtxMap(&Call->path) &&
+ "When we ascend to a previously unvisited call, this must be the "
+ "first time we encounter the caller context!");
+ C.updateLocCtxMap(&Call->path, CE->getCalleeContext());
+ }
+ Call->setCallee(*CE, SM);
// Update the previous location in the active path.
- PrevLoc = C->getLocation();
+ PrevLoc = Call->getLocation();
- if (!CallStack.empty()) {
- assert(CallStack.back().first == C);
- CallStack.pop_back();
+ if (!C.CallStack.empty()) {
+ assert(C.CallStack.back().first == Call);
+ C.CallStack.pop_back();
}
return;
}
-
- if (AddPathEdges) {
- // Query the location context here and the previous location
- // as processing CallEnter may change the active path.
- PDB.LC = N->getLocationContext();
-
- // 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;
- }
+ assert(C.getCurrLocationContext() == C.getLocationContextForActivePath() &&
+ "The current position in the bug path is out of sync with the "
+ "location context associated with the active path!");
// Have we encountered an exit from a function call?
if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
// We are descending into a call (backwards). Construct
// a new call piece to contain the path pieces for that call.
- auto C = PathDiagnosticCallPiece::construct(*CE, SM);
+ auto Call = PathDiagnosticCallPiece::construct(*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());
- }
+ assert(!C.isInLocCtxMap(&Call->path) &&
+ "We just entered a call, this must've been the first time we "
+ "encounter its context!");
+ C.updateLocCtxMap(&Call->path, CE->getCalleeContext());
+
+ if (C.shouldAddPathEdges()) {
// Add the edge to the return site.
- addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn);
+ addEdgeToPath(C.getActivePath(), PrevLoc, Call->callReturn);
PrevLoc.invalidate();
}
- auto *P = C.get();
- PD.getActivePath().push_front(std::move(C));
+ auto *P = Call.get();
+ C.getActivePath().push_front(std::move(Call));
// Make the contents of the call the active path for now.
- PD.pushActivePath(&P->path);
- CallStack.push_back(StackDiagPair(P, N));
+ C.PD->pushActivePath(&P->path);
+ C.CallStack.push_back(CallWithEntry(P, C.getCurrentNode()));
return;
}
if (auto PS = P.getAs<PostStmt>()) {
- if (!AddPathEdges)
+ if (!C.shouldAddPathEdges())
return;
- // 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);
+ PathDiagnosticLocation(PS->getStmt(), SM, C.getCurrLocationContext());
+ addEdgeToPath(C.getActivePath(), PrevLoc, L);
}
} else if (auto BE = P.getAs<BlockEdge>()) {
- if (!AddPathEdges) {
- generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD);
+ if (!C.shouldAddPathEdges()) {
+ generateMinimalDiagForBlockEdge(C, *BE);
return;
}
- // 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);
- }
- }
-
// 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);
+ PathDiagnosticLocation L(Loop, SM, C.getCurrLocationContext());
const Stmt *Body = nullptr;
if (const auto *FS = dyn_cast<ForStmt>(Loop))
@@ -1200,27 +1255,27 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
"of the loop");
p->setPrunable(true);
- addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation());
- PD.getActivePath().push_front(std::move(p));
+ addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation());
+ C.getActivePath().push_front(std::move(p));
if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PathDiagnosticLocation::createEndBrace(CS, SM));
+ addEdgeToPath(C.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createEndBrace(CS, SM));
}
}
const CFGBlock *BSrc = BE->getSrc();
- ParentMap &PM = PDB.getParentMap();
+ const ParentMap &PM = C.getParentMap();
if (const Stmt *Term = BSrc->getTerminatorStmt()) {
// 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);
+ bool IsInLoopBody = isInLoopBody(
+ PM, getStmtBeforeCond(PM, TermCond, C.getCurrentNode()), Term);
- const char *str = nullptr;
+ StringRef str;
if (isJumpToFalseBranch(&*BE)) {
if (!IsInLoopBody) {
@@ -1236,31 +1291,41 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
str = StrEnteringLoop;
}
- if (str) {
- PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC);
+ if (!str.empty()) {
+ PathDiagnosticLocation L(TermCond ? TermCond : Term, SM,
+ C.getCurrLocationContext());
auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
PE->setPrunable(true);
- addEdgeToPath(PD.getActivePath(), PrevLoc,
- PE->getLocation());
- PD.getActivePath().push_front(std::move(PE));
+ addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation());
+ C.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);
+ PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext());
+ addEdgeToPath(C.getActivePath(), PrevLoc, L);
}
}
}
}
static std::unique_ptr<PathDiagnostic>
-generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) {
+generateDiagnosticForBasicReport(const BasicBugReport *R) {
const BugType &BT = R->getBugType();
- return llvm::make_unique<PathDiagnostic>(
- R->getBugType().getCheckName(), R->getDeclWithIssue(),
- R->getBugType().getName(), R->getDescription(),
- R->getShortDescription(/*UseFallback=*/false), BT.getCategory(),
- R->getUniqueingLocation(), R->getUniqueingDecl(),
+ return std::make_unique<PathDiagnostic>(
+ BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
+ R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
+ BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
+ std::make_unique<FilesToLineNumsMap>());
+}
+
+static std::unique_ptr<PathDiagnostic>
+generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R,
+ const SourceManager &SM) {
+ const BugType &BT = R->getBugType();
+ return std::make_unique<PathDiagnostic>(
+ BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(),
+ R->getDescription(), R->getShortDescription(/*UseFallback=*/false),
+ BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(),
findExecutedLines(SM, R->getErrorNode()));
}
@@ -1342,8 +1407,8 @@ using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>;
/// This avoids a "swoosh" effect, where an edge from a top-level statement A
/// points to a sub-expression B.1 that's not at the start of B. In these cases,
/// we'd like to see an edge from A to B, then another one from B to B.1.
-static void addContextEdges(PathPieces &pieces, SourceManager &SM,
- const ParentMap &PM, const LocationContext *LCtx) {
+static void addContextEdges(PathPieces &pieces, const LocationContext *LC) {
+ const ParentMap &PM = LC->getParentMap();
PathPieces::iterator Prev = pieces.end();
for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
Prev = I, ++I) {
@@ -1360,7 +1425,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) {
SrcContexts.push_back(NextSrcContext);
InnerStmt = NextSrcContext.asStmt();
- NextSrcContext = getEnclosingStmtLocation(InnerStmt, SM, PM, LCtx,
+ NextSrcContext = getEnclosingStmtLocation(InnerStmt, LC,
/*allowNested=*/true);
}
@@ -1373,7 +1438,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
// We are looking at an edge. Is the destination within a larger
// expression?
PathDiagnosticLocation DstContext =
- getEnclosingStmtLocation(Dst, SM, PM, LCtx, /*allowNested=*/true);
+ getEnclosingStmtLocation(Dst, LC, /*allowNested=*/true);
if (!DstContext.isValid() || DstContext.asStmt() == Dst)
break;
@@ -1493,7 +1558,7 @@ static void simplifySimpleBranches(PathPieces &pieces) {
/// If the locations in the range are not on the same line, returns None.
///
/// Note that this does not do a precise user-visible character or column count.
-static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
+static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
SourceRange Range) {
SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
SM.getExpansionRange(Range.getEnd()).getEnd());
@@ -1523,7 +1588,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
}
/// \sa getLengthOnSingleLine(SourceManager, SourceRange)
-static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
+static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
const Stmt *S) {
return getLengthOnSingleLine(SM, S->getSourceRange());
}
@@ -1544,7 +1609,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
/// - if there is an inlined call between the edges instead of a single event.
/// - if the whole statement is large enough that having subexpression arrows
/// might be helpful.
-static void removeContextCycles(PathPieces &Path, SourceManager &SM) {
+static void removeContextCycles(PathPieces &Path, const SourceManager &SM) {
for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
// Pattern match the current piece and its successor.
const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
@@ -1599,7 +1664,7 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM) {
}
/// Return true if X is contained by Y.
-static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) {
+static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y) {
while (X) {
if (X == Y)
return true;
@@ -1609,8 +1674,8 @@ static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) {
}
// Remove short edges on the same line less than 3 columns in difference.
-static void removePunyEdges(PathPieces &path, SourceManager &SM,
- ParentMap &PM) {
+static void removePunyEdges(PathPieces &path, const SourceManager &SM,
+ const ParentMap &PM) {
bool erased = false;
for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
@@ -1685,13 +1750,13 @@ static void removeIdenticalEvents(PathPieces &path) {
}
}
-static bool optimizeEdges(PathPieces &path, SourceManager &SM,
- OptimizedCallsSet &OCS,
- LocationContextMap &LCM) {
+static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path,
+ OptimizedCallsSet &OCS) {
bool hasChanges = false;
- const LocationContext *LC = LCM[&path];
+ const LocationContext *LC = C.getLocationContextFor(&path);
assert(LC);
- ParentMap &PM = LC->getParentMap();
+ const ParentMap &PM = LC->getParentMap();
+ const SourceManager &SM = C.getSourceManager();
for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
// Optimize subpaths.
@@ -1699,7 +1764,8 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
// Record the fact that a call has been optimized so we only do the
// effort once.
if (!OCS.count(CallI)) {
- while (optimizeEdges(CallI->path, SM, OCS, LCM)) {}
+ while (optimizeEdges(C, CallI->path, OCS)) {
+ }
OCS.insert(CallI);
}
++I;
@@ -1845,7 +1911,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM,
if (!hasChanges) {
// Adjust edges into subexpressions to make them more uniform
// and aesthetically pleasing.
- addContextEdges(path, SM, PM, LC);
+ addContextEdges(path, LC);
// Remove "cyclical" edges that include one or more context edges.
removeContextCycles(path, SM);
// Hoist edges originating from branch conditions to branches
@@ -1866,27 +1932,24 @@ 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,
- SourceManager &SM) {
+static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C,
+ PathPieces &Path) {
const auto *FirstEdge =
dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
if (!FirstEdge)
return;
- const Decl *D = LCM[&Path]->getDecl();
- PathDiagnosticLocation EntryLoc = PathDiagnosticLocation::createBegin(D, SM);
+ const Decl *D = C.getLocationContextFor(&Path)->getDecl();
+ PathDiagnosticLocation EntryLoc =
+ PathDiagnosticLocation::createBegin(D, C.getSourceManager());
if (FirstEdge->getStartLocation() != EntryLoc)
return;
Path.pop_front();
}
-using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *,
- std::vector<std::shared_ptr<PathDiagnosticPiece>>>;
-
/// Populate executes lines with lines containing at least one diagnostics.
-static void updateExecutedLinesWithDiagnosticPieces(
- PathDiagnostic &PD) {
+static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD) {
PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true);
FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines();
@@ -1900,56 +1963,61 @@ static void updateExecutedLinesWithDiagnosticPieces(
}
}
-/// 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);
+PathDiagnosticConstruct::PathDiagnosticConstruct(
+ const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode,
+ const PathSensitiveBugReport *R)
+ : Consumer(PDC), CurrentNode(ErrorNode),
+ SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()),
+ PD(generateEmptyDiagnosticForReport(R, getSourceManager())) {
+ LCM[&PD->getActivePath()] = ErrorNode->getLocationContext();
+}
+
+PathDiagnosticBuilder::PathDiagnosticBuilder(
+ BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath,
+ PathSensitiveBugReport *r, const ExplodedNode *ErrorNode,
+ std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics)
+ : BugReporterContext(BRC), BugPath(std::move(BugPath)), R(r),
+ ErrorNode(ErrorNode),
+ VisitorsDiagnostics(std::move(VisitorsDiagnostics)) {}
+
+std::unique_ptr<PathDiagnostic>
+PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const {
+ PathDiagnosticConstruct Construct(PDC, ErrorNode, R);
+
+ const SourceManager &SM = getSourceManager();
+ const AnalyzerOptions &Opts = getAnalyzerOptions();
+ StringRef ErrorTag = ErrorNode->getLocation().getTag()->getTagDescription();
+
+ // See whether we need to silence the checker/package.
+ // FIXME: This will not work if the report was emitted with an incorrect tag.
+ for (const std::string &CheckerOrPackage : Opts.SilencedCheckersAndPackages) {
+ if (ErrorTag.startswith(CheckerOrPackage))
+ return nullptr;
}
- PathDiagnosticLocation PrevLoc = PD->getLocation();
- const ExplodedNode *NextNode = ErrorNode->getFirstPred();
- while (NextNode) {
- if (GenerateDiagnostics)
- generatePathDiagnosticsForNode(
- NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges);
+ if (!PDC->shouldGenerateDiagnostics())
+ return generateEmptyDiagnosticForReport(R, getSourceManager());
+
+ // Construct the final (warning) event for the bug report.
+ auto EndNotes = VisitorsDiagnostics->find(ErrorNode);
+ PathDiagnosticPieceRef LastPiece;
+ if (EndNotes != VisitorsDiagnostics->end()) {
+ assert(!EndNotes->second.empty());
+ LastPiece = EndNotes->second[0];
+ } else {
+ LastPiece = BugReporterVisitor::getDefaultEndPath(*this, ErrorNode,
+ *getBugReport());
+ }
+ Construct.PD->setEndOfPath(LastPiece);
+
+ PathDiagnosticLocation PrevLoc = Construct.PD->getLocation();
+ // From the error node to the root, ascend the bug path and construct the bug
+ // report.
+ while (Construct.ascendToPrevNode()) {
+ generatePathDiagnosticsForNode(Construct, PrevLoc);
- auto VisitorNotes = VisitorsDiagnostics.find(NextNode);
- NextNode = NextNode->getFirstPred();
- if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end())
+ auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode());
+ if (VisitorNotes == VisitorsDiagnostics->end())
continue;
// This is a workaround due to inability to put shared PathDiagnosticPiece
@@ -1957,74 +2025,74 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer(
std::set<llvm::FoldingSetNodeID> DeduplicationSet;
// Add pieces from custom visitors.
- for (const auto &Note : VisitorNotes->second) {
+ for (const PathDiagnosticPieceRef &Note : VisitorNotes->second) {
llvm::FoldingSetNodeID ID;
Note->Profile(ID);
- auto P = DeduplicationSet.insert(ID);
- if (!P.second)
+ if (!DeduplicationSet.insert(ID).second)
continue;
- if (AddPathEdges)
- addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation());
- updateStackPiecesWithMessage(*Note, CallStack);
- PD->getActivePath().push_front(Note);
+ if (PDC->shouldAddPathEdges())
+ addEdgeToPath(Construct.getActivePath(), PrevLoc, Note->getLocation());
+ updateStackPiecesWithMessage(Note, Construct.CallStack);
+ Construct.getActivePath().push_front(Note);
}
}
- if (AddPathEdges) {
+ if (PDC->shouldAddPathEdges()) {
// 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 StackFrameContext *CalleeLC =
+ Construct.getLocationContextForActivePath()->getStackFrame();
const Decl *D = CalleeLC->getDecl();
- addEdgeToPath(PD->getActivePath(), PrevLoc,
+ addEdgeToPath(Construct.getActivePath(), PrevLoc,
PathDiagnosticLocation::createBegin(D, SM));
}
// Finally, prune the diagnostic path of uninteresting stuff.
- if (!PD->path.empty()) {
+ if (!Construct.PD->path.empty()) {
if (R->shouldPrunePath() && Opts.ShouldPrunePaths) {
bool stillHasNotes =
- removeUnneededCalls(PD->getMutablePieces(), R, LCM);
+ removeUnneededCalls(Construct, Construct.getMutablePieces(), R);
assert(stillHasNotes);
(void)stillHasNotes;
}
// Remove pop-up notes if needed.
if (!Opts.ShouldAddPopUpNotes)
- removePopUpNotes(PD->getMutablePieces());
+ removePopUpNotes(Construct.getMutablePieces());
// Redirect all call pieces to have valid locations.
- adjustCallLocations(PD->getMutablePieces());
- removePiecesWithInvalidLocations(PD->getMutablePieces());
+ adjustCallLocations(Construct.getMutablePieces());
+ removePiecesWithInvalidLocations(Construct.getMutablePieces());
- if (AddPathEdges) {
+ if (PDC->shouldAddPathEdges()) {
// 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)) {}
+ while (optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) {
+ }
// Drop the very first function-entry edge. It's not really necessary
// for top-level functions.
- dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM);
+ dropFunctionEntryEdge(Construct, Construct.getMutablePieces());
}
// 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());
+ removeRedundantMsgs(Construct.getMutablePieces());
+ removeEdgesToDefaultInitializers(Construct.getMutablePieces());
}
- if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions)
- CompactMacroExpandedPieces(PD->getMutablePieces(), SM);
+ if (Opts.ShouldDisplayMacroExpansions)
+ CompactMacroExpandedPieces(Construct.getMutablePieces(), SM);
- return PD;
+ return std::move(Construct.PD);
}
-
//===----------------------------------------------------------------------===//
// Methods for BugType and subclasses.
//===----------------------------------------------------------------------===//
@@ -2037,9 +2105,8 @@ void BuiltinBug::anchor() {}
// Methods for BugReport and subclasses.
//===----------------------------------------------------------------------===//
-void BugReport::NodeResolver::anchor() {}
-
-void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) {
+void PathSensitiveBugReport::addVisitor(
+ std::unique_ptr<BugReporterVisitor> visitor) {
if (!visitor)
return;
@@ -2054,20 +2121,11 @@ void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) {
Callbacks.push_back(std::move(visitor));
}
-void BugReport::clearVisitors() {
+void PathSensitiveBugReport::clearVisitors() {
Callbacks.clear();
}
-BugReport::~BugReport() {
- while (!interestingSymbols.empty()) {
- popInterestingSymbolsAndRegions();
- }
-}
-
-const Decl *BugReport::getDeclWithIssue() const {
- if (DeclWithIssue)
- return DeclWithIssue;
-
+const Decl *PathSensitiveBugReport::getDeclWithIssue() const {
const ExplodedNode *N = getErrorNode();
if (!N)
return nullptr;
@@ -2076,17 +2134,34 @@ const Decl *BugReport::getDeclWithIssue() const {
return LC->getStackFrame()->getDecl();
}
-void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
+void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const {
+ hash.AddInteger(static_cast<int>(getKind()));
+ hash.AddPointer(&BT);
+ hash.AddString(Description);
+ assert(Location.isValid());
+ Location.Profile(hash);
+
+ for (SourceRange range : Ranges) {
+ if (!range.isValid())
+ continue;
+ hash.AddInteger(range.getBegin().getRawEncoding());
+ hash.AddInteger(range.getEnd().getRawEncoding());
+ }
+}
+
+void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const {
+ hash.AddInteger(static_cast<int>(getKind()));
hash.AddPointer(&BT);
hash.AddString(Description);
PathDiagnosticLocation UL = getUniqueingLocation();
if (UL.isValid()) {
UL.Profile(hash);
- } else if (Location.isValid()) {
- Location.Profile(hash);
} else {
- assert(ErrorNode);
- hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
+ // TODO: The statement may be null if the report was emitted before any
+ // statements were executed. In particular, some checkers by design
+ // occasionally emit their reports in empty functions (that have no
+ // statements in their body). Do we profile correctly in this case?
+ hash.AddPointer(ErrorNode->getCurrentOrPreviousStmtForDiagnostics());
}
for (SourceRange range : Ranges) {
@@ -2097,96 +2172,141 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
}
}
-void BugReport::markInteresting(SymbolRef sym) {
+template <class T>
+static void insertToInterestingnessMap(
+ llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val,
+ bugreporter::TrackingKind TKind) {
+ auto Result = InterestingnessMap.insert({Val, TKind});
+
+ if (Result.second)
+ return;
+
+ // Even if this symbol/region was already marked as interesting as a
+ // condition, if we later mark it as interesting again but with
+ // thorough tracking, overwrite it. Entities marked with thorough
+ // interestiness are the most important (or most interesting, if you will),
+ // and we wouldn't like to downplay their importance.
+
+ switch (TKind) {
+ case bugreporter::TrackingKind::Thorough:
+ Result.first->getSecond() = bugreporter::TrackingKind::Thorough;
+ return;
+ case bugreporter::TrackingKind::Condition:
+ return;
+ }
+
+ llvm_unreachable(
+ "BugReport::markInteresting currently can only handle 2 different "
+ "tracking kinds! Please define what tracking kind should this entitiy"
+ "have, if it was already marked as interesting with a different kind!");
+}
+
+void PathSensitiveBugReport::markInteresting(SymbolRef sym,
+ bugreporter::TrackingKind TKind) {
if (!sym)
return;
- getInterestingSymbols().insert(sym);
+ insertToInterestingnessMap(InterestingSymbols, sym, TKind);
if (const auto *meta = dyn_cast<SymbolMetadata>(sym))
- getInterestingRegions().insert(meta->getRegion());
+ markInteresting(meta->getRegion(), TKind);
}
-void BugReport::markInteresting(const MemRegion *R) {
+void PathSensitiveBugReport::markInteresting(const MemRegion *R,
+ bugreporter::TrackingKind TKind) {
if (!R)
return;
R = R->getBaseRegion();
- getInterestingRegions().insert(R);
+ insertToInterestingnessMap(InterestingRegions, R, TKind);
if (const auto *SR = dyn_cast<SymbolicRegion>(R))
- getInterestingSymbols().insert(SR->getSymbol());
+ markInteresting(SR->getSymbol(), TKind);
}
-void BugReport::markInteresting(SVal V) {
- markInteresting(V.getAsRegion());
- markInteresting(V.getAsSymbol());
+void PathSensitiveBugReport::markInteresting(SVal V,
+ bugreporter::TrackingKind TKind) {
+ markInteresting(V.getAsRegion(), TKind);
+ markInteresting(V.getAsSymbol(), TKind);
}
-void BugReport::markInteresting(const LocationContext *LC) {
+void PathSensitiveBugReport::markInteresting(const LocationContext *LC) {
if (!LC)
return;
InterestingLocationContexts.insert(LC);
}
-bool BugReport::isInteresting(SVal V) {
- return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol());
+Optional<bugreporter::TrackingKind>
+PathSensitiveBugReport::getInterestingnessKind(SVal V) const {
+ auto RKind = getInterestingnessKind(V.getAsRegion());
+ auto SKind = getInterestingnessKind(V.getAsSymbol());
+ if (!RKind)
+ return SKind;
+ if (!SKind)
+ return RKind;
+
+ // If either is marked with throrough tracking, return that, we wouldn't like
+ // to downplay a note's importance by 'only' mentioning it as a condition.
+ switch(*RKind) {
+ case bugreporter::TrackingKind::Thorough:
+ return RKind;
+ case bugreporter::TrackingKind::Condition:
+ return SKind;
+ }
+
+ llvm_unreachable(
+ "BugReport::getInterestingnessKind currently can only handle 2 different "
+ "tracking kinds! Please define what tracking kind should we return here "
+ "when the kind of getAsRegion() and getAsSymbol() is different!");
+ return None;
}
-bool BugReport::isInteresting(SymbolRef sym) {
+Optional<bugreporter::TrackingKind>
+PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const {
if (!sym)
- return false;
+ return None;
// We don't currently consider metadata symbols to be interesting
// even if we know their region is interesting. Is that correct behavior?
- return getInterestingSymbols().count(sym);
+ auto It = InterestingSymbols.find(sym);
+ if (It == InterestingSymbols.end())
+ return None;
+ return It->getSecond();
}
-bool BugReport::isInteresting(const MemRegion *R) {
+Optional<bugreporter::TrackingKind>
+PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const {
if (!R)
- return false;
- R = R->getBaseRegion();
- bool b = getInterestingRegions().count(R);
- if (b)
- return true;
- if (const auto *SR = dyn_cast<SymbolicRegion>(R))
- return getInterestingSymbols().count(SR->getSymbol());
- return false;
-}
+ return None;
-bool BugReport::isInteresting(const LocationContext *LC) {
- if (!LC)
- return false;
- return InterestingLocationContexts.count(LC);
-}
+ R = R->getBaseRegion();
+ auto It = InterestingRegions.find(R);
+ if (It != InterestingRegions.end())
+ return It->getSecond();
-void BugReport::lazyInitializeInterestingSets() {
- if (interestingSymbols.empty()) {
- interestingSymbols.push_back(new Symbols());
- interestingRegions.push_back(new Regions());
- }
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+ return getInterestingnessKind(SR->getSymbol());
+ return None;
}
-BugReport::Symbols &BugReport::getInterestingSymbols() {
- lazyInitializeInterestingSets();
- return *interestingSymbols.back();
+bool PathSensitiveBugReport::isInteresting(SVal V) const {
+ return getInterestingnessKind(V).hasValue();
}
-BugReport::Regions &BugReport::getInterestingRegions() {
- lazyInitializeInterestingSets();
- return *interestingRegions.back();
+bool PathSensitiveBugReport::isInteresting(SymbolRef sym) const {
+ return getInterestingnessKind(sym).hasValue();
}
-void BugReport::pushInterestingSymbolsAndRegions() {
- interestingSymbols.push_back(new Symbols(getInterestingSymbols()));
- interestingRegions.push_back(new Regions(getInterestingRegions()));
+bool PathSensitiveBugReport::isInteresting(const MemRegion *R) const {
+ return getInterestingnessKind(R).hasValue();
}
-void BugReport::popInterestingSymbolsAndRegions() {
- delete interestingSymbols.pop_back_val();
- delete interestingRegions.pop_back_val();
+bool PathSensitiveBugReport::isInteresting(const LocationContext *LC) const {
+ if (!LC)
+ return false;
+ return InterestingLocationContexts.count(LC);
}
-const Stmt *BugReport::getStmt() const {
+const Stmt *PathSensitiveBugReport::getStmt() const {
if (!ErrorNode)
return nullptr;
@@ -2196,59 +2316,83 @@ const Stmt *BugReport::getStmt() const {
if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
if (BE->getBlock() == &Exit)
- S = GetPreviousStmt(ErrorNode);
+ S = ErrorNode->getPreviousStmtForDiagnostics();
}
if (!S)
- S = PathDiagnosticLocation::getStmt(ErrorNode);
+ S = ErrorNode->getStmtForDiagnostics();
return S;
}
-llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() {
+ArrayRef<SourceRange>
+PathSensitiveBugReport::getRanges() const {
// If no custom ranges, add the range of the statement corresponding to
// the error node.
- if (Ranges.empty()) {
- if (const auto *E = dyn_cast_or_null<Expr>(getStmt()))
- addRange(E->getSourceRange());
- else
- return llvm::make_range(ranges_iterator(), ranges_iterator());
+ if (Ranges.empty() && isa_and_nonnull<Expr>(getStmt()))
+ return ErrorNodeRange;
+
+ return Ranges;
+}
+
+PathDiagnosticLocation
+PathSensitiveBugReport::getLocation() const {
+ assert(ErrorNode && "Cannot create a location with a null node.");
+ const Stmt *S = ErrorNode->getStmtForDiagnostics();
+ ProgramPoint P = ErrorNode->getLocation();
+ const LocationContext *LC = P.getLocationContext();
+ SourceManager &SM =
+ ErrorNode->getState()->getStateManager().getContext().getSourceManager();
+
+ if (!S) {
+ // If this is an implicit call, return the implicit call point location.
+ if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>())
+ return PathDiagnosticLocation(PIE->getLocation(), SM);
+ if (auto FE = P.getAs<FunctionExitPoint>()) {
+ if (const ReturnStmt *RS = FE->getStmt())
+ return PathDiagnosticLocation::createBegin(RS, SM, LC);
+ }
+ S = ErrorNode->getNextStmtForDiagnostics();
}
- // User-specified absence of range info.
- if (Ranges.size() == 1 && !Ranges.begin()->isValid())
- return llvm::make_range(ranges_iterator(), ranges_iterator());
+ if (S) {
+ // For member expressions, return the location of the '.' or '->'.
+ if (const auto *ME = dyn_cast<MemberExpr>(S))
+ return PathDiagnosticLocation::createMemberLoc(ME, SM);
- return llvm::make_range(Ranges.begin(), Ranges.end());
-}
+ // For binary operators, return the location of the operator.
+ if (const auto *B = dyn_cast<BinaryOperator>(S))
+ return PathDiagnosticLocation::createOperatorLoc(B, SM);
+
+ if (P.getAs<PostStmtPurgeDeadSymbols>())
+ return PathDiagnosticLocation::createEnd(S, SM, LC);
+
+ if (S->getBeginLoc().isValid())
+ return PathDiagnosticLocation(S, SM, LC);
-PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
- if (ErrorNode) {
- assert(!Location.isValid() &&
- "Either Location or ErrorNode should be specified but not both.");
- return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM);
+ return PathDiagnosticLocation(
+ PathDiagnosticLocation::getValidSourceLocation(S, LC), SM);
}
- assert(Location.isValid());
- return Location;
+ return PathDiagnosticLocation::createDeclEnd(ErrorNode->getLocationContext(),
+ SM);
}
//===----------------------------------------------------------------------===//
// Methods for BugReporter and subclasses.
//===----------------------------------------------------------------------===//
-BugReportEquivClass::~BugReportEquivClass() = default;
-
-GRBugReporter::~GRBugReporter() = default;
-
-BugReporterData::~BugReporterData() = default;
-
-ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); }
+const ExplodedGraph &PathSensitiveBugReporter::getGraph() const {
+ return Eng.getGraph();
+}
-ProgramStateManager&
-GRBugReporter::getStateManager() { return Eng.getStateManager(); }
+ProgramStateManager &PathSensitiveBugReporter::getStateManager() const {
+ return Eng.getStateManager();
+}
BugReporter::~BugReporter() {
- FlushReports();
+ // Make sure reports are flushed.
+ assert(StrBugTypes.empty() &&
+ "Destroying BugReporter before diagnostics are emitted!");
// Free the bug reports we are tracking.
for (const auto I : EQClassesVector)
@@ -2256,9 +2400,6 @@ BugReporter::~BugReporter() {
}
void BugReporter::FlushReports() {
- if (BugTypes.isEmpty())
- return;
-
// We need to flush reports in deterministic order to ensure the order
// of the reports is consistent between runs.
for (const auto EQ : EQClassesVector)
@@ -2269,9 +2410,6 @@ void BugReporter::FlushReports() {
// FIXME: There are leaks from checkers that assume that the BugTypes they
// create will be destroyed by the BugReporter.
llvm::DeleteContainerSeconds(StrBugTypes);
-
- // Remove all references to the BugType objects.
- BugTypes = F.getEmptySet();
}
//===----------------------------------------------------------------------===//
@@ -2280,29 +2418,32 @@ void BugReporter::FlushReports() {
namespace {
-/// A wrapper around a report graph, which contains only a single path, and its
-/// node maps.
-class ReportGraph {
+/// A wrapper around an ExplodedGraph that contains a single path from the root
+/// to the error node.
+class BugPathInfo {
public:
- InterExplodedGraphMap BackMap;
- std::unique_ptr<ExplodedGraph> Graph;
+ std::unique_ptr<ExplodedGraph> BugPath;
+ PathSensitiveBugReport *Report;
const ExplodedNode *ErrorNode;
- size_t Index;
};
-/// A wrapper around a trimmed graph and its node maps.
-class TrimmedGraph {
- InterExplodedGraphMap InverseMap;
+/// A wrapper around an ExplodedGraph whose leafs are all error nodes. Can
+/// conveniently retrieve bug paths from a single error node to the root.
+class BugPathGetter {
+ std::unique_ptr<ExplodedGraph> TrimmedGraph;
using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
+ /// Assign each node with its distance from the root.
PriorityMapTy PriorityMap;
- using NodeIndexPair = std::pair<const ExplodedNode *, size_t>;
-
- SmallVector<NodeIndexPair, 32> ReportNodes;
+ /// Since the getErrorNode() or BugReport refers to the original ExplodedGraph,
+ /// we need to pair it to the error node of the constructed trimmed graph.
+ using ReportNewNodePair =
+ std::pair<PathSensitiveBugReport *, const ExplodedNode *>;
+ SmallVector<ReportNewNodePair, 32> ReportNodes;
- std::unique_ptr<ExplodedGraph> G;
+ BugPathInfo CurrentBugPath;
/// A helper class for sorting ExplodedNodes by priority.
template <bool Descending>
@@ -2326,37 +2467,48 @@ class TrimmedGraph {
: LI->second < RI->second;
}
- bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const {
- return (*this)(LHS.first, RHS.first);
+ bool operator()(const ReportNewNodePair &LHS,
+ const ReportNewNodePair &RHS) const {
+ return (*this)(LHS.second, RHS.second);
}
};
public:
- TrimmedGraph(const ExplodedGraph *OriginalGraph,
- ArrayRef<const ExplodedNode *> Nodes);
+ BugPathGetter(const ExplodedGraph *OriginalGraph,
+ ArrayRef<PathSensitiveBugReport *> &bugReports);
- bool popNextReportGraph(ReportGraph &GraphWrapper);
+ BugPathInfo *getNextBugPath();
};
} // namespace
-TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
- ArrayRef<const ExplodedNode *> Nodes) {
+BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph,
+ ArrayRef<PathSensitiveBugReport *> &bugReports) {
+ SmallVector<const ExplodedNode *, 32> Nodes;
+ for (const auto I : bugReports) {
+ assert(I->isValid() &&
+ "We only allow BugReporterVisitors and BugReporter itself to "
+ "invalidate reports!");
+ Nodes.emplace_back(I->getErrorNode());
+ }
+
// The trimmed graph is created in the body of the constructor to ensure
// that the DenseMaps have been initialized already.
InterExplodedGraphMap ForwardMap;
- G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap);
+ TrimmedGraph = OriginalGraph->trim(Nodes, &ForwardMap);
// Find the (first) error node in the trimmed graph. We just need to consult
// the node map which maps from nodes in the original graph to nodes
// in the new graph.
llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes;
- for (unsigned i = 0, count = Nodes.size(); i < count; ++i) {
- if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) {
- ReportNodes.push_back(std::make_pair(NewNode, i));
- RemainingNodes.insert(NewNode);
- }
+ for (PathSensitiveBugReport *Report : bugReports) {
+ const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode());
+ assert(NewNode &&
+ "Failed to construct a trimmed graph that contains this error "
+ "node!");
+ ReportNodes.emplace_back(Report, NewNode);
+ RemainingNodes.insert(NewNode);
}
assert(!RemainingNodes.empty() && "No error node found in the trimmed graph");
@@ -2364,8 +2516,8 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
// Perform a forward BFS to find all the shortest paths.
std::queue<const ExplodedNode *> WS;
- assert(G->num_roots() == 1);
- WS.push(*G->roots_begin());
+ assert(TrimmedGraph->num_roots() == 1);
+ WS.push(*TrimmedGraph->roots_begin());
unsigned Priority = 0;
while (!WS.empty()) {
@@ -2374,8 +2526,7 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
PriorityMapTy::iterator PriorityEntry;
bool IsNew;
- std::tie(PriorityEntry, IsNew) =
- PriorityMap.insert(std::make_pair(Node, Priority));
+ std::tie(PriorityEntry, IsNew) = PriorityMap.insert({Node, Priority});
++Priority;
if (!IsNew) {
@@ -2387,29 +2538,26 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
if (RemainingNodes.empty())
break;
- for (ExplodedNode::const_pred_iterator I = Node->succ_begin(),
- E = Node->succ_end();
- I != E; ++I)
- WS.push(*I);
+ for (const ExplodedNode *Succ : Node->succs())
+ WS.push(Succ);
}
// Sort the error paths from longest to shortest.
llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
}
-bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
+BugPathInfo *BugPathGetter::getNextBugPath() {
if (ReportNodes.empty())
- return false;
+ return nullptr;
const ExplodedNode *OrigN;
- std::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val();
+ std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val();
assert(PriorityMap.find(OrigN) != PriorityMap.end() &&
"error node not accessible from root");
- // Create a new graph with a single path. This is the graph
- // that will be returned to the caller.
- auto GNew = llvm::make_unique<ExplodedGraph>();
- GraphWrapper.BackMap.clear();
+ // Create a new graph with a single path. This is the graph that will be
+ // returned to the caller.
+ auto GNew = std::make_unique<ExplodedGraph>();
// Now walk from the error node up the BFS path, always taking the
// predeccessor with the lowest number.
@@ -2417,19 +2565,15 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
while (true) {
// Create the equivalent node in the new graph with the same state
// and location.
- ExplodedNode *NewN = GNew->createUncachedNode(OrigN->getLocation(), OrigN->getState(),
- OrigN->isSink());
-
- // Store the mapping to the original node.
- InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN);
- assert(IMitr != InverseMap.end() && "No mapping to original node.");
- GraphWrapper.BackMap[NewN] = IMitr->second;
+ ExplodedNode *NewN = GNew->createUncachedNode(
+ OrigN->getLocation(), OrigN->getState(),
+ OrigN->getID(), OrigN->isSink());
// Link up the new node with the previous node.
if (Succ)
Succ->addPredecessor(NewN, *GNew);
else
- GraphWrapper.ErrorNode = NewN;
+ CurrentBugPath.ErrorNode = NewN;
Succ = NewN;
@@ -2442,23 +2586,22 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
// Find the next predeccessor node. We choose the node that is marked
// with the lowest BFS number.
OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(),
- PriorityCompare<false>(PriorityMap));
+ PriorityCompare<false>(PriorityMap));
}
- GraphWrapper.Graph = std::move(GNew);
+ CurrentBugPath.BugPath = std::move(GNew);
- return true;
+ return &CurrentBugPath;
}
/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic
/// object and collapses PathDiagosticPieces that are expanded by macros.
static void CompactMacroExpandedPieces(PathPieces &path,
const SourceManager& SM) {
- using MacroStackTy =
- std::vector<
- std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
+ using MacroStackTy = std::vector<
+ std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
- using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>;
+ using PiecesTy = std::vector<PathDiagnosticPieceRef>;
MacroStackTy MacroStack;
PiecesTy Pieces;
@@ -2548,33 +2691,39 @@ static void CompactMacroExpandedPieces(PathPieces &path,
/// 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,
+generateVisitorsDiagnostics(PathSensitiveBugReport *R,
+ const ExplodedNode *ErrorNode,
BugReporterContext &BRC) {
- auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>();
- BugReport::VisitorList visitors;
+ std::unique_ptr<VisitorsDiagnosticsTy> Notes =
+ std::make_unique<VisitorsDiagnosticsTy>();
+ PathSensitiveBugReport::VisitorList visitors;
// 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));
- }
+ // At each iteration, move all visitors from report to visitor list. This is
+ // important, because the Profile() functions of the visitors make sure that
+ // a visitor isn't added multiple times for the same node, but it's fine
+ // to add the a visitor with Profile() for different nodes (e.g. tracking
+ // a region at different points of the symbolic execution).
+ for (std::unique_ptr<BugReporterVisitor> &Visitor : R->visitors())
+ visitors.push_back(std::move(Visitor));
+
R->clearVisitors();
const ExplodedNode *Pred = NextNode->getFirstPred();
if (!Pred) {
- std::shared_ptr<PathDiagnosticPiece> LastPiece;
+ PathDiagnosticPieceRef LastPiece;
for (auto &V : visitors) {
V->finalizeVisitor(BRC, ErrorNode, *R);
if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) {
assert(!LastPiece &&
"There can only be one final piece in a diagnostic.");
+ assert(Piece->getKind() == PathDiagnosticPiece::Kind::Event &&
+ "The final piece must contain a message!");
LastPiece = std::move(Piece);
(*Notes)[ErrorNode].push_back(LastPiece);
}
@@ -2597,127 +2746,81 @@ generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode,
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) {
+Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport(
+ ArrayRef<PathSensitiveBugReport *> &bugReports,
+ PathSensitiveBugReporter &Reporter) {
- while (TrimG.popNextReportGraph(ErrorGraph)) {
+ BugPathGetter BugGraph(&Reporter.getGraph(), bugReports);
+
+ while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) {
// Find the BugReport with the original location.
- assert(ErrorGraph.Index < bugReports.size());
- BugReport *R = bugReports[ErrorGraph.Index];
+ PathSensitiveBugReport *R = BugPath->Report;
assert(R && "No original report found for sliced graph.");
assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
- const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
+ const ExplodedNode *ErrorNode = BugPath->ErrorNode;
// Register refutation visitors first, if they mark the bug invalid no
// further analysis is required
- R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
+ R->addVisitor(std::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
// Register additional node visitors.
- R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
- R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
- R->addVisitor(llvm::make_unique<TagVisitor>());
+ R->addVisitor(std::make_unique<NilReceiverBRVisitor>());
+ R->addVisitor(std::make_unique<ConditionBRVisitor>());
+ R->addVisitor(std::make_unique<TagVisitor>());
- BugReporterContext BRC(Reporter, ErrorGraph.BackMap);
+ BugReporterContext BRC(Reporter);
// Run all visitors on a given graph, once.
std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
generateVisitorsDiagnostics(R, ErrorNode, BRC);
if (R->isValid()) {
- if (Opts.ShouldCrosscheckWithZ3) {
+ if (Reporter.getAnalyzerOptions().ShouldCrosscheckWithZ3) {
// If crosscheck is enabled, remove all visitors, add the refutation
// visitor and check again
R->clearVisitors();
- R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>());
+ R->addVisitor(std::make_unique<FalsePositiveRefutationBRVisitor>());
// 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);
+ generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC);
}
// Check if the bug is still valid
if (R->isValid())
- return std::make_pair(R, std::move(visitorNotes));
+ return PathDiagnosticBuilder(
+ std::move(BRC), std::move(BugPath->BugPath), BugPath->Report,
+ BugPath->ErrorNode, std::move(visitorNotes));
}
}
- return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>());
+ return {};
}
std::unique_ptr<DiagnosticForConsumerMapTy>
-GRBugReporter::generatePathDiagnostics(
+PathSensitiveBugReporter::generatePathDiagnostics(
ArrayRef<PathDiagnosticConsumer *> consumers,
- ArrayRef<BugReport *> &bugReports) {
+ ArrayRef<PathSensitiveBugReport *> &bugReports) {
assert(!bugReports.empty());
- 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);
- }
- }
-
- // If all the reports have been marked invalid by a previous path generation,
- // we're done.
- if (!HasValid)
- return Out;
+ auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
- TrimmedGraph TrimG(&getGraph(), errorNodes);
- ReportGraph ErrorGraph;
- auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports,
- getAnalyzerOptions(), *this);
- BugReport *R = ReportInfo.first;
+ Optional<PathDiagnosticBuilder> PDB =
+ PathDiagnosticBuilder::findValidReport(bugReports, *this);
- if (R && R->isValid()) {
- const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
+ if (PDB) {
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);
+ if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) {
+ (*Out)[PC] = std::move(PD);
+ }
}
}
return Out;
}
-void BugReporter::Register(const BugType *BT) {
- BugTypes = F.add(BugTypes, BT);
-}
-
void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
- if (const ExplodedNode *E = R->getErrorNode()) {
- // An error node must either be a sink or have a tag, otherwise
- // it could get reclaimed before the path diagnostic is created.
- assert((E->isSink() || E->getLocation().getTag()) &&
- "Error node must either be a sink or have a tag");
-
- const AnalysisDeclContext *DeclCtx =
- E->getLocationContext()->getAnalysisDeclContext();
- // The source of autosynthesized body can be handcrafted AST or a model
- // file. The locations from handcrafted ASTs have no valid source locations
- // and have to be discarded. Locations from model files should be preserved
- // for processing and reporting.
- if (DeclCtx->isBodyAutosynthesized() &&
- !DeclCtx->isBodyAutosynthesizedFromModelFile())
- return;
- }
-
- bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid();
+ bool ValidSourceLoc = R->getLocation().isValid();
assert(ValidSourceLoc);
// If we mess up in a release build, we'd still prefer to just drop the bug
// instead of trying to go on.
@@ -2729,8 +2832,6 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
R->Profile(ID);
// Lookup the equivance class. If there isn't one, create it.
- const BugType& BT = R->getBugType();
- Register(&BT);
void *InsertPos;
BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
@@ -2742,6 +2843,28 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
EQ->AddReport(std::move(R));
}
+void PathSensitiveBugReporter::emitReport(std::unique_ptr<BugReport> R) {
+ if (auto PR = dyn_cast<PathSensitiveBugReport>(R.get()))
+ if (const ExplodedNode *E = PR->getErrorNode()) {
+ // An error node must either be a sink or have a tag, otherwise
+ // it could get reclaimed before the path diagnostic is created.
+ assert((E->isSink() || E->getLocation().getTag()) &&
+ "Error node must either be a sink or have a tag");
+
+ const AnalysisDeclContext *DeclCtx =
+ E->getLocationContext()->getAnalysisDeclContext();
+ // The source of autosynthesized body can be handcrafted AST or a model
+ // file. The locations from handcrafted ASTs have no valid source
+ // locations and have to be discarded. Locations from model files should
+ // be preserved for processing and reporting.
+ if (DeclCtx->isBodyAutosynthesized() &&
+ !DeclCtx->isBodyAutosynthesizedFromModelFile())
+ return;
+ }
+
+ BugReporter::emitReport(std::move(R));
+}
+
//===----------------------------------------------------------------------===//
// Emitting reports in equivalence classes.
//===----------------------------------------------------------------------===//
@@ -2758,107 +2881,19 @@ struct FRIEC_WLItem {
} // namespace
-static const CFGBlock *findBlockForNode(const ExplodedNode *N) {
- ProgramPoint P = N->getLocation();
- if (auto BEP = P.getAs<BlockEntrance>())
- return BEP->getBlock();
-
- // Find the node's current statement in the CFG.
- if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
- return N->getLocationContext()->getAnalysisDeclContext()
- ->getCFGStmtMap()->getBlock(S);
-
- return nullptr;
-}
-
-// Returns true if by simply looking at the block, we can be sure that it
-// results in a sink during analysis. This is useful to know when the analysis
-// was interrupted, and we try to figure out if it would sink eventually.
-// There may be many more reasons why a sink would appear during analysis
-// (eg. checkers may generate sinks arbitrarily), but here we only consider
-// sinks that would be obvious by looking at the CFG.
-static bool isImmediateSinkBlock(const CFGBlock *Blk) {
- if (Blk->hasNoReturnElement())
- return true;
-
- // FIXME: Throw-expressions are currently generating sinks during analysis:
- // they're not supported yet, and also often used for actually terminating
- // the program. So we should treat them as sinks in this analysis as well,
- // at least for now, but once we have better support for exceptions,
- // we'd need to carefully handle the case when the throw is being
- // immediately caught.
- if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) {
- if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>())
- if (isa<CXXThrowExpr>(StmtElm->getStmt()))
- return true;
- return false;
- }))
- return true;
-
- return false;
-}
-
-// Returns true if by looking at the CFG surrounding the node's program
-// point, we can be sure that any analysis starting from this point would
-// eventually end with a sink. We scan the child CFG blocks in a depth-first
-// manner and see if all paths eventually end up in an immediate sink block.
-static bool isInevitablySinking(const ExplodedNode *N) {
- const CFG &Cfg = N->getCFG();
-
- const CFGBlock *StartBlk = findBlockForNode(N);
- if (!StartBlk)
- return false;
- if (isImmediateSinkBlock(StartBlk))
- return true;
-
- llvm::SmallVector<const CFGBlock *, 32> DFSWorkList;
- llvm::SmallPtrSet<const CFGBlock *, 32> Visited;
-
- DFSWorkList.push_back(StartBlk);
- while (!DFSWorkList.empty()) {
- const CFGBlock *Blk = DFSWorkList.back();
- DFSWorkList.pop_back();
- Visited.insert(Blk);
-
- // If at least one path reaches the CFG exit, it means that control is
- // returned to the caller. For now, say that we are not sure what
- // happens next. If necessary, this can be improved to analyze
- // the parent StackFrameContext's call site in a similar manner.
- if (Blk == &Cfg.getExit())
- return false;
-
- for (const auto &Succ : Blk->succs()) {
- if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) {
- if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) {
- // If the block has reachable child blocks that aren't no-return,
- // add them to the worklist.
- DFSWorkList.push_back(SuccBlk);
- }
- }
- }
- }
-
- // Nothing reached the exit. It can only mean one thing: there's no return.
- return true;
-}
-
-static BugReport *
-FindReportInEquivalenceClass(BugReportEquivClass& EQ,
- SmallVectorImpl<BugReport*> &bugReports) {
- BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
- assert(I != E);
- const BugType& BT = I->getBugType();
-
+BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass(
+ BugReportEquivClass &EQ, SmallVectorImpl<BugReport *> &bugReports) {
// If we don't need to suppress any of the nodes because they are
// post-dominated by a sink, simply add all the nodes in the equivalence class
// to 'Nodes'. Any of the reports will serve as a "representative" report.
+ assert(EQ.getReports().size() > 0);
+ const BugType& BT = EQ.getReports()[0]->getBugType();
if (!BT.isSuppressOnSink()) {
- BugReport *R = &*I;
- for (auto &I : EQ) {
- const ExplodedNode *N = I.getErrorNode();
- if (N) {
- R = &I;
- bugReports.push_back(R);
+ BugReport *R = EQ.getReports()[0].get();
+ for (auto &J : EQ.getReports()) {
+ if (auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) {
+ R = PR;
+ bugReports.push_back(PR);
}
}
return R;
@@ -2872,20 +2907,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// stack for very long paths.
BugReport *exampleReport = nullptr;
- for (; I != E; ++I) {
- const ExplodedNode *errorNode = I->getErrorNode();
-
- if (!errorNode)
+ for (const auto &I: EQ.getReports()) {
+ auto *R = dyn_cast<PathSensitiveBugReport>(I.get());
+ if (!R)
continue;
+
+ const ExplodedNode *errorNode = R->getErrorNode();
if (errorNode->isSink()) {
llvm_unreachable(
"BugType::isSuppressSink() should not be 'true' for sink end nodes");
}
// No successors? By definition this nodes isn't post-dominated by a sink.
if (errorNode->succ_empty()) {
- bugReports.push_back(&*I);
+ bugReports.push_back(R);
if (!exampleReport)
- exampleReport = &*I;
+ exampleReport = R;
continue;
}
@@ -2893,8 +2929,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
// to being post-dominated by a sink. This works better when the analysis
// is incomplete and we have never reached the no-return function call(s)
// that we'd inevitably bump into on this path.
- if (isInevitablySinking(errorNode))
- continue;
+ if (const CFGBlock *ErrorB = errorNode->getCFGBlock())
+ if (ErrorB->isInevitablySinking())
+ continue;
// 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.
@@ -2917,9 +2954,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
if (Succ->succ_empty()) {
// If we found an end-of-path node that is not a sink.
if (!Succ->isSink()) {
- bugReports.push_back(&*I);
+ bugReports.push_back(R);
if (!exampleReport)
- exampleReport = &*I;
+ exampleReport = R;
WL.clear();
break;
}
@@ -2950,7 +2987,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
SmallVector<BugReport*, 10> bugReports;
- BugReport *report = FindReportInEquivalenceClass(EQ, bugReports);
+ BugReport *report = findReportInEquivalenceClass(EQ, bugReports);
if (!report)
return;
@@ -2965,8 +3002,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
// 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>(
+ PathDiagnosticLocation L = report->getLocation();
+ auto piece = std::make_unique<PathDiagnosticEventPiece>(
L, report->getDescription());
for (SourceRange Range : report->getRanges())
piece->addRange(Range);
@@ -2993,10 +3030,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
Pieces.push_front(*I);
}
- // Get the meta data.
- const BugReport::ExtraTextList &Meta = report->getExtraText();
- for (const auto &i : Meta)
- PD->addMeta(i);
+ for (const auto &I : report->getFixits())
+ Pieces.back()->addFixit(I);
updateExecutedLinesWithDiagnosticPieces(*PD);
Consumer->HandlePathDiagnostic(std::move(PD));
@@ -3006,7 +3041,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) {
/// Insert all lines participating in the function signature \p Signature
/// into \p ExecutedLines.
static void populateExecutedLinesWithFunctionSignature(
- const Decl *Signature, SourceManager &SM,
+ const Decl *Signature, const SourceManager &SM,
FilesToLineNumsMap &ExecutedLines) {
SourceRange SignatureSourceRange;
const Stmt* Body = Signature->getBody();
@@ -3031,7 +3066,7 @@ static void populateExecutedLinesWithFunctionSignature(
}
static void populateExecutedLinesWithStmt(
- const Stmt *S, SourceManager &SM,
+ const Stmt *S, const SourceManager &SM,
FilesToLineNumsMap &ExecutedLines) {
SourceLocation Loc = S->getSourceRange().getBegin();
if (!Loc.isValid())
@@ -3045,8 +3080,8 @@ static void populateExecutedLinesWithStmt(
/// \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>();
+findExecutedLines(const SourceManager &SM, const ExplodedNode *N) {
+ auto ExecutedLines = std::make_unique<FilesToLineNumsMap>();
while (N) {
if (N->getFirstPred() == nullptr) {
@@ -3057,7 +3092,7 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) {
// Inlined function: show signature.
const Decl* D = CE->getCalleeContext()->getDecl();
populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines);
- } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+ } else if (const Stmt *S = N->getStmtForDiagnostics()) {
populateExecutedLinesWithStmt(S, SM, *ExecutedLines);
// Show extra context for some parent kinds.
@@ -3082,16 +3117,89 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) {
std::unique_ptr<DiagnosticForConsumerMapTy>
BugReporter::generateDiagnosticForConsumerMap(
- BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers,
+ BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers,
ArrayRef<BugReport *> bugReports) {
+ auto *basicReport = cast<BasicBugReport>(exampleReport);
+ auto Out = std::make_unique<DiagnosticForConsumerMapTy>();
+ for (auto *Consumer : consumers)
+ (*Out)[Consumer] = generateDiagnosticForBasicReport(basicReport);
+ return Out;
+}
- if (!report->isPathSensitive()) {
- auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>();
- for (auto *Consumer : consumers)
- (*Out)[Consumer] = generateEmptyDiagnosticForReport(report,
- getSourceManager());
- return Out;
+static PathDiagnosticCallPiece *
+getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
+ const SourceManager &SMgr) {
+ SourceLocation CallLoc = CP->callEnter.asLocation();
+
+ // If the call is within a macro, don't do anything (for now).
+ if (CallLoc.isMacroID())
+ return nullptr;
+
+ 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 (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr))
+ return CP;
+
+ const PathPieces &Path = CP->path;
+ if (Path.empty())
+ return nullptr;
+
+ // Check if the last piece in the callee path is a call to a function outside
+ // of the main file.
+ if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
+ return getFirstStackedCallToHeaderFile(CPInner, SMgr);
+
+ // Otherwise, the last piece is in the main file.
+ return nullptr;
+}
+
+static void resetDiagnosticLocationToMainFile(PathDiagnostic &PD) {
+ if (PD.path.empty())
+ return;
+
+ PathDiagnosticPiece *LastP = PD.path.back().get();
+ assert(LastP);
+ const SourceManager &SMgr = LastP->getLocation().getManager();
+
+ // We only need to check if the report ends inside headers, if the last piece
+ // is a call piece.
+ if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
+ CP = getFirstStackedCallToHeaderFile(CP, SMgr);
+ if (CP) {
+ // Mark the piece.
+ CP->setAsLastInMainSourceFile();
+
+ // Update the path diagnostic message.
+ const auto *ND = dyn_cast<NamedDecl>(CP->getCallee());
+ if (ND) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << " (within a call to '" << ND->getDeclName() << "')";
+ PD.appendToDesc(os.str());
+ }
+
+ // Reset the report containing declaration and location.
+ PD.setDeclWithIssue(CP->getCaller());
+ PD.setLocation(CP->getLocation());
+
+ return;
+ }
}
+}
+
+
+
+std::unique_ptr<DiagnosticForConsumerMapTy>
+PathSensitiveBugReporter::generateDiagnosticForConsumerMap(
+ BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> bugReports) {
+ std::vector<BasicBugReport *> BasicBugReports;
+ std::vector<PathSensitiveBugReport *> PathSensitiveBugReports;
+ if (isa<BasicBugReport>(exampleReport))
+ return BugReporter::generateDiagnosticForConsumerMap(exampleReport,
+ consumers, bugReports);
// Generate the full path sensitive diagnostic, using the generation scheme
// specified by the PathDiagnosticConsumer. Note that we have to generate
@@ -3099,8 +3207,13 @@ BugReporter::generateDiagnosticForConsumerMap(
// 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);
+
+ // Avoid copying the whole array because there may be a lot of reports.
+ ArrayRef<PathSensitiveBugReport *> convertedArrayOfReports(
+ reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.begin()),
+ reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.end()));
+ std::unique_ptr<DiagnosticForConsumerMapTy> Out = generatePathDiagnostics(
+ consumers, convertedArrayOfReports);
if (Out->empty())
return Out;
@@ -3109,40 +3222,43 @@ BugReporter::generateDiagnosticForConsumerMap(
// 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();
+ const AnalyzerOptions &Opts = getAnalyzerOptions();
for (auto const &P : *Out)
if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll)
- P.second->resetDiagnosticLocationToMainFile();
+ resetDiagnosticLocationToMainFile(*P.second);
return Out;
}
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
- const CheckerBase *Checker,
- StringRef Name, StringRef Category,
- StringRef Str, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges) {
- EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str,
- Loc, Ranges);
+ const CheckerBase *Checker, StringRef Name,
+ StringRef Category, StringRef Str,
+ PathDiagnosticLocation Loc,
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
+ EmitBasicReport(DeclWithIssue, Checker->getCheckerName(), Name, Category, Str,
+ Loc, Ranges, Fixits);
}
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
- CheckName CheckName,
+ CheckerNameRef CheckName,
StringRef name, StringRef category,
StringRef str, PathDiagnosticLocation Loc,
- ArrayRef<SourceRange> Ranges) {
+ ArrayRef<SourceRange> Ranges,
+ ArrayRef<FixItHint> Fixits) {
// 'BT' is owned by BugReporter.
BugType *BT = getBugTypeForName(CheckName, name, category);
- auto R = llvm::make_unique<BugReport>(*BT, str, Loc);
+ auto R = std::make_unique<BasicBugReport>(*BT, str, Loc);
R->setDeclWithIssue(DeclWithIssue);
- for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
- I != E; ++I)
- R->addRange(*I);
+ for (const auto &SR : Ranges)
+ R->addRange(SR);
+ for (const auto &FH : Fixits)
+ R->addFixItHint(FH);
emitReport(std::move(R));
}
-BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name,
- StringRef category) {
+BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName,
+ StringRef name, StringRef category) {
SmallString<136> fullDesc;
llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
<< ":" << category;
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 250793c4baff0..7ba93b858baf5 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -26,6 +26,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
@@ -34,7 +35,6 @@
#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"
@@ -180,21 +180,60 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
RLCV->getStore() == RightNode->getState()->getStore();
}
-static Optional<const llvm::APSInt *>
-getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
+static Optional<SVal> getSValForVar(const Expr *CondVarExpr,
+ const ExplodedNode *N) {
ProgramStateRef State = N->getState();
const LocationContext *LCtx = N->getLocationContext();
+ assert(CondVarExpr);
+ CondVarExpr = CondVarExpr->IgnoreImpCasts();
+
// The declaration of the value may rely on a pointer so take its l-value.
- if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(CondVarExpr)) {
- if (const auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
- SVal DeclSVal = State->getSVal(State->getLValue(VD, LCtx));
- if (auto DeclCI = DeclSVal.getAs<nonloc::ConcreteInt>())
- return &DeclCI->getValue();
- }
- }
+ // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may
+ // evaluate to a FieldRegion when it refers to a declaration of a lambda
+ // capture variable. We most likely need to duplicate that logic here.
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(CondVarExpr))
+ if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))
+ return State->getSVal(State->getLValue(VD, LCtx));
+
+ if (const auto *ME = dyn_cast<MemberExpr>(CondVarExpr))
+ if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()))
+ if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>())
+ return State->getRawSVal(*FieldL, FD->getType());
+
+ return None;
+}
+
+static Optional<const llvm::APSInt *>
+getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
- return {};
+ if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (auto CI = V->getAs<nonloc::ConcreteInt>())
+ return &CI->getValue();
+ return None;
+}
+
+static bool isVarAnInterestingCondition(const Expr *CondVarExpr,
+ const ExplodedNode *N,
+ const PathSensitiveBugReport *B) {
+ // Even if this condition is marked as interesting, it isn't *that*
+ // interesting if it didn't happen in a nested stackframe, the user could just
+ // follow the arrows.
+ if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame()))
+ return false;
+
+ if (Optional<SVal> V = getSValForVar(CondVarExpr, N))
+ if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V))
+ return *K == bugreporter::TrackingKind::Condition;
+
+ return false;
+}
+
+static bool isInterestingExpr(const Expr *E, const ExplodedNode *N,
+ const PathSensitiveBugReport *B) {
+ if (Optional<SVal> V = getSValForVar(E, N))
+ return B->getInterestingnessKind(*V).hasValue();
+ return false;
}
/// \return name of the macro inside the location \p Loc.
@@ -255,21 +294,21 @@ static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest,
// Implementation of BugReporterVisitor.
//===----------------------------------------------------------------------===//
-std::shared_ptr<PathDiagnosticPiece>
-BugReporterVisitor::getEndPath(BugReporterContext &,
- const ExplodedNode *, BugReport &) {
+PathDiagnosticPieceRef BugReporterVisitor::getEndPath(BugReporterContext &,
+ const ExplodedNode *,
+ PathSensitiveBugReport &) {
return nullptr;
}
-void
-BugReporterVisitor::finalizeVisitor(BugReporterContext &,
- const ExplodedNode *, BugReport &) {}
-
-std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
- BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
+void BugReporterVisitor::finalizeVisitor(BugReporterContext &,
+ const ExplodedNode *,
+ PathSensitiveBugReport &) {}
+PathDiagnosticPieceRef
+BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
+ const ExplodedNode *EndPathNode,
+ const PathSensitiveBugReport &BR) {
+ PathDiagnosticLocation L = BR.getLocation();
const auto &Ranges = BR.getRanges();
// Only add the statement itself as a range if we didn't specify any
@@ -297,6 +336,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
MemRegionManager &MmrMgr;
const SourceManager &SM;
const PrintingPolicy &PP;
+ bugreporter::TrackingKind TKind;
/// Recursion limit for dereferencing fields when looking for the
/// region of interest.
@@ -317,10 +357,10 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
using RegionVector = SmallVector<const MemRegion *, 5>;
public:
- NoStoreFuncVisitor(const SubRegion *R)
+ NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
: RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
SM(MmrMgr.getContext().getSourceManager()),
- PP(MmrMgr.getContext().getPrintingPolicy()) {}
+ PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
@@ -333,9 +373,9 @@ public:
return static_cast<void *>(&Tag);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BR,
- BugReport &R) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BR,
+ PathSensitiveBugReport &R) override;
private:
/// Attempts to find the region of interest in a given record decl,
@@ -368,11 +408,11 @@ private:
/// either emit a note or suppress the report enirely.
/// \return Diagnostics piece for region not modified in the current function,
/// if it decides to emit one.
- std::shared_ptr<PathDiagnosticPiece>
- maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N,
- const RegionVector &FieldChain, const MemRegion *MatchedRegion,
- StringRef FirstElement, bool FirstIsReferenceType,
- unsigned IndirectionLevel);
+ PathDiagnosticPieceRef
+ maybeEmitNote(PathSensitiveBugReport &R, const CallEvent &Call,
+ const ExplodedNode *N, const RegionVector &FieldChain,
+ const MemRegion *MatchedRegion, StringRef FirstElement,
+ bool FirstIsReferenceType, unsigned IndirectionLevel);
/// Pretty-print region \p MatchedRegion to \p os.
/// \return Whether printing succeeded.
@@ -501,9 +541,9 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord(
return None;
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
- BugReport &R) {
+ PathSensitiveBugReport &R) {
const LocationContext *Ctx = N->getLocationContext();
const StackFrameContext *SCtx = Ctx->getStackFrame();
@@ -611,8 +651,11 @@ void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) {
} while (N);
}
-std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote(
- BugReport &R, const CallEvent &Call, const ExplodedNode *N,
+static llvm::StringLiteral WillBeUsedForACondition =
+ ", which participates in a condition later";
+
+PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
+ PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N,
const RegionVector &FieldChain, const MemRegion *MatchedRegion,
StringRef FirstElement, bool FirstIsReferenceType,
unsigned IndirectionLevel) {
@@ -657,6 +700,8 @@ std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote(
return nullptr;
os << "'";
+ if (TKind == bugreporter::TrackingKind::Condition)
+ os << WillBeUsedForACondition;
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
@@ -752,13 +797,12 @@ class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor {
bool WasModified = false;
public:
- MacroNullReturnSuppressionVisitor(const SubRegion *R,
- const SVal V) : RegionOfInterest(R),
- ValueAtDereference(V) {}
+ MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V)
+ : RegionOfInterest(R), ValueAtDereference(V) {}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override {
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override {
if (WasModified)
return nullptr;
@@ -784,12 +828,12 @@ public:
static void addMacroVisitorIfNecessary(
const ExplodedNode *N, const MemRegion *R,
- bool EnableNullFPSuppression, BugReport &BR,
+ bool EnableNullFPSuppression, PathSensitiveBugReport &BR,
const SVal V) {
AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
if (EnableNullFPSuppression &&
Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>())
- BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>(
+ BR.addVisitor(std::make_unique<MacroNullReturnSuppressionVisitor>(
R->getAs<SubRegion>(), V));
}
@@ -806,7 +850,7 @@ 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) {
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ const Stmt *S = N->getStmtForDiagnostics();
ProgramStateRef State = N->getState();
auto *LCtx = N->getLocationContext();
if (!S)
@@ -841,7 +885,7 @@ 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 BugReporterVisitor {
- const StackFrameContext *StackFrame;
+ const StackFrameContext *CalleeSFC;
enum {
Initial,
MaybeUnsuppress,
@@ -851,13 +895,13 @@ class ReturnVisitor : public BugReporterVisitor {
bool EnableNullFPSuppression;
bool ShouldInvalidate = true;
AnalyzerOptions& Options;
+ bugreporter::TrackingKind TKind;
public:
- ReturnVisitor(const StackFrameContext *Frame,
- bool Suppressed,
- AnalyzerOptions &Options)
- : StackFrame(Frame), EnableNullFPSuppression(Suppressed),
- Options(Options) {}
+ ReturnVisitor(const StackFrameContext *Frame, bool Suppressed,
+ AnalyzerOptions &Options, bugreporter::TrackingKind TKind)
+ : CalleeSFC(Frame), EnableNullFPSuppression(Suppressed),
+ Options(Options), TKind(TKind) {}
static void *getTag() {
static int Tag = 0;
@@ -866,7 +910,7 @@ public:
void Profile(llvm::FoldingSetNodeID &ID) const override {
ID.AddPointer(ReturnVisitor::getTag());
- ID.AddPointer(StackFrame);
+ ID.AddPointer(CalleeSFC);
ID.AddBoolean(EnableNullFPSuppression);
}
@@ -878,8 +922,9 @@ public:
/// the statement is a call that was inlined, we add the visitor to the
/// bug report, so it can print a note later.
static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S,
- BugReport &BR,
- bool InEnableNullFPSuppression) {
+ PathSensitiveBugReport &BR,
+ bool InEnableNullFPSuppression,
+ bugreporter::TrackingKind TKind) {
if (!CallEvent::isCallStmt(S))
return;
@@ -950,17 +995,16 @@ public:
if (Optional<Loc> RetLoc = RetVal.getAs<Loc>())
EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
- BR.markInteresting(CalleeContext);
- BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext,
+ BR.addVisitor(std::make_unique<ReturnVisitor>(CalleeContext,
EnableNullFPSuppression,
- Options));
+ Options, TKind));
}
- std::shared_ptr<PathDiagnosticPiece>
- visitNodeInitial(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+ PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
// Only print a message at the interesting return statement.
- if (N->getLocationContext() != StackFrame)
+ if (N->getLocationContext() != CalleeSFC)
return nullptr;
Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
@@ -974,7 +1018,7 @@ public:
// Okay, we're at the right return statement, but do we have the return
// value available?
ProgramStateRef State = N->getState();
- SVal V = State->getSVal(Ret, StackFrame);
+ SVal V = State->getSVal(Ret, CalleeSFC);
if (V.isUnknownOrUndef())
return nullptr;
@@ -1001,13 +1045,16 @@ public:
RetE = RetE->IgnoreParenCasts();
- // If we're returning 0, we should track where that 0 came from.
- bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression);
+ // Let's track the return value.
+ bugreporter::trackExpressionValue(
+ N, RetE, BR, TKind, EnableNullFPSuppression);
// Build an appropriate message based on the return value.
SmallString<64> Msg;
llvm::raw_svector_ostream Out(Msg);
+ bool WouldEventBeMeaningless = false;
+
if (State->isNull(V).isConstrainedTrue()) {
if (V.getAs<Loc>()) {
@@ -1030,10 +1077,19 @@ public:
} else {
if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
Out << "Returning the value " << CI->getValue();
- } else if (V.getAs<Loc>()) {
- Out << "Returning pointer";
} else {
- Out << "Returning value";
+ // There is nothing interesting about returning a value, when it is
+ // plain value without any constraints, and the function is guaranteed
+ // to return that every time. We could use CFG::isLinear() here, but
+ // constexpr branches are obvious to the compiler, not necesserily to
+ // the programmer.
+ if (N->getCFG().size() == 3)
+ WouldEventBeMeaningless = true;
+
+ if (V.getAs<Loc>())
+ Out << "Returning pointer";
+ else
+ Out << "Returning value";
}
}
@@ -1052,26 +1108,36 @@ public:
Out << " (loaded from '" << *DD << "')";
}
- PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame);
+ PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC);
if (!L.isValid() || !L.asLocation().isValid())
return nullptr;
- return std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
+ if (TKind == bugreporter::TrackingKind::Condition)
+ Out << WillBeUsedForACondition;
+
+ auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
+
+ // If we determined that the note is meaningless, make it prunable, and
+ // don't mark the stackframe interesting.
+ if (WouldEventBeMeaningless)
+ EventPiece->setPrunable(true);
+ else
+ BR.markInteresting(CalleeSFC);
+
+ return EventPiece;
}
- std::shared_ptr<PathDiagnosticPiece>
- visitNodeMaybeUnsuppress(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
-#ifndef NDEBUG
+ PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
-#endif
// Are we at the entry node for this call?
Optional<CallEnter> CE = N->getLocationAs<CallEnter>();
if (!CE)
return nullptr;
- if (CE->getCalleeContext() != StackFrame)
+ if (CE->getCalleeContext() != CalleeSFC)
return nullptr;
Mode = Satisfied;
@@ -1083,7 +1149,7 @@ public:
CallEventManager &CallMgr = StateMgr.getCallEventManager();
ProgramStateRef State = N->getState();
- CallEventRef<> Call = CallMgr.getCaller(StackFrame, State);
+ CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State);
for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
if (!ArgV)
@@ -1097,7 +1163,7 @@ public:
if (!State->isNull(*ArgV).isConstrainedTrue())
continue;
- if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression))
+ if (trackExpressionValue(N, ArgE, BR, TKind, EnableNullFPSuppression))
ShouldInvalidate = false;
// If we /can't/ track the null pointer, we should err on the side of
@@ -1108,9 +1174,9 @@ public:
return nullptr;
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override {
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override {
switch (Mode) {
case Initial:
return visitNodeInitial(N, BRC, BR);
@@ -1124,9 +1190,9 @@ public:
}
void finalizeVisitor(BugReporterContext &, const ExplodedNode *,
- BugReport &BR) override {
+ PathSensitiveBugReport &BR) override {
if (EnableNullFPSuppression && ShouldInvalidate)
- BR.markInvalid(ReturnVisitor::getTag(), StackFrame);
+ BR.markInvalid(ReturnVisitor::getTag(), CalleeSFC);
}
};
@@ -1141,6 +1207,7 @@ void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(&tag);
ID.AddPointer(R);
ID.Add(V);
+ ID.AddInteger(static_cast<int>(TKind));
ID.AddBoolean(EnableNullFPSuppression);
}
@@ -1246,9 +1313,8 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream& os,
}
/// Show default diagnostics for storing bad region.
-static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os,
- const MemRegion *R,
- SVal V) {
+static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &os,
+ const MemRegion *R, SVal V) {
if (V.getAs<loc::ConcreteInt>()) {
bool b = false;
if (R->isBoundable()) {
@@ -1291,9 +1357,10 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os,
}
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
- BugReporterContext &BRC, BugReport &BR) {
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
if (Satisfied)
return nullptr;
@@ -1314,7 +1381,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
// should track the initializer expression.
if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) {
const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
- if (FieldReg && FieldReg == R) {
+ if (FieldReg == R) {
StoreSite = Pred;
InitE = PIP->getInitializer()->getInit();
}
@@ -1351,14 +1418,19 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
if (const auto *VR = dyn_cast<VarRegion>(R)) {
- const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+ if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) {
+ ProgramStateManager &StateMgr = BRC.getStateManager();
+ CallEventManager &CallMgr = StateMgr.getCallEventManager();
- ProgramStateManager &StateMgr = BRC.getStateManager();
- CallEventManager &CallMgr = StateMgr.getCallEventManager();
-
- CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(),
- Succ->getState());
- InitE = Call->getArgExpr(Param->getFunctionScopeIndex());
+ CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(),
+ Succ->getState());
+ InitE = Call->getArgExpr(Param->getFunctionScopeIndex());
+ } else {
+ // Handle Objective-C 'self'.
+ assert(isa<ImplicitParamDecl>(VR->getDecl()));
+ InitE = cast<ObjCMessageExpr>(CE->getCalleeContext()->getCallSite())
+ ->getInstanceReceiver()->IgnoreParenCasts();
+ }
IsParam = true;
}
}
@@ -1371,22 +1443,23 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (!StoreSite)
return nullptr;
+
Satisfied = true;
// If we have an expression that provided the value, try to track where it
// came from.
if (InitE) {
- if (V.isUndef() ||
- V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) {
- if (!IsParam)
- InitE = InitE->IgnoreParenCasts();
- bugreporter::trackExpressionValue(StoreSite, InitE, BR,
- EnableNullFPSuppression);
- }
- ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(),
- BR, EnableNullFPSuppression);
+ if (!IsParam)
+ InitE = InitE->IgnoreParenCasts();
+
+ bugreporter::trackExpressionValue(
+ StoreSite, InitE, BR, TKind, EnableNullFPSuppression);
}
+ if (TKind == TrackingKind::Condition &&
+ !OriginSFC->isParentOf(StoreSite->getStackFrame()))
+ return nullptr;
+
// Okay, we've found the binding. Emit an appropriate message.
SmallString<256> sbuf;
llvm::raw_svector_ostream os(sbuf);
@@ -1411,8 +1484,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
if (auto KV = State->getSVal(OriginalR).getAs<KnownSVal>())
- BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, OriginalR, EnableNullFPSuppression));
+ BR.addVisitor(std::make_unique<FindLastStoreBRVisitor>(
+ *KV, OriginalR, EnableNullFPSuppression, TKind, OriginSFC));
}
}
}
@@ -1428,6 +1501,9 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (os.str().empty())
showBRDefaultDiagnostics(os, R, V);
+ if (TKind == bugreporter::TrackingKind::Condition)
+ os << WillBeUsedForACondition;
+
// Construct a new PathDiagnosticPiece.
ProgramPoint P = StoreSite->getLocation();
PathDiagnosticLocation L;
@@ -1467,9 +1543,8 @@ bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const {
return (bool)N->getState()->assume(Constraint, !Assumption);
}
-std::shared_ptr<PathDiagnosticPiece>
-TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &) {
+PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
const ExplodedNode *PrevN = N->getFirstPred();
if (IsSatisfied)
return nullptr;
@@ -1547,10 +1622,10 @@ const char *SuppressInlineDefensiveChecksVisitor::getTag() {
return "IDCVisitor";
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
BugReporterContext &BRC,
- BugReport &BR) {
+ PathSensitiveBugReport &BR) {
const ExplodedNode *Pred = Succ->getFirstPred();
if (IsSatisfied)
return nullptr;
@@ -1649,24 +1724,12 @@ public:
ID.AddPointer(&x);
}
- std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC,
- BugReport &BR) override;
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
};
} // end of anonymous namespace
-static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) {
- if (auto SP = Node->getLocationAs<StmtPoint>()) {
- const Stmt *S = SP->getStmt();
- assert(S);
-
- return const_cast<CFGBlock *>(Node->getLocationContext()
- ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S));
- }
-
- return nullptr;
-}
-
static std::shared_ptr<PathDiagnosticEventPiece>
constructDebugPieceForTrackedCondition(const Expr *Cond,
const ExplodedNode *N,
@@ -1687,34 +1750,75 @@ constructDebugPieceForTrackedCondition(const Expr *Cond,
(Twine() + "Tracking condition '" + ConditionText + "'").str());
}
-std::shared_ptr<PathDiagnosticPiece>
+static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) {
+ if (B->succ_size() != 2)
+ return false;
+
+ const CFGBlock *Then = B->succ_begin()->getReachableBlock();
+ const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock();
+
+ if (!Then || !Else)
+ return false;
+
+ if (Then->isInevitablySinking() != Else->isInevitablySinking())
+ return true;
+
+ // For the following condition the following CFG would be built:
+ //
+ // ------------->
+ // / \
+ // [B1] -> [B2] -> [B3] -> [sink]
+ // assert(A && B || C); \ \
+ // -----------> [go on with the execution]
+ //
+ // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block
+ // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we
+ // reached the end of the condition!
+ if (const Stmt *ElseCond = Else->getTerminatorCondition())
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(ElseCond))
+ if (BinOp->isLogicalOp())
+ return isAssertlikeBlock(Else, Context);
+
+ return false;
+}
+
+PathDiagnosticPieceRef
TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC,
- BugReport &BR) {
+ PathSensitiveBugReport &BR) {
// We can only reason about control dependencies within the same stack frame.
if (Origin->getStackFrame() != N->getStackFrame())
return nullptr;
- CFGBlock *NB = GetRelevantBlock(N);
+ CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock());
// Skip if we already inspected this block.
if (!VisitedBlocks.insert(NB).second)
return nullptr;
- CFGBlock *OriginB = GetRelevantBlock(Origin);
+ CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock());
// TODO: Cache CFGBlocks for each ExplodedNode.
if (!OriginB || !NB)
return nullptr;
+ if (isAssertlikeBlock(NB, BRC.getASTContext()))
+ return nullptr;
+
if (ControlDeps.isControlDependent(OriginB, NB)) {
+ // We don't really want to explain for range loops. Evidence suggests that
+ // the only thing that leads to is the addition of calls to operator!=.
+ if (llvm::isa_and_nonnull<CXXForRangeStmt>(NB->getTerminatorStmt()))
+ return nullptr;
+
if (const Expr *Condition = NB->getLastCondition()) {
// Keeping track of the already tracked conditions on a visitor level
// isn't sufficient, because a new visitor is created for each tracked
// expression, hence the BugReport level set.
if (BR.addTrackedCondition(N)) {
bugreporter::trackExpressionValue(
- N, Condition, BR, /*EnableNullFPSuppression=*/false);
+ N, Condition, BR, bugreporter::TrackingKind::Condition,
+ /*EnableNullFPSuppression=*/false);
return constructDebugPieceForTrackedCondition(Condition, N, BRC);
}
}
@@ -1819,7 +1923,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex,
static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
const Expr *Inner) {
while (N) {
- if (PathDiagnosticLocation::getStmt(N) == Inner)
+ if (N->getStmtForDiagnostics() == Inner)
return N;
N = N->getFirstPred();
}
@@ -1827,8 +1931,11 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
}
bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
- const Expr *E, BugReport &report,
+ const Expr *E,
+ PathSensitiveBugReport &report,
+ bugreporter::TrackingKind TKind,
bool EnableNullFPSuppression) {
+
if (!E || !InputNode)
return false;
@@ -1838,6 +1945,7 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
return false;
ProgramStateRef LVState = LVNode->getState();
+ const StackFrameContext *SFC = LVNode->getStackFrame();
// We only track expressions if we believe that they are important. Chances
// are good that control dependencies to the tracking point are also improtant
@@ -1845,19 +1953,20 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
// TODO: Shouldn't we track control dependencies of every bug location, rather
// than only tracked expressions?
if (LVState->getAnalysisManager().getAnalyzerOptions().ShouldTrackConditions)
- report.addVisitor(llvm::make_unique<TrackControlDependencyCondBRVisitor>(
+ report.addVisitor(std::make_unique<TrackControlDependencyCondBRVisitor>(
InputNode));
// The message send could be nil due to the receiver being nil.
// 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(Inner, LVNode))
- trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression);
+ trackExpressionValue(
+ LVNode, Receiver, report, TKind, EnableNullFPSuppression);
// Track the index if this is an array subscript.
if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner))
trackExpressionValue(
- LVNode, Arr->getIdx(), report, /*EnableNullFPSuppression*/ false);
+ LVNode, Arr->getIdx(), report, TKind, /*EnableNullFPSuppression*/false);
// See if the expression we're interested refers to a variable.
// If so, we can track both its contents and constraints on its value.
@@ -1872,8 +1981,8 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
// got initialized.
if (RR && !LVIsNull)
if (auto KV = LVal.getAs<KnownSVal>())
- report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, RR, EnableNullFPSuppression));
+ report.addVisitor(std::make_unique<FindLastStoreBRVisitor>(
+ *KV, RR, EnableNullFPSuppression, TKind, SFC));
// In case of C++ references, we want to differentiate between a null
// reference and reference to null pointer.
@@ -1887,17 +1996,18 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
// 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)));
+ std::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R), TKind));
MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
LVNode, R, EnableNullFPSuppression, report, V);
- report.markInteresting(V);
- report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R));
+ report.markInteresting(V, TKind);
+ report.addVisitor(std::make_unique<UndefOrNullArgVisitor>(R));
- // If the contents are symbolic, find out when they became null.
- if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true))
- report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
+ // If the contents are symbolic and null, find out when they became null.
+ if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true))
+ if (LVState->isNull(V).isConstrainedTrue())
+ report.addVisitor(std::make_unique<TrackConstraintBRVisitor>(
V.castAs<DefinedSVal>(), false));
// Add visitor, which will suppress inline defensive checks.
@@ -1905,12 +2015,12 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() &&
EnableNullFPSuppression)
report.addVisitor(
- llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV,
+ std::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV,
LVNode));
if (auto KV = V.getAs<KnownSVal>())
- report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, R, EnableNullFPSuppression));
+ report.addVisitor(std::make_unique<FindLastStoreBRVisitor>(
+ *KV, R, EnableNullFPSuppression, TKind, SFC));
return true;
}
}
@@ -1920,40 +2030,43 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext());
ReturnVisitor::addVisitorIfNecessary(
- LVNode, Inner, report, EnableNullFPSuppression);
+ LVNode, Inner, report, EnableNullFPSuppression, TKind);
// Is it a symbolic value?
if (auto L = V.getAs<loc::MemRegionVal>()) {
- report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion()));
-
// FIXME: this is a hack for fixing a later crash when attempting to
// dereference a void* pointer.
// We should not try to dereference pointers at all when we don't care
// what is written inside the pointer.
bool CanDereference = true;
- if (const auto *SR = dyn_cast<SymbolicRegion>(L->getRegion()))
+ if (const auto *SR = L->getRegionAs<SymbolicRegion>()) {
if (SR->getSymbol()->getType()->getPointeeType()->isVoidType())
CanDereference = false;
+ } else if (L->getRegionAs<AllocaRegion>())
+ CanDereference = false;
// 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 (ExplodedGraph::isInterestingLValueExpr(Inner)) {
+ if (ExplodedGraph::isInterestingLValueExpr(Inner))
RVal = LVState->getRawSVal(L.getValue(), Inner->getType());
- } else if (CanDereference) {
+ else if (CanDereference)
RVal = LVState->getSVal(L->getRegion());
- }
- if (CanDereference)
+ if (CanDereference) {
+ report.addVisitor(
+ std::make_unique<UndefOrNullArgVisitor>(L->getRegion()));
+
if (auto KV = RVal.getAs<KnownSVal>())
- report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- *KV, L->getRegion(), EnableNullFPSuppression));
+ report.addVisitor(std::make_unique<FindLastStoreBRVisitor>(
+ *KV, L->getRegion(), EnableNullFPSuppression, TKind, SFC));
+ }
const MemRegion *RegionRVal = RVal.getAsRegion();
if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) {
- report.markInteresting(RegionRVal);
- report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
+ report.markInteresting(RegionRVal, TKind);
+ report.addVisitor(std::make_unique<TrackConstraintBRVisitor>(
loc::MemRegionVal(RegionRVal), /*assumption=*/false));
}
}
@@ -1978,9 +2091,9 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
return nullptr;
}
-std::shared_ptr<PathDiagnosticPiece>
-NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+PathDiagnosticPieceRef
+NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
Optional<PreStmt> P = N->getLocationAs<PreStmt>();
if (!P)
return nullptr;
@@ -2006,8 +2119,9 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
// The receiver was nil, and hence the method was skipped.
// Register a BugReporterVisitor to issue a message telling us how
// the receiver was null.
- bugreporter::trackExpressionValue(N, Receiver, BR,
- /*EnableNullFPSuppression*/ false);
+ bugreporter::trackExpressionValue(
+ N, Receiver, BR, bugreporter::TrackingKind::Thorough,
+ /*EnableNullFPSuppression*/ false);
// Issue a message saying that the method was skipped.
PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
N->getLocationContext());
@@ -2015,57 +2129,16 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
}
//===----------------------------------------------------------------------===//
-// Implementation of FindLastStoreBRVisitor.
-//===----------------------------------------------------------------------===//
-
-// Registers every VarDecl inside a Stmt with a last store visitor.
-void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
- const Stmt *S,
- bool EnableNullFPSuppression) {
- const ExplodedNode *N = BR.getErrorNode();
- std::deque<const Stmt *> WorkList;
- WorkList.push_back(S);
-
- while (!WorkList.empty()) {
- const Stmt *Head = WorkList.front();
- WorkList.pop_front();
-
- ProgramStateManager &StateMgr = N->getState()->getStateManager();
-
- 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 = N->getSVal(S);
-
- if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) {
- // Register a new visitor with the BugReport.
- BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
- V.castAs<KnownSVal>(), R, EnableNullFPSuppression));
- }
- }
- }
-
- for (const Stmt *SubStmt : Head->children())
- WorkList.push_back(SubStmt);
- }
-}
-
-//===----------------------------------------------------------------------===//
// Visitor that tries to report interesting diagnostics from conditions.
//===----------------------------------------------------------------------===//
/// Return the tag associated with this visitor. This tag will be used
/// to make all PathDiagnosticPieces created by this visitor.
-const char *ConditionBRVisitor::getTag() {
- return "ConditionBRVisitor";
-}
+const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor"; }
-std::shared_ptr<PathDiagnosticPiece>
-ConditionBRVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+PathDiagnosticPieceRef
+ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
auto piece = VisitNodeImpl(N, BRC, BR);
if (piece) {
piece->setTag(getTag());
@@ -2075,9 +2148,10 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N,
return piece;
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
ProgramPoint ProgPoint = N->getLocation();
const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags =
ExprEngine::geteagerlyAssumeBinOpBifurcationTags();
@@ -2113,9 +2187,10 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
return nullptr;
}
-std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
+PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator(
const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk,
- const CFGBlock *dstBlk, BugReport &R, BugReporterContext &BRC) {
+ const CFGBlock *dstBlk, PathSensitiveBugReport &R,
+ BugReporterContext &BRC) {
const Expr *Cond = nullptr;
// In the code below, Term is a CFG terminator and Cond is a branch condition
@@ -2170,10 +2245,10 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
return VisitTrueTest(Cond, BRC, R, N, TookTrue);
}
-std::shared_ptr<PathDiagnosticPiece>
+PathDiagnosticPieceRef
ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
- BugReport &R, const ExplodedNode *N,
- bool TookTrue) {
+ PathSensitiveBugReport &R,
+ const ExplodedNode *N, bool TookTrue) {
ProgramStateRef CurrentState = N->getState();
ProgramStateRef PrevState = N->getFirstPred()->getState();
const LocationContext *LCtx = N->getLocationContext();
@@ -2243,7 +2318,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
const Expr *ParentEx,
raw_ostream &Out,
BugReporterContext &BRC,
- BugReport &report,
+ PathSensitiveBugReport &report,
const ExplodedNode *N,
Optional<bool> &prunable,
bool IsSameFieldName) {
@@ -2258,7 +2333,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
SourceLocation EndLoc = OriginalExpr->getEndLoc();
if (BeginLoc.isMacroID() && EndLoc.isMacroID()) {
- SourceManager &SM = BRC.getSourceManager();
+ const SourceManager &SM = BRC.getSourceManager();
const LangOptions &LO = BRC.getASTContext().getLangOpts();
if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) &&
Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) {
@@ -2326,21 +2401,22 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
return false;
}
-std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC,
- BugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
+ PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming) {
bool shouldInvert = false;
Optional<bool> shouldPrune;
// Check if the field name of the MemberExprs is ambiguous. Example:
// " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
bool IsSameFieldName = false;
- if (const auto *LhsME =
- dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts()))
- if (const auto *RhsME =
- dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts()))
- IsSameFieldName = LhsME->getMemberDecl()->getName() ==
- RhsME->getMemberDecl()->getName();
+ const auto *LhsME = dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts());
+ const auto *RhsME = dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts());
+
+ if (LhsME && RhsME)
+ IsSameFieldName =
+ LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName();
SmallString<128> LhsString, RhsString;
{
@@ -2410,25 +2486,44 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
Out << (shouldInvert ? LhsString : RhsString);
const LocationContext *LCtx = N->getLocationContext();
- PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ const SourceManager &SM = BRC.getSourceManager();
+
+ if (isVarAnInterestingCondition(BExpr->getLHS(), N, &R) ||
+ isVarAnInterestingCondition(BExpr->getRHS(), N, &R))
+ Out << WillBeUsedForACondition;
// Convert 'field ...' to 'Field ...' if it is a MemberExpr.
std::string Message = Out.str();
Message[0] = toupper(Message[0]);
- // If we know the value create a pop-up note.
- if (!IsAssuming)
+ // If we know the value create a pop-up note to the value part of 'BExpr'.
+ if (!IsAssuming) {
+ PathDiagnosticLocation Loc;
+ if (!shouldInvert) {
+ if (LhsME && LhsME->getMemberLoc().isValid())
+ Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM);
+ else
+ Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, LCtx);
+ } else {
+ if (RhsME && RhsME->getMemberLoc().isValid())
+ Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM);
+ else
+ Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, LCtx);
+ }
+
return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message);
+ }
+ PathDiagnosticLocation Loc(Cond, SM, LCtx);
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message);
if (shouldPrune.hasValue())
event->setPrunable(shouldPrune.getValue());
return event;
}
-std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
+PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable(
StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC,
- BugReport &report, const ExplodedNode *N, bool TookTrue) {
+ PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) {
// FIXME: If there's already a constraint tracker for this variable,
// we shouldn't emit anything here (c.f. the double note in
// test/Analysis/inlining/path-notes.c)
@@ -2441,24 +2536,22 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
const LocationContext *LCtx = N->getLocationContext();
PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx);
+
+ if (isVarAnInterestingCondition(CondVarExpr, N, &report))
+ Out << WillBeUsedForACondition;
+
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
- 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))
- event->setPrunable(false);
- }
- }
- }
+ if (isInterestingExpr(CondVarExpr, N, &report))
+ event->setPrunable(false);
return event;
}
-std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC,
- BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
+ PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming) {
const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
if (!VD)
return nullptr;
@@ -2472,29 +2565,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
return nullptr;
const LocationContext *LCtx = N->getLocationContext();
- PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
- // If we know the value create a pop-up note.
- if (!IsAssuming)
+ if (isVarAnInterestingCondition(DRE, N, &report))
+ Out << WillBeUsedForACondition;
+
+ // If we know the value create a pop-up note to the 'DRE'.
+ if (!IsAssuming) {
+ PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), LCtx);
return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
+ }
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
- const ProgramState *state = N->getState().get();
- if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
- if (report.isInteresting(R))
- event->setPrunable(false);
- else {
- SVal V = state->getSVal(R);
- if (report.isInteresting(V))
- event->setPrunable(false);
- }
- }
+
+ if (isInterestingExpr(DRE, N, &report))
+ event->setPrunable(false);
+
return std::move(event);
}
-std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(
const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC,
- BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
+ PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming) {
SmallString<256> Buf;
llvm::raw_svector_ostream Out(Buf);
@@ -2505,15 +2598,28 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
return nullptr;
const LocationContext *LCtx = N->getLocationContext();
- PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ PathDiagnosticLocation Loc;
+
+ // If we know the value create a pop-up note to the member of the MemberExpr.
+ if (!IsAssuming && ME->getMemberLoc().isValid())
+ Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager());
+ else
+ Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(), LCtx);
+
if (!Loc.isValid() || !Loc.asLocation().isValid())
return nullptr;
+ if (isVarAnInterestingCondition(ME, N, &report))
+ Out << WillBeUsedForACondition;
+
// If we know the value create a pop-up note.
if (!IsAssuming)
return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
- return std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+ auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+ if (isInterestingExpr(ME, N, &report))
+ event->setPrunable(false);
+ return event;
}
bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
@@ -2553,10 +2659,8 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
return true;
}
-const char *const ConditionBRVisitor::GenericTrueMessage =
- "Assuming the condition is true";
-const char *const ConditionBRVisitor::GenericFalseMessage =
- "Assuming the condition is false";
+constexpr llvm::StringLiteral ConditionBRVisitor::GenericTrueMessage;
+constexpr llvm::StringLiteral ConditionBRVisitor::GenericFalseMessage;
bool ConditionBRVisitor::isPieceMessageGeneric(
const PathDiagnosticPiece *Piece) {
@@ -2569,10 +2673,11 @@ bool ConditionBRVisitor::isPieceMessageGeneric(
//===----------------------------------------------------------------------===//
void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
- BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) {
+ BugReporterContext &BRC, const ExplodedNode *N,
+ PathSensitiveBugReport &BR) {
// Here we suppress false positives coming from system headers. This list is
// based on known issues.
- AnalyzerOptions &Options = BRC.getAnalyzerOptions();
+ const AnalyzerOptions &Options = BRC.getAnalyzerOptions();
const Decl *D = N->getLocationContext()->getDecl();
if (AnalysisDeclContext::isInStdNamespace(D)) {
@@ -2639,8 +2744,8 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
// Skip reports within the sys/queue.h macros as we do not have the ability to
// reason about data structure shapes.
- SourceManager &SM = BRC.getSourceManager();
- FullSourceLoc Loc = BR.getLocation(SM).asLocation();
+ const SourceManager &SM = BRC.getSourceManager();
+ FullSourceLoc Loc = BR.getLocation().asLocation();
while (Loc.isMacroID()) {
Loc = Loc.getSpellingLoc();
if (SM.getFilename(Loc).endswith("sys/queue.h")) {
@@ -2654,9 +2759,9 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
// Implementation of UndefOrNullArgVisitor.
//===----------------------------------------------------------------------===//
-std::shared_ptr<PathDiagnosticPiece>
-UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &BRC, BugReport &BR) {
+PathDiagnosticPieceRef
+UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
ProgramStateRef State = N->getState();
ProgramPoint ProgLoc = N->getLocation();
@@ -2712,7 +2817,8 @@ FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
: Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
void FalsePositiveRefutationBRVisitor::finalizeVisitor(
- BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
+ BugReporterContext &BRC, const ExplodedNode *EndPathNode,
+ PathSensitiveBugReport &BR) {
// Collect new constraints
VisitNode(EndPathNode, BRC, BR);
@@ -2747,10 +2853,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
}
-std::shared_ptr<PathDiagnosticPiece>
-FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N,
- BugReporterContext &,
- BugReport &) {
+PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) {
// Collect new constraints
const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
ConstraintRangeTy::Factory &CF =
@@ -2784,9 +2888,9 @@ void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddPointer(&Tag);
}
-std::shared_ptr<PathDiagnosticPiece>
-TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
- BugReport &R) {
+PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &R) {
ProgramPoint PP = N->getLocation();
const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag());
if (!T)
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index a5f7500e63074..5f04a59ba0551 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -27,6 +27,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
#include "clang/Basic/IdentifierTable.h"
@@ -34,10 +35,9 @@
#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/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
@@ -191,7 +191,8 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
return ADC;
}
-const StackFrameContext *CallEvent::getCalleeStackFrame() const {
+const StackFrameContext *
+CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext();
if (!ADC)
return nullptr;
@@ -217,11 +218,12 @@ const StackFrameContext *CallEvent::getCalleeStackFrame() const {
break;
assert(Idx < Sz);
- return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx);
+ return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
}
-const VarRegion *CallEvent::getParameterLocation(unsigned Index) const {
- const StackFrameContext *SFC = getCalleeStackFrame();
+const VarRegion *CallEvent::getParameterLocation(unsigned Index,
+ unsigned BlockCount) const {
+ const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;
@@ -322,7 +324,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
if (getKind() != CE_CXXAllocator)
if (isArgumentConstructedDirectly(Idx))
if (auto AdjIdx = getAdjustedParameterIndex(Idx))
- if (const VarRegion *VR = getParameterLocation(*AdjIdx))
+ if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
}
@@ -366,7 +368,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
if (CD.Flags & CDF_MaybeBuiltin) {
return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
- (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs());
+ (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) &&
+ (!CD.RequiredParams || CD.RequiredParams <= parameters().size());
}
if (!CD.IsLookupDone) {
@@ -405,7 +408,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
return false;
}
- return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs());
+ return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) &&
+ (!CD.RequiredParams || CD.RequiredParams == parameters().size());
}
SVal CallEvent::getArgSVal(unsigned Index) const {
@@ -1033,7 +1037,7 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
ObjCMessageKind ObjCMethodCall::getMessageKind() const {
if (!Data) {
// Find the parent, ignoring implicit casts.
- ParentMap &PM = getLocationContext()->getParentMap();
+ const ParentMap &PM = getLocationContext()->getParentMap();
const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr());
// Check if parent is a PseudoObjectExpr.
diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp
index f4e6f909d7644..bc1c8964b3ee4 100644
--- a/lib/StaticAnalyzer/Core/Checker.cpp
+++ b/lib/StaticAnalyzer/Core/Checker.cpp
@@ -19,10 +19,10 @@ using namespace ento;
int ImplicitNullDerefEvent::Tag;
StringRef CheckerBase::getTagDescription() const {
- return getCheckName().getName();
+ return getCheckerName().getName();
}
-CheckName CheckerBase::getCheckName() const { return Name; }
+CheckerNameRef CheckerBase::getCheckerName() const { return Name; }
CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName,
StringRef Msg)
@@ -30,10 +30,10 @@ CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName,
CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker,
StringRef Msg)
- : SimpleProgramPointTag(Checker->getCheckName().getName(), Msg) {}
+ : SimpleProgramPointTag(Checker->getCheckerName().getName(), Msg) {}
raw_ostream& clang::ento::operator<<(raw_ostream &Out,
const CheckerBase &Checker) {
- Out << Checker.getCheckName().getName();
+ Out << Checker.getCheckerName().getName();
return Out;
}
diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index 34cdc9db699db..11693132de687 100644
--- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -91,7 +91,7 @@ parseAssignment(const Stmt *S) {
} else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) {
// Initialization
assert(PD->isSingleDecl() && "We process decls one by one");
- VD = dyn_cast_or_null<VarDecl>(PD->getSingleDecl());
+ VD = cast<VarDecl>(PD->getSingleDecl());
RHS = VD->getAnyInitializer();
}
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 27d5797b4cbc9..f676bd8952836 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -748,7 +748,7 @@ void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out,
continue;
Indent(Out, Space, IsDot)
- << "{ \"checker\": \"" << CT.second->getCheckName().getName()
+ << "{ \"checker\": \"" << CT.second->getCheckerName().getName()
<< "\", \"messages\": [" << NL;
Indent(Out, InnerSpace, IsDot)
<< '\"' << TempBuf.str().trim() << '\"' << NL;
diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
index 54501314386a9..bdae3e605efff 100644
--- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
+++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -17,4 +17,5 @@ const char * const MemoryRefCount =
"Memory (Core Foundation/Objective-C/OSObject)";
const char * const MemoryError = "Memory error";
const char * const UnixAPI = "Unix API";
+const char * const CXXObjectLifecycle = "C++ object lifecycle";
}}}
diff --git a/lib/StaticAnalyzer/Core/DynamicType.cpp b/lib/StaticAnalyzer/Core/DynamicType.cpp
new file mode 100644
index 0000000000000..a78e0e05e903b
--- /dev/null
+++ b/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -0,0 +1,229 @@
+//===- DynamicType.cpp - Dynamic type related APIs --------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs that track and query dynamic type information. This
+// information can be used to devirtualize calls during the symbolic execution
+// or do type checking.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
+#include "clang/Basic/JsonSupport.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>
+
+/// The GDM component containing the dynamic type info. This is a map from a
+/// symbol to its most likely type.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *,
+ clang::ento::DynamicTypeInfo)
+
+/// A set factory of dynamic cast informations.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
+
+/// A map from symbols to cast informations.
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
+ CastSet)
+
+namespace clang {
+namespace ento {
+
+DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
+ MR = MR->StripCasts();
+
+ // Look up the dynamic type in the GDM.
+ if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
+ return *DTI;
+
+ // Otherwise, fall back to what we know about the region.
+ if (const auto *TR = dyn_cast<TypedRegion>(MR))
+ return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false);
+
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
+ SymbolRef Sym = SR->getSymbol();
+ return DynamicTypeInfo(Sym->getType());
+ }
+
+ return {};
+}
+
+const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
+ const MemRegion *MR) {
+ return State->get<DynamicTypeMap>(MR);
+}
+
+const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastFromTy,
+ QualType CastToTy) {
+ const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
+ if (!Lookup)
+ return nullptr;
+
+ for (const DynamicCastInfo &Cast : *Lookup)
+ if (Cast.equals(CastFromTy, CastToTy))
+ return &Cast;
+
+ return nullptr;
+}
+
+ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
+ DynamicTypeInfo NewTy) {
+ State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
+ assert(State);
+ return State;
+}
+
+ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
+ QualType NewTy, bool CanBeSubClassed) {
+ return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed));
+}
+
+ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastFromTy,
+ QualType CastToTy,
+ bool CastSucceeds) {
+ if (!MR)
+ return State;
+
+ if (CastSucceeds) {
+ assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) &&
+ "DynamicTypeInfo should always be a pointer.");
+ State = State->set<DynamicTypeMap>(MR, CastToTy);
+ }
+
+ DynamicCastInfo::CastResult ResultKind =
+ CastSucceeds ? DynamicCastInfo::CastResult::Success
+ : DynamicCastInfo::CastResult::Failure;
+
+ CastSet::Factory &F = State->get_context<CastSet>();
+
+ const CastSet *TempSet = State->get<DynamicCastMap>(MR);
+ CastSet Set = TempSet ? *TempSet : F.getEmptySet();
+
+ Set = F.add(Set, {CastFromTy, CastToTy, ResultKind});
+ State = State->set<DynamicCastMap>(MR, Set);
+
+ assert(State);
+ return State;
+}
+
+template <typename MapTy>
+ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map,
+ SymbolReaper &SR) {
+ for (const auto &Elem : Map)
+ if (!SR.isLiveRegion(Elem.first))
+ State = State->remove<DynamicCastMap>(Elem.first);
+
+ return State;
+}
+
+ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) {
+ return removeDead(State, State->get<DynamicTypeMap>(), SR);
+}
+
+ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
+ return removeDead(State, State->get<DynamicCastMap>(), SR);
+}
+
+static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+
+ const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>();
+ if (Map.isEmpty()) {
+ Out << "null," << NL;
+ return;
+ }
+
+ ++Space;
+ Out << '[' << NL;
+ for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+ const MemRegion *MR = I->first;
+ const DynamicTypeInfo &DTI = I->second;
+ Indent(Out, Space, IsDot)
+ << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
+ if (!DTI.isValid()) {
+ Out << "null";
+ } else {
+ Out << '\"' << DTI.getType()->getPointeeType().getAsString()
+ << "\", \"sub_classable\": "
+ << (DTI.canBeASubClass() ? "true" : "false");
+ }
+ Out << " }";
+
+ if (std::next(I) != Map.end())
+ Out << ',';
+ Out << NL;
+ }
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL;
+}
+
+static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space,
+ bool IsDot) {
+ Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+
+ const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
+ if (Map.isEmpty()) {
+ Out << "null," << NL;
+ return;
+ }
+
+ ++Space;
+ Out << '[' << NL;
+ for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+ const MemRegion *MR = I->first;
+ const CastSet &Set = I->second;
+
+ Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": ";
+ if (Set.isEmpty()) {
+ Out << "null ";
+ } else {
+ ++Space;
+ Out << '[' << NL;
+ for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
+ Indent(Out, Space, IsDot)
+ << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
+ << SI->to().getAsString() << "\", \"kind\": \""
+ << (SI->succeeds() ? "success" : "fail") << "\" }";
+
+ if (std::next(SI) != Set.end())
+ Out << ',';
+ Out << NL;
+ }
+ --Space;
+ Indent(Out, Space, IsDot) << ']';
+ }
+ Out << '}';
+
+ if (std::next(I) != Map.end())
+ Out << ',';
+ Out << NL;
+ }
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL;
+}
+
+void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space, bool IsDot) {
+ printDynamicTypesJson(Out, State, NL, Space, IsDot);
+ printDynamicCastsJson(Out, State, NL, Space, IsDot);
+}
+
+} // namespace ento
+} // namespace clang
diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
deleted file mode 100644
index 79424452240d7..0000000000000
--- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines APIs that track and query dynamic type information. This
-// information can be used to devirtualize calls during the symbolic execution
-// or do type checking.
-//
-//===----------------------------------------------------------------------===//
-
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
-#include "clang/Basic/JsonSupport.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 {
-
-DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State,
- const MemRegion *Reg) {
- Reg = Reg->StripCasts();
-
- // Look up the dynamic type in the GDM.
- const DynamicTypeInfo *GDMType = State->get<DynamicTypeMap>(Reg);
- if (GDMType)
- return *GDMType;
-
- // Otherwise, fall back to what we know about the region.
- if (const auto *TR = dyn_cast<TypedRegion>(Reg))
- return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false);
-
- if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) {
- SymbolRef Sym = SR->getSymbol();
- return DynamicTypeInfo(Sym->getType());
- }
-
- return {};
-}
-
-ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
- DynamicTypeInfo NewTy) {
- Reg = Reg->StripCasts();
- ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy);
- assert(NewState);
- return NewState;
-}
-
-void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
- const char *NL, unsigned int Space, bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
-
- const DynamicTypeMapTy &DTM = State->get<DynamicTypeMap>();
- if (DTM.isEmpty()) {
- Out << "null," << NL;
- return;
- }
-
- ++Space;
- Out << '[' << NL;
- for (DynamicTypeMapTy::iterator I = DTM.begin(); I != DTM.end(); ++I) {
- const MemRegion *MR = I->first;
- const DynamicTypeInfo &DTI = I->second;
- Out << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
- if (DTI.isValid()) {
- Out << '\"' << DTI.getType()->getPointeeType().getAsString()
- << "\", \"sub_classable\": "
- << (DTI.canBeASubClass() ? "true" : "false");
- } else {
- Out << "null"; // Invalid type info
- }
- Out << "}";
-
- if (std::next(I) != DTM.end())
- Out << ',';
- Out << NL;
- }
-
- --Space;
- Indent(Out, Space, IsDot) << "]," << NL;
-}
-
-void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() {
- static int index = 0;
- return &index;
-}
-
-} // namespace ento
-} // namespace clang
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index 551c89b04db4b..1ccf4c6104a65 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -108,6 +108,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::ObjCStringLiteralClass:
case Stmt::StringLiteralClass:
case Stmt::TypeTraitExprClass:
+ case Stmt::SizeOfPackExprClass:
// Known constants; defer to SValBuilder.
return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index c86b1436baabb..c4838492271cd 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/Stmt.h"
+#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Basic/LLVM.h"
@@ -134,7 +135,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
// Do not collect nodes for non-consumed Stmt or Expr to ensure precise
// diagnostic generation; specifically, so that we could anchor arrows
// pointing to the beginning of statements (as written in code).
- ParentMap &PM = progPoint.getLocationContext()->getParentMap();
+ const ParentMap &PM = progPoint.getLocationContext()->getParentMap();
if (!PM.isConsumedExpr(Ex))
return false;
@@ -282,16 +283,115 @@ ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
return Storage.getAddrOfPtr1() + 1;
}
-int64_t ExplodedNode::getID(ExplodedGraph *G) const {
- return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this);
-}
-
bool ExplodedNode::isTrivial() const {
return pred_size() == 1 && succ_size() == 1 &&
getFirstPred()->getState()->getID() == getState()->getID() &&
getFirstPred()->succ_size() == 1;
}
+const CFGBlock *ExplodedNode::getCFGBlock() const {
+ ProgramPoint P = getLocation();
+ if (auto BEP = P.getAs<BlockEntrance>())
+ return BEP->getBlock();
+
+ // Find the node's current statement in the CFG.
+ // FIXME: getStmtForDiagnostics() does nasty things in order to provide
+ // a valid statement for body farms, do we need this behavior here?
+ if (const Stmt *S = getStmtForDiagnostics())
+ return getLocationContext()
+ ->getAnalysisDeclContext()
+ ->getCFGStmtMap()
+ ->getBlock(S);
+
+ return nullptr;
+}
+
+static const LocationContext *
+findTopAutosynthesizedParentContext(const LocationContext *LC) {
+ assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
+ const LocationContext *ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ LC = ParentLC;
+ ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ }
+ return LC;
+}
+
+const Stmt *ExplodedNode::getStmtForDiagnostics() const {
+ // We cannot place diagnostics on autosynthesized code.
+ // Put them onto the call site through which we jumped into autosynthesized
+ // code for the first time.
+ const LocationContext *LC = getLocationContext();
+ if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ // It must be a stack frame because we only autosynthesize functions.
+ return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
+ ->getCallSite();
+ }
+ // Otherwise, see if the node's program point directly points to a statement.
+ // FIXME: Refactor into a ProgramPoint method?
+ ProgramPoint P = getLocation();
+ if (auto SP = P.getAs<StmtPoint>())
+ return SP->getStmt();
+ if (auto BE = P.getAs<BlockEdge>())
+ return BE->getSrc()->getTerminatorStmt();
+ if (auto CE = P.getAs<CallEnter>())
+ return CE->getCallExpr();
+ if (auto CEE = P.getAs<CallExitEnd>())
+ return CEE->getCalleeContext()->getCallSite();
+ if (auto PIPP = P.getAs<PostInitializer>())
+ return PIPP->getInitializer()->getInit();
+ if (auto CEB = P.getAs<CallExitBegin>())
+ return CEB->getReturnStmt();
+ if (auto FEP = P.getAs<FunctionExitPoint>())
+ return FEP->getStmt();
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
+ for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
+ if (const Stmt *S = N->getStmtForDiagnostics()) {
+ // Check if the statement is '?' or '&&'/'||'. These are "merges",
+ // not actual statement points.
+ switch (S->getStmtClass()) {
+ case Stmt::ChooseExprClass:
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ continue;
+ case Stmt::BinaryOperatorClass: {
+ BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
+ if (Op == BO_LAnd || Op == BO_LOr)
+ continue;
+ break;
+ }
+ default:
+ break;
+ }
+ // We found the statement, so return it.
+ return S;
+ }
+ }
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
+ for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
+ if (const Stmt *S = N->getStmtForDiagnostics())
+ return S;
+
+ return nullptr;
+}
+
+const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
+ if (const Stmt *S = getStmtForDiagnostics())
+ return S;
+
+ return getPreviousStmtForDiagnostics();
+}
+
ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
ProgramStateRef State,
bool IsSink,
@@ -313,14 +413,14 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
V = (NodeTy*) getAllocator().Allocate<NodeTy>();
}
- new (V) NodeTy(L, State, IsSink);
+ ++NumNodes;
+ new (V) NodeTy(L, State, NumNodes, IsSink);
if (ReclaimNodeInterval)
ChangedNodes.push_back(V);
// Insert the node into the node set and return it.
Nodes.InsertNode(V, InsertPos);
- ++NumNodes;
if (IsNew) *IsNew = true;
}
@@ -332,9 +432,10 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
ProgramStateRef State,
+ int64_t Id,
bool IsSink) {
NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>();
- new (V) NodeTy(L, State, IsSink);
+ new (V) NodeTy(L, State, Id, IsSink);
return V;
}
@@ -394,7 +495,8 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
// Create the corresponding node in the new graph and record the mapping
// from the old node to the new node.
- ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink());
+ ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
+ N->getID(), N->isSink());
Pass2[N] = NewN;
// Also record the reverse mapping from the new node to the old node.
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 1fef5b3c1edd5..e92e95354f5f2 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -977,8 +977,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
CallOpts.IsArrayCtorOrDtor).getAsRegion();
- VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false,
- Pred, Dst, CallOpts);
+ VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(),
+ /*IsBase=*/false, Pred, Dst, CallOpts);
}
void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
@@ -1036,8 +1036,9 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy,
Base->isVirtual());
- VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(),
- CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {});
+ EvalCallOptions CallOpts;
+ VisitCXXDestructor(BaseTy, BaseVal.getAsRegion(), CurDtor->getBody(),
+ /*IsBase=*/true, Pred, Dst, CallOpts);
}
void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
@@ -1048,10 +1049,10 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
const LocationContext *LCtx = Pred->getLocationContext();
const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
- Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
- LCtx->getStackFrame());
- SVal FieldVal =
- State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>());
+ Loc ThisStorageLoc =
+ getSValBuilder().getCXXThis(CurDtor, LCtx->getStackFrame());
+ Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>();
+ SVal FieldVal = State->getLValue(Member, ThisLoc);
// FIXME: We need to run the same destructor on every element of the array.
// This workaround will just run the first destructor (which will still
@@ -1060,8 +1061,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
FieldVal = makeZeroElementRegion(State, FieldVal, T,
CallOpts.IsArrayCtorOrDtor);
- VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(),
- CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts);
+ VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(),
+ /*IsBase=*/false, Pred, Dst, CallOpts);
}
void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
@@ -1109,8 +1110,6 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
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.
@@ -1266,6 +1265,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPCancelDirectiveClass:
case Stmt::OMPTaskLoopDirectiveClass:
case Stmt::OMPTaskLoopSimdDirectiveClass:
+ case Stmt::OMPMasterTaskLoopDirectiveClass:
+ case Stmt::OMPMasterTaskLoopSimdDirectiveClass:
+ case Stmt::OMPParallelMasterTaskLoopDirectiveClass:
case Stmt::OMPDistributeDirectiveClass:
case Stmt::OMPDistributeParallelForDirectiveClass:
case Stmt::OMPDistributeParallelForSimdDirectiveClass:
@@ -1369,6 +1371,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CUDAKernelCallExprClass:
case Stmt::OpaqueValueExprClass:
case Stmt::AsTypeExprClass:
+ case Stmt::ConceptSpecializationExprClass:
+ case Stmt::CXXRewrittenBinaryOperatorClass:
// Fall through.
// Cases we intentionally don't evaluate, since they don't need
@@ -3005,9 +3009,13 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end());
for (const auto &EQ : EQClasses) {
- for (const BugReport &Report : EQ) {
- if (Report.getErrorNode()->getState() == N->getState() &&
- Report.getErrorNode()->getLocation() == N->getLocation())
+ for (const auto &I : EQ.getReports()) {
+ const auto *PR = dyn_cast<PathSensitiveBugReport>(I.get());
+ if (!PR)
+ continue;
+ const ExplodedNode *EN = PR->getErrorNode();
+ if (EN->getState() == N->getState() &&
+ EN->getLocation() == N->getLocation())
return true;
}
}
@@ -3023,22 +3031,16 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
llvm::function_ref<void(const ExplodedNode *)> PreCallback,
llvm::function_ref<void(const ExplodedNode *)> PostCallback,
llvm::function_ref<bool(const ExplodedNode *)> Stop) {
- const ExplodedNode *FirstHiddenNode = N;
- while (FirstHiddenNode->pred_size() == 1 &&
- isNodeHidden(*FirstHiddenNode->pred_begin())) {
- FirstHiddenNode = *FirstHiddenNode->pred_begin();
- }
- const ExplodedNode *OtherNode = FirstHiddenNode;
while (true) {
- PreCallback(OtherNode);
- if (Stop(OtherNode))
+ PreCallback(N);
+ if (Stop(N))
return true;
- if (OtherNode == N)
+ if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc()))
break;
- PostCallback(OtherNode);
+ PostCallback(N);
- OtherNode = *OtherNode->succ_begin();
+ N = N->getFirstSucc();
}
return false;
}
@@ -3055,16 +3057,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
const unsigned int Space = 1;
ProgramStateRef State = N->getState();
- auto Noop = [](const ExplodedNode*){};
- bool HasReport = traverseHiddenNodes(
- N, Noop, Noop, &nodeHasBugReport);
- bool IsSink = traverseHiddenNodes(
- N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); });
-
- Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \""
- << (const void *)N << "\", \"state_id\": " << State->getID()
- << ", \"has_report\": " << (HasReport ? "true" : "false")
- << ", \"is_sink\": " << (IsSink ? "true" : "false")
+ Out << "{ \"state_id\": " << State->getID()
<< ",\\l";
Indent(Out, Space, IsDot) << "\"program_points\": [\\l";
@@ -3077,9 +3070,12 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
Out << ", \"tag\": ";
if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
- Out << '\"' << Tag->getTagDescription() << "\" }";
+ Out << '\"' << Tag->getTagDescription() << "\"";
else
- Out << "null }";
+ Out << "null";
+ Out << ", \"node_id\": " << OtherNode->getID() <<
+ ", \"is_sink\": " << OtherNode->isSink() <<
+ ", \"has_report\": " << nodeHasBugReport(OtherNode) << " }";
},
// Adds a comma and a new-line between each program point.
[&](const ExplodedNode *) { Out << ",\\l"; },
@@ -3088,16 +3084,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
Out << "\\l"; // Adds a new-line to the last program point.
Indent(Out, Space, IsDot) << "],\\l";
- bool SameAsAllPredecessors =
- std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) {
- return P->getState() == State;
- });
-
- if (!SameAsAllPredecessors) {
- State->printDOT(Out, N->getLocationContext(), Space);
- } else {
- Indent(Out, Space, IsDot) << "\"program_state\": null";
- }
+ State->printDOT(Out, N->getLocationContext(), Space);
Out << "\\l}\\l";
return Out.str();
@@ -3132,8 +3119,12 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
// Iterate through the reports and get their nodes.
for (BugReporter::EQClasses_iterator
EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) {
- const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode());
- if (N) Src.push_back(N);
+ const auto *R =
+ dyn_cast<PathSensitiveBugReport>(EI->getReports()[0].get());
+ if (!R)
+ continue;
+ const auto *N = const_cast<ExplodedNode *>(R->getErrorNode());
+ Src.push_back(N);
}
return DumpGraph(Src, Filename);
} else {
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index f436650fbdd9b..02a398c77ac88 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -850,8 +850,7 @@ VisitOffsetOfExpr(const OffsetOfExpr *OOE,
if (OOE->EvaluateAsInt(Result, getContext())) {
APSInt IV = Result.Val.getInt();
assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
- assert(OOE->getType()->isBuiltinType());
- assert(OOE->getType()->getAs<BuiltinType>()->isInteger());
+ assert(OOE->getType()->castAs<BuiltinType>()->isInteger());
assert(IV.isSigned() == OOE->getType()->isSignedIntegerType());
SVal X = svalBuilder.makeIntVal(IV);
B.generateNode(OOE, Pred,
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 1cbd09ea57932..058be985540d3 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -323,7 +323,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
CallEventManager &CEMgr = getStateManager().getCallEventManager();
SVal V = UnknownVal();
auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
- const LocationContext *FutureSFC = Caller->getCalleeStackFrame();
+ const LocationContext *FutureSFC =
+ Caller->getCalleeStackFrame(currBldrCtx->blockCount());
// Return early if we are unable to reliably foresee
// the future stack frame.
if (!FutureSFC)
@@ -342,7 +343,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// because this-argument is implemented as a normal argument in
// operator call expressions but not in operator declarations.
const VarRegion *VR = Caller->getParameterLocation(
- *Caller->getAdjustedParameterIndex(Idx));
+ *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
if (!VR)
return None;
@@ -603,7 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
bool IsBaseDtor,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
- const EvalCallOptions &CallOpts) {
+ EvalCallOptions &CallOpts) {
assert(S && "A destructor without a trigger!");
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
@@ -611,7 +612,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
-
// FIXME: There should always be a Decl, otherwise the destructor call
// shouldn't have been added to the CFG in the first place.
if (!DtorDecl) {
@@ -625,9 +625,27 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
return;
}
+ if (!Dest) {
+ // We're trying to destroy something that is not a region. This may happen
+ // for a variety of reasons (unknown target region, concrete integer instead
+ // of target region, etc.). The current code makes an attempt to recover.
+ // FIXME: We probably don't really need to recover when we're dealing
+ // with concrete integers specifically.
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ if (const Expr *E = dyn_cast_or_null<Expr>(S)) {
+ Dest = MRMgr.getCXXTempObjectRegion(E, Pred->getLocationContext());
+ } else {
+ static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor");
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateSink(Pred->getLocation().withTag(&T),
+ Pred->getState(), Pred);
+ return;
+ }
+ }
+
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXDestructorCall> Call =
- CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
Call->getSourceRange().getBegin(),
@@ -757,7 +775,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
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.
+ // we should be using the usual pre-/(default-)eval-/post-call checkers
+ // here.
State = Call->invalidateRegions(blockCount);
if (!State)
return;
@@ -785,9 +804,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
if (CNE->isArray()) {
// FIXME: allocating an array requires simulating the constructors.
// For now, just return a symbolicated region.
- if (const SubRegion *NewReg =
- dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) {
- QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ if (const auto *NewReg = cast_or_null<SubRegion>(symVal.getAsRegion())) {
+ QualType ObjTy = CNE->getType()->getPointeeType();
const ElementRegion *EleReg =
getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
Result = loc::MemRegionVal(EleReg);
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index b935e3afe34b2..345d4d817deae 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -451,9 +451,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
// Construct a new stack frame for the callee.
AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
const StackFrameContext *CalleeSFC =
- CalleeADC->getStackFrame(ParentOfCallee, CallE,
- currBldrCtx->getBlock(),
- currStmtIdx);
+ CalleeADC->getStackFrame(ParentOfCallee, CallE, currBldrCtx->getBlock(),
+ currBldrCtx->blockCount(), currStmtIdx);
CallEnter Loc(CallE, CalleeSFC, CurLC);
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 64c42699fcf3c..a4918d7179ff5 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Stmt.h"
@@ -23,7 +24,6 @@
#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/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/ArrayRef.h"
@@ -134,17 +134,17 @@ private:
} // namespace
-void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string& prefix,
- const Preprocessor &PP) {
+void ento::createHTMLDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &prefix, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &) {
C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true));
}
-void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string& prefix,
- const Preprocessor &PP) {
+void ento::createHTMLSingleFileDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &prefix, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &) {
C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false));
}
@@ -555,8 +555,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
- << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue,
- PP.getLangOpts()) << " -->\n";
+ << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(),
+ DeclWithIssue, PP.getLangOpts())
+ << " -->\n";
os << "\n<!-- BUGLINE "
<< LineNumber
@@ -612,7 +613,7 @@ HandlePopUpPieceStartTag(Rewriter &R,
for (const auto &Range : PopUpRanges) {
html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "",
"<table class='variable_popup'><tbody>",
- /*IsTokenRange=*/false);
+ /*IsTokenRange=*/true);
}
}
@@ -644,12 +645,11 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
Out << "</tbody></table></span>";
html::HighlightRange(R, Range.getBegin(), Range.getEnd(),
"<span class='variable'>", Buf.c_str(),
- /*IsTokenRange=*/false);
-
- // Otherwise inject just the new row at the end of the range.
+ /*IsTokenRange=*/true);
} else {
+ // Otherwise inject just the new row at the end of the range.
html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(),
- /*IsTokenRange=*/false);
+ /*IsTokenRange=*/true);
}
}
@@ -658,16 +658,14 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
// Process the path.
// Maintain the counts of extra note pieces separately.
unsigned TotalPieces = path.size();
- unsigned TotalNotePieces =
- std::count_if(path.begin(), path.end(),
- [](const std::shared_ptr<PathDiagnosticPiece> &p) {
- return isa<PathDiagnosticNotePiece>(*p);
- });
- unsigned PopUpPieceCount =
- std::count_if(path.begin(), path.end(),
- [](const std::shared_ptr<PathDiagnosticPiece> &p) {
- return isa<PathDiagnosticPopUpPiece>(*p);
- });
+ unsigned TotalNotePieces = std::count_if(
+ path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ return isa<PathDiagnosticNotePiece>(*p);
+ });
+ unsigned PopUpPieceCount = std::count_if(
+ path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ return isa<PathDiagnosticPopUpPiece>(*p);
+ });
unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
unsigned NumRegularPieces = TotalRegularPieces;
diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index 9838249ae82ca..1a09a521f1167 100644
--- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -165,7 +165,9 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
return true;
while (!N->pred_empty()) {
- const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ // FIXME: getStmtForDiagnostics() does nasty things in order to provide
+ // a valid statement for body farms, do we need this behavior here?
+ const Stmt *S = N->getStmtForDiagnostics();
if (!S) {
N = N->getFirstPred();
continue;
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index f763701af7fb1..a10d7e69ad7e7 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -506,7 +506,7 @@ void ElementRegion::dumpToStream(raw_ostream &os) const {
}
void FieldRegion::dumpToStream(raw_ostream &os) const {
- os << superRegion << "->" << *getDecl();
+ os << superRegion << "." << *getDecl();
}
void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
@@ -1075,7 +1075,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
const SubRegion *Super,
bool IsVirtual) {
if (isa<TypedValueRegion>(Super)) {
- assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual));
+ assert(isValidBaseClass(RD, cast<TypedValueRegion>(Super), IsVirtual));
(void)&isValidBaseClass;
if (IsVirtual) {
@@ -1426,6 +1426,7 @@ static RegionOffset calculateOffset(const MemRegion *R) {
case MemRegion::FieldRegionKind: {
const auto *FR = cast<FieldRegion>(R);
R = FR->getSuperRegion();
+ assert(R);
const RecordDecl *RD = FR->getDecl()->getParent();
if (RD->isUnion() || !RD->isCompleteDefinition()) {
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
deleted file mode 100644
index 54fbd6a5bc496..0000000000000
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ /dev/null
@@ -1,1466 +0,0 @@
-//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the PathDiagnostic-related interfaces.
-//
-//===----------------------------------------------------------------------===//
-
-#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/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 (const auto &P : subPieces) {
- if (isa<PathDiagnosticEventPiece>(*P))
- return true;
- if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get()))
- if (MP->containsEvent())
- return true;
- }
- return false;
-}
-
-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 {};
-}
-
-PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
- Kind k, DisplayHint hint)
- : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
-
-PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
- : kind(k), Hint(hint) {}
-
-PathDiagnosticPiece::~PathDiagnosticPiece() = default;
-
-PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default;
-
-PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default;
-
-PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default;
-
-PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;
-
-PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;
-
-PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default;
-
-void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
- bool ShouldFlattenMacros) const {
- for (auto &Piece : *this) {
- switch (Piece->getKind()) {
- case PathDiagnosticPiece::Call: {
- auto &Call = cast<PathDiagnosticCallPiece>(*Piece);
- if (auto CallEnter = Call.getCallEnterEvent())
- Current.push_back(std::move(CallEnter));
- Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros);
- if (auto callExit = Call.getCallExitEvent())
- Current.push_back(std::move(callExit));
- break;
- }
- case PathDiagnosticPiece::Macro: {
- auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece);
- if (ShouldFlattenMacros) {
- Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);
- } else {
- Current.push_back(Piece);
- PathPieces NewPath;
- Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);
- // FIXME: This probably shouldn't mutate the original path piece.
- Macro.subPieces = NewPath;
- }
- break;
- }
- case PathDiagnosticPiece::Event:
- case PathDiagnosticPiece::ControlFlow:
- case PathDiagnosticPiece::Note:
- case PathDiagnosticPiece::PopUp:
- Current.push_back(Piece);
- break;
- }
- }
-}
-
-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,
- const SourceManager &SMgr) {
- SourceLocation CallLoc = CP->callEnter.asLocation();
-
- // If the call is within a macro, don't do anything (for now).
- if (CallLoc.isMacroID())
- return nullptr;
-
- 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 (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr))
- return CP;
-
- const PathPieces &Path = CP->path;
- if (Path.empty())
- return nullptr;
-
- // Check if the last piece in the callee path is a call to a function outside
- // of the main file.
- if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
- return getFirstStackedCallToHeaderFile(CPInner, SMgr);
-
- // Otherwise, the last piece is in the main file.
- return nullptr;
-}
-
-void PathDiagnostic::resetDiagnosticLocationToMainFile() {
- if (path.empty())
- return;
-
- PathDiagnosticPiece *LastP = path.back().get();
- assert(LastP);
- const SourceManager &SMgr = LastP->getLocation().getManager();
-
- // We only need to check if the report ends inside headers, if the last piece
- // is a call piece.
- if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
- CP = getFirstStackedCallToHeaderFile(CP, SMgr);
- if (CP) {
- // Mark the piece.
- CP->setAsLastInMainSourceFile();
-
- // Update the path diagnostic message.
- const auto *ND = dyn_cast<NamedDecl>(CP->getCallee());
- if (ND) {
- SmallString<200> buf;
- llvm::raw_svector_ostream os(buf);
- os << " (within a call to '" << ND->getDeclName() << "')";
- appendToDesc(os.str());
- }
-
- // Reset the report containing declaration and location.
- DeclWithIssue = CP->getCaller();
- Loc = CP->getLocation();
-
- return;
- }
- }
-}
-
-void PathDiagnosticConsumer::anchor() {}
-
-PathDiagnosticConsumer::~PathDiagnosticConsumer() {
- // Delete the contents of the FoldingSet if it isn't empty already.
- for (auto &Diag : Diags)
- delete &Diag;
-}
-
-void PathDiagnosticConsumer::HandlePathDiagnostic(
- std::unique_ptr<PathDiagnostic> D) {
- if (!D || D->path.empty())
- return;
-
- // We need to flatten the locations (convert Stmt* to locations) because
- // the referenced statements may be freed by the time the diagnostics
- // are emitted.
- D->flattenLocations();
-
- // If the PathDiagnosticConsumer does not support diagnostics that
- // cross file boundaries, prune out such diagnostics now.
- if (!supportsCrossFileDiagnostics()) {
- // Verify that the entire path is from the same FileID.
- FileID FID;
- const SourceManager &SMgr = D->path.front()->getLocation().getManager();
- SmallVector<const PathPieces *, 5> WorkList;
- WorkList.push_back(&D->path);
- SmallString<128> buf;
- llvm::raw_svector_ostream warning(buf);
- warning << "warning: Path diagnostic report is not generated. Current "
- << "output format does not support diagnostics that cross file "
- << "boundaries. Refer to --analyzer-output for valid output "
- << "formats\n";
-
- while (!WorkList.empty()) {
- const PathPieces &path = *WorkList.pop_back_val();
-
- for (const auto &I : path) {
- const PathDiagnosticPiece *piece = I.get();
- FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
-
- if (FID.isInvalid()) {
- FID = SMgr.getFileID(L);
- } else if (SMgr.getFileID(L) != FID) {
- llvm::errs() << warning.str();
- return;
- }
-
- // Check the source ranges.
- ArrayRef<SourceRange> Ranges = piece->getRanges();
- 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());
- if (!L.isFileID() || SMgr.getFileID(L) != FID) {
- llvm::errs() << warning.str();
- return;
- }
- }
-
- if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece))
- WorkList.push_back(&call->path);
- else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece))
- WorkList.push_back(&macro->subPieces);
- }
- }
-
- if (FID.isInvalid())
- return; // FIXME: Emit a warning?
- }
-
- // Profile the node to see if we already have something matching it
- llvm::FoldingSetNodeID profile;
- D->Profile(profile);
- void *InsertPos = nullptr;
-
- if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
- // Keep the PathDiagnostic with the shorter path.
- // Note, the enclosing routine is called in deterministic order, so the
- // results will be consistent between runs (no reason to break ties if the
- // size is the same).
- const unsigned orig_size = orig->full_size();
- const unsigned new_size = D->full_size();
- if (orig_size <= new_size)
- return;
-
- assert(orig != D.get());
- Diags.RemoveNode(orig);
- delete orig;
- }
-
- Diags.InsertNode(D.release());
-}
-
-static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y);
-
-static Optional<bool>
-compareControlFlow(const PathDiagnosticControlFlowPiece &X,
- const PathDiagnosticControlFlowPiece &Y) {
- FullSourceLoc XSL = X.getStartLocation().asLocation();
- FullSourceLoc YSL = Y.getStartLocation().asLocation();
- if (XSL != YSL)
- return XSL.isBeforeInTranslationUnitThan(YSL);
- FullSourceLoc XEL = X.getEndLocation().asLocation();
- FullSourceLoc YEL = Y.getEndLocation().asLocation();
- if (XEL != YEL)
- return XEL.isBeforeInTranslationUnitThan(YEL);
- return None;
-}
-
-static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X,
- const PathDiagnosticMacroPiece &Y) {
- return comparePath(X.subPieces, Y.subPieces);
-}
-
-static Optional<bool> compareCall(const PathDiagnosticCallPiece &X,
- const PathDiagnosticCallPiece &Y) {
- FullSourceLoc X_CEL = X.callEnter.asLocation();
- FullSourceLoc Y_CEL = Y.callEnter.asLocation();
- if (X_CEL != Y_CEL)
- return X_CEL.isBeforeInTranslationUnitThan(Y_CEL);
- FullSourceLoc X_CEWL = X.callEnterWithin.asLocation();
- FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation();
- if (X_CEWL != Y_CEWL)
- return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL);
- FullSourceLoc X_CRL = X.callReturn.asLocation();
- FullSourceLoc Y_CRL = Y.callReturn.asLocation();
- if (X_CRL != Y_CRL)
- return X_CRL.isBeforeInTranslationUnitThan(Y_CRL);
- return comparePath(X.path, Y.path);
-}
-
-static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
- const PathDiagnosticPiece &Y) {
- if (X.getKind() != Y.getKind())
- return X.getKind() < Y.getKind();
-
- FullSourceLoc XL = X.getLocation().asLocation();
- FullSourceLoc YL = Y.getLocation().asLocation();
- if (XL != YL)
- return XL.isBeforeInTranslationUnitThan(YL);
-
- if (X.getString() != Y.getString())
- return X.getString() < Y.getString();
-
- if (X.getRanges().size() != Y.getRanges().size())
- return X.getRanges().size() < Y.getRanges().size();
-
- const SourceManager &SM = XL.getManager();
-
- for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {
- SourceRange XR = X.getRanges()[i];
- SourceRange YR = Y.getRanges()[i];
- if (XR != YR) {
- if (XR.getBegin() != YR.getBegin())
- return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin());
- return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());
- }
- }
-
- switch (X.getKind()) {
- case PathDiagnosticPiece::ControlFlow:
- return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),
- cast<PathDiagnosticControlFlowPiece>(Y));
- case PathDiagnosticPiece::Macro:
- return compareMacro(cast<PathDiagnosticMacroPiece>(X),
- cast<PathDiagnosticMacroPiece>(Y));
- case PathDiagnosticPiece::Call:
- return compareCall(cast<PathDiagnosticCallPiece>(X),
- cast<PathDiagnosticCallPiece>(Y));
- case PathDiagnosticPiece::Event:
- case PathDiagnosticPiece::Note:
- case PathDiagnosticPiece::PopUp:
- return None;
- }
- llvm_unreachable("all cases handled");
-}
-
-static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) {
- if (X.size() != Y.size())
- return X.size() < Y.size();
-
- PathPieces::const_iterator X_I = X.begin(), X_end = X.end();
- PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end();
-
- for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) {
- Optional<bool> b = comparePiece(**X_I, **Y_I);
- if (b.hasValue())
- return b.getValue();
- }
-
- 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 compareCrossTUSourceLocs(XL, YL);
- if (X.getBugType() != Y.getBugType())
- return X.getBugType() < Y.getBugType();
- if (X.getCategory() != Y.getCategory())
- return X.getCategory() < Y.getCategory();
- if (X.getVerboseDescription() != Y.getVerboseDescription())
- return X.getVerboseDescription() < Y.getVerboseDescription();
- if (X.getShortDescription() != Y.getShortDescription())
- return X.getShortDescription() < Y.getShortDescription();
- if (X.getDeclWithIssue() != Y.getDeclWithIssue()) {
- const Decl *XD = X.getDeclWithIssue();
- if (!XD)
- return true;
- const Decl *YD = Y.getDeclWithIssue();
- if (!YD)
- return false;
- SourceLocation XDL = XD->getLocation();
- SourceLocation YDL = YD->getLocation();
- if (XDL != YDL) {
- const SourceManager &SM = XL.getManager();
- return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM),
- FullSourceLoc(YDL, SM));
- }
- }
- PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();
- PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end();
- if (XE - XI != YE - YI)
- return (XE - XI) < (YE - YI);
- for ( ; XI != XE ; ++XI, ++YI) {
- if (*XI != *YI)
- return (*XI) < (*YI);
- }
- Optional<bool> b = comparePath(X.path, Y.path);
- assert(b.hasValue());
- return b.getValue();
-}
-
-void PathDiagnosticConsumer::FlushDiagnostics(
- PathDiagnosticConsumer::FilesMade *Files) {
- if (flushed)
- return;
-
- flushed = true;
-
- std::vector<const PathDiagnostic *> BatchDiags;
- for (const auto &D : Diags)
- BatchDiags.push_back(&D);
-
- // Sort the diagnostics so that they are always emitted in a deterministic
- // order.
- int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) =
- [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) {
- assert(*X != *Y && "PathDiagnostics not uniqued!");
- if (compare(**X, **Y))
- return -1;
- assert(compare(**Y, **X) && "Not a total order!");
- return 1;
- };
- array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp);
-
- FlushDiagnosticsImpl(BatchDiags, Files);
-
- // Delete the flushed diagnostics.
- for (const auto D : BatchDiags)
- delete D;
-
- // Clear out the FoldingSet.
- Diags.clear();
-}
-
-PathDiagnosticConsumer::FilesMade::~FilesMade() {
- for (PDFileEntry &Entry : Set)
- Entry.~PDFileEntry();
-}
-
-void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,
- StringRef ConsumerName,
- StringRef FileName) {
- llvm::FoldingSetNodeID NodeID;
- NodeID.Add(PD);
- void *InsertPos;
- PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
- if (!Entry) {
- Entry = Alloc.Allocate<PDFileEntry>();
- Entry = new (Entry) PDFileEntry(NodeID);
- Set.InsertNode(Entry, InsertPos);
- }
-
- // Allocate persistent storage for the file name.
- char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);
- memcpy(FileName_cstr, FileName.data(), FileName.size());
-
- Entry->files.push_back(std::make_pair(ConsumerName,
- StringRef(FileName_cstr,
- FileName.size())));
-}
-
-PathDiagnosticConsumer::PDFileEntry::ConsumerFiles *
-PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) {
- llvm::FoldingSetNodeID NodeID;
- NodeID.Add(PD);
- void *InsertPos;
- PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
- if (!Entry)
- return nullptr;
- return &Entry->files;
-}
-
-//===----------------------------------------------------------------------===//
-// PathDiagnosticLocation methods.
-//===----------------------------------------------------------------------===//
-
-static SourceLocation getValidSourceLocation(const Stmt* S,
- LocationOrAnalysisDeclContext LAC,
- bool UseEnd = false) {
- SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc();
- assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
- "be passed to PathDiagnosticLocation upon creation.");
-
- // S might be a temporary statement that does not have a location in the
- // source code, so find an enclosing statement and use its location.
- if (!L.isValid()) {
- AnalysisDeclContext *ADC;
- if (LAC.is<const LocationContext*>())
- ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext();
- else
- ADC = LAC.get<AnalysisDeclContext*>();
-
- ParentMap &PM = ADC->getParentMap();
-
- const Stmt *Parent = S;
- do {
- Parent = PM.getParent(Parent);
-
- // In rare cases, we have implicit top-level expressions,
- // such as arguments for implicit member initializers.
- // In this case, fall back to the start of the body (even if we were
- // asked for the statement end location).
- if (!Parent) {
- const Stmt *Body = ADC->getBody();
- if (Body)
- L = Body->getBeginLoc();
- else
- L = ADC->getDecl()->getEndLoc();
- break;
- }
-
- L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc();
- } while (!L.isValid());
- }
-
- // FIXME: Ironically, this assert actually fails in some cases.
- //assert(L.isValid());
- return L;
-}
-
-static PathDiagnosticLocation
-getLocationForCaller(const StackFrameContext *SFC,
- const LocationContext *CallerCtx,
- const SourceManager &SM) {
- const CFGBlock &Block = *SFC->getCallSiteBlock();
- CFGElement Source = Block[SFC->getIndex()];
-
- switch (Source.getKind()) {
- case CFGElement::Statement:
- case CFGElement::Constructor:
- case CFGElement::CXXRecordTypedCall:
- return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
- SM, CallerCtx);
- case CFGElement::Initializer: {
- const CFGInitializer &Init = Source.castAs<CFGInitializer>();
- return PathDiagnosticLocation(Init.getInitializer()->getInit(),
- SM, CallerCtx);
- }
- case CFGElement::AutomaticObjectDtor: {
- const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>();
- return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),
- SM, CallerCtx);
- }
- case CFGElement::DeleteDtor: {
- const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>();
- return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx);
- }
- case CFGElement::BaseDtor:
- case CFGElement::MemberDtor: {
- const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
- if (const Stmt *CallerBody = CallerInfo->getBody())
- return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
- return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
- }
- 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:
- llvm_unreachable("CFGElement kind should not be on callsite!");
- }
-
- llvm_unreachable("Unknown CFGElement kind");
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createBegin(const Decl *D,
- const SourceManager &SM) {
- return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createBegin(const Stmt *S,
- const SourceManager &SM,
- LocationOrAnalysisDeclContext LAC) {
- return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
- SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createEnd(const Stmt *S,
- const SourceManager &SM,
- LocationOrAnalysisDeclContext LAC) {
- if (const auto *CS = dyn_cast<CompoundStmt>(S))
- return createEndBrace(CS, SM);
- return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),
- SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
- const SourceManager &SM) {
- return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createConditionalColonLoc(
- const ConditionalOperator *CO,
- const SourceManager &SM) {
- return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
- const SourceManager &SM) {
-
- assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid());
-
- // In some cases, getMemberLoc isn't valid -- in this case we'll return with
- // some other related valid SourceLocation.
- if (ME->getMemberLoc().isValid())
- return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
-
- return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
- const SourceManager &SM) {
- SourceLocation L = CS->getLBracLoc();
- return PathDiagnosticLocation(L, SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
- const SourceManager &SM) {
- SourceLocation L = CS->getRBracLoc();
- return PathDiagnosticLocation(L, SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
- const SourceManager &SM) {
- // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
- if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
- if (!CS->body_empty()) {
- SourceLocation Loc = (*CS->body_begin())->getBeginLoc();
- return PathDiagnosticLocation(Loc, SM, SingleLocK);
- }
-
- return PathDiagnosticLocation();
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
- const SourceManager &SM) {
- SourceLocation L = LC->getDecl()->getBodyRBrace();
- return PathDiagnosticLocation(L, SM, SingleLocK);
-}
-
-PathDiagnosticLocation
-PathDiagnosticLocation::create(const ProgramPoint& P,
- const SourceManager &SMng) {
- const Stmt* S = nullptr;
- if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
- const CFGBlock *BSrc = BE->getSrc();
- if (BSrc->getTerminator().isVirtualBaseBranch()) {
- // TODO: VirtualBaseBranches should also appear for destructors.
- // In this case we should put the diagnostic at the end of decl.
- return PathDiagnosticLocation::createBegin(
- P.getLocationContext()->getDecl(), SMng);
-
- } else {
- S = BSrc->getTerminatorCondition();
- if (!S) {
- // If the BlockEdge has no terminator condition statement but its
- // source is the entry of the CFG (e.g. a checker crated the branch at
- // the beginning of a function), use the function's declaration instead.
- assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no "
- "TerminatorCondition and is not the enrty block of the CFG");
- return PathDiagnosticLocation::createBegin(
- P.getLocationContext()->getDecl(), SMng);
- }
- }
- } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) {
- S = SP->getStmt();
- if (P.getAs<PostStmtPurgeDeadSymbols>())
- return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext());
- } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) {
- return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(),
- SMng);
- } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) {
- return PathDiagnosticLocation(PIC->getLocation(), SMng);
- } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) {
- return PathDiagnosticLocation(PIE->getLocation(), SMng);
- } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
- return getLocationForCaller(CE->getCalleeContext(),
- CE->getLocationContext(),
- SMng);
- } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) {
- return getLocationForCaller(CEE->getCalleeContext(),
- CEE->getLocationContext(),
- SMng);
- } else if (auto CEB = P.getAs<CallExitBegin>()) {
- if (const ReturnStmt *RS = CEB->getReturnStmt())
- return PathDiagnosticLocation::createBegin(RS, SMng,
- CEB->getLocationContext());
- return PathDiagnosticLocation(
- CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng);
- } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
- CFGElement BlockFront = BE->getBlock()->front();
- if (auto StmtElt = BlockFront.getAs<CFGStmt>()) {
- return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng);
- } else if (auto NewAllocElt = BlockFront.getAs<CFGNewAllocator>()) {
- return PathDiagnosticLocation(
- NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng);
- }
- llvm_unreachable("Unexpected CFG element at front of block");
- } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) {
- return PathDiagnosticLocation(FE->getStmt(), SMng,
- FE->getLocationContext());
- } else {
- llvm_unreachable("Unexpected ProgramPoint");
- }
-
- return PathDiagnosticLocation(S, SMng, P.getLocationContext());
-}
-
-static const LocationContext *
-findTopAutosynthesizedParentContext(const LocationContext *LC) {
- assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
- const LocationContext *ParentLC = LC->getParent();
- assert(ParentLC && "We don't start analysis from autosynthesized code");
- while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
- LC = ParentLC;
- ParentLC = LC->getParent();
- assert(ParentLC && "We don't start analysis from autosynthesized code");
- }
- return LC;
-}
-
-const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) {
- // We cannot place diagnostics on autosynthesized code.
- // Put them onto the call site through which we jumped into autosynthesized
- // code for the first time.
- const LocationContext *LC = N->getLocationContext();
- if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
- // It must be a stack frame because we only autosynthesize functions.
- return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
- ->getCallSite();
- }
- // Otherwise, see if the node's program point directly points to a statement.
- ProgramPoint P = N->getLocation();
- if (auto SP = P.getAs<StmtPoint>())
- return SP->getStmt();
- if (auto BE = P.getAs<BlockEdge>())
- return BE->getSrc()->getTerminatorStmt();
- if (auto CE = P.getAs<CallEnter>())
- return CE->getCallExpr();
- if (auto CEE = P.getAs<CallExitEnd>())
- return CEE->getCalleeContext()->getCallSite();
- if (auto PIPP = P.getAs<PostInitializer>())
- return PIPP->getInitializer()->getInit();
- if (auto CEB = P.getAs<CallExitBegin>())
- return CEB->getReturnStmt();
- if (auto FEP = P.getAs<FunctionExitPoint>())
- return FEP->getStmt();
-
- return nullptr;
-}
-
-const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) {
- for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) {
- if (const Stmt *S = getStmt(N)) {
- // Check if the statement is '?' or '&&'/'||'. These are "merges",
- // not actual statement points.
- switch (S->getStmtClass()) {
- case Stmt::ChooseExprClass:
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass:
- continue;
- case Stmt::BinaryOperatorClass: {
- BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
- if (Op == BO_LAnd || Op == BO_LOr)
- continue;
- break;
- }
- default:
- break;
- }
- // We found the statement, so return it.
- return S;
- }
- }
-
- return nullptr;
-}
-
-PathDiagnosticLocation
- PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N,
- const SourceManager &SM) {
- assert(N && "Cannot create a location with a null node.");
- const Stmt *S = getStmt(N);
- const LocationContext *LC = N->getLocationContext();
-
- if (!S) {
- // If this is an implicit call, return the implicit call point location.
- if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>())
- return PathDiagnosticLocation(PIE->getLocation(), SM);
- if (auto FE = N->getLocationAs<FunctionExitPoint>()) {
- if (const ReturnStmt *RS = FE->getStmt())
- return PathDiagnosticLocation::createBegin(RS, SM, LC);
- }
- S = getNextStmt(N);
- }
-
- if (S) {
- ProgramPoint P = N->getLocation();
-
- // For member expressions, return the location of the '.' or '->'.
- if (const auto *ME = dyn_cast<MemberExpr>(S))
- return PathDiagnosticLocation::createMemberLoc(ME, SM);
-
- // For binary operators, return the location of the operator.
- if (const auto *B = dyn_cast<BinaryOperator>(S))
- return PathDiagnosticLocation::createOperatorLoc(B, SM);
-
- if (P.getAs<PostStmtPurgeDeadSymbols>())
- return PathDiagnosticLocation::createEnd(S, SM, LC);
-
- if (S->getBeginLoc().isValid())
- return PathDiagnosticLocation(S, SM, LC);
- return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM);
- }
-
- return createDeclEnd(N->getLocationContext(), SM);
-}
-
-PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
- const PathDiagnosticLocation &PDL) {
- FullSourceLoc L = PDL.asLocation();
- return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
-}
-
-FullSourceLoc
- PathDiagnosticLocation::genLocation(SourceLocation L,
- LocationOrAnalysisDeclContext LAC) const {
- assert(isValid());
- // Note that we want a 'switch' here so that the compiler can warn us in
- // case we add more cases.
- switch (K) {
- case SingleLocK:
- case RangeK:
- break;
- case StmtK:
- // Defensive checking.
- if (!S)
- break;
- return FullSourceLoc(getValidSourceLocation(S, LAC),
- const_cast<SourceManager&>(*SM));
- case DeclK:
- // Defensive checking.
- if (!D)
- break;
- return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
- }
-
- return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
-}
-
-PathDiagnosticRange
- PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
- assert(isValid());
- // Note that we want a 'switch' here so that the compiler can warn us in
- // case we add more cases.
- switch (K) {
- case SingleLocK:
- return PathDiagnosticRange(SourceRange(Loc,Loc), true);
- case RangeK:
- break;
- case StmtK: {
- const Stmt *S = asStmt();
- switch (S->getStmtClass()) {
- default:
- break;
- case Stmt::DeclStmtClass: {
- const auto *DS = cast<DeclStmt>(S);
- if (DS->isSingleDecl()) {
- // Should always be the case, but we'll be defensive.
- return SourceRange(DS->getBeginLoc(),
- DS->getSingleDecl()->getLocation());
- }
- break;
- }
- // FIXME: Provide better range information for different
- // terminators.
- case Stmt::IfStmtClass:
- case Stmt::WhileStmtClass:
- case Stmt::DoStmtClass:
- case Stmt::ForStmtClass:
- case Stmt::ChooseExprClass:
- case Stmt::IndirectGotoStmtClass:
- case Stmt::SwitchStmtClass:
- case Stmt::BinaryConditionalOperatorClass:
- case Stmt::ConditionalOperatorClass:
- case Stmt::ObjCForCollectionStmtClass: {
- SourceLocation L = getValidSourceLocation(S, LAC);
- return SourceRange(L, L);
- }
- }
- SourceRange R = S->getSourceRange();
- if (R.isValid())
- return R;
- break;
- }
- case DeclK:
- if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
- return MD->getSourceRange();
- if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
- if (Stmt *Body = FD->getBody())
- return Body->getSourceRange();
- }
- else {
- SourceLocation L = D->getLocation();
- return PathDiagnosticRange(SourceRange(L, L), true);
- }
- }
-
- return SourceRange(Loc, Loc);
-}
-
-void PathDiagnosticLocation::flatten() {
- if (K == StmtK) {
- K = RangeK;
- S = nullptr;
- D = nullptr;
- }
- else if (K == DeclK) {
- K = SingleLocK;
- S = nullptr;
- D = nullptr;
- }
-}
-
-//===----------------------------------------------------------------------===//
-// Manipulation of PathDiagnosticCallPieces.
-//===----------------------------------------------------------------------===//
-
-std::shared_ptr<PathDiagnosticCallPiece>
-PathDiagnosticCallPiece::construct(const CallExitEnd &CE,
- const SourceManager &SM) {
- const Decl *caller = CE.getLocationContext()->getDecl();
- PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),
- CE.getLocationContext(),
- SM);
- return std::shared_ptr<PathDiagnosticCallPiece>(
- new PathDiagnosticCallPiece(caller, pos));
-}
-
-PathDiagnosticCallPiece *
-PathDiagnosticCallPiece::construct(PathPieces &path,
- const Decl *caller) {
- std::shared_ptr<PathDiagnosticCallPiece> C(
- new PathDiagnosticCallPiece(path, caller));
- path.clear();
- auto *R = C.get();
- path.push_front(std::move(C));
- return R;
-}
-
-void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
- const SourceManager &SM) {
- const StackFrameContext *CalleeCtx = CE.getCalleeContext();
- Callee = CalleeCtx->getDecl();
-
- callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);
- callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);
-
- // Autosynthesized property accessors are special because we'd never
- // pop back up to non-autosynthesized code until we leave them.
- // This is not generally true for autosynthesized callees, which may call
- // non-autosynthesized callbacks.
- // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag
- // defaults to false.
- if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee))
- IsCalleeAnAutosynthesizedPropertyAccessor = (
- MD->isPropertyAccessor() &&
- CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized());
-}
-
-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;
- 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,
- bool ExtendedDescription,
- StringRef Prefix = StringRef()) {
- if (!D)
- return false;
-
- if (isa<BlockDecl>(D)) {
- if (ExtendedDescription)
- Out << Prefix << "anonymous block";
- return ExtendedDescription;
- }
-
- if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
- Out << Prefix;
- if (ExtendedDescription && !MD->isUserProvided()) {
- if (MD->isExplicitlyDefaulted())
- Out << "defaulted ";
- else
- Out << "implicit ";
- }
-
- if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
- if (CD->isDefaultConstructor())
- Out << "default ";
- else if (CD->isCopyConstructor())
- Out << "copy ";
- else if (CD->isMoveConstructor())
- Out << "move ";
-
- Out << "constructor";
- describeClass(Out, MD->getParent(), " for ");
- } else if (isa<CXXDestructorDecl>(MD)) {
- if (!MD->isUserProvided()) {
- Out << "destructor";
- describeClass(Out, MD->getParent(), " for ");
- } else {
- // 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 << "'";
- else
- Out << "'" << *MD << "'";
- }
-
- return true;
- }
-
- 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;
-}
-
-std::shared_ptr<PathDiagnosticEventPiece>
-PathDiagnosticCallPiece::getCallEnterEvent() const {
- // We do not produce call enters and call exits for autosynthesized property
- // accessors. We do generally produce them for other functions coming from
- // the body farm because they may call callbacks that bring us back into
- // visible code.
- if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor)
- return nullptr;
-
- SmallString<256> buf;
- llvm::raw_svector_ostream Out(buf);
-
- Out << "Calling ";
- describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true);
-
- assert(callEnter.asLocation().isValid());
- return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str());
-}
-
-std::shared_ptr<PathDiagnosticEventPiece>
-PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {
- if (!callEnterWithin.asLocation().isValid())
- return nullptr;
- if (Callee->isImplicit() || !Callee->hasBody())
- return nullptr;
- if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee))
- if (MD->isDefaulted())
- return nullptr;
-
- SmallString<256> buf;
- llvm::raw_svector_ostream Out(buf);
-
- Out << "Entered call";
- describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from ");
-
- return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str());
-}
-
-std::shared_ptr<PathDiagnosticEventPiece>
-PathDiagnosticCallPiece::getCallExitEvent() const {
- // We do not produce call enters and call exits for autosynthesized property
- // accessors. We do generally produce them for other functions coming from
- // the body farm because they may call callbacks that bring us back into
- // visible code.
- if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor)
- return nullptr;
-
- SmallString<256> buf;
- llvm::raw_svector_ostream Out(buf);
-
- if (!CallStackMessage.empty()) {
- Out << CallStackMessage;
- } else {
- bool DidDescribe = describeCodeDecl(Out, Callee,
- /*ExtendedDescription=*/false,
- "Returning from ");
- if (!DidDescribe)
- Out << "Returning to caller";
- }
-
- assert(callReturn.asLocation().isValid());
- return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str());
-}
-
-static void compute_path_size(const PathPieces &pieces, unsigned &size) {
- 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;
- }
-}
-
-unsigned PathDiagnostic::full_size() {
- unsigned size = 0;
- compute_path_size(path, size);
- return size;
-}
-
-//===----------------------------------------------------------------------===//
-// FoldingSet profiling methods.
-//===----------------------------------------------------------------------===//
-
-void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger(Range.getBegin().getRawEncoding());
- ID.AddInteger(Range.getEnd().getRawEncoding());
- ID.AddInteger(Loc.getRawEncoding());
-}
-
-void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
- ID.AddInteger((unsigned) getKind());
- ID.AddString(str);
- // FIXME: Add profiling support for code hints.
- ID.AddInteger((unsigned) getDisplayHint());
- ArrayRef<SourceRange> Ranges = getRanges();
- 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 (const auto &I : path)
- ID.Add(*I);
-}
-
-void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
- PathDiagnosticPiece::Profile(ID);
- ID.Add(Pos);
-}
-
-void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
- PathDiagnosticPiece::Profile(ID);
- for (const auto &I : *this)
- ID.Add(I);
-}
-
-void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
- PathDiagnosticSpotPiece::Profile(ID);
- for (const auto &I : subPieces)
- ID.Add(*I);
-}
-
-void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {
- PathDiagnosticSpotPiece::Profile(ID);
-}
-
-void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const {
- PathDiagnosticSpotPiece::Profile(ID);
-}
-
-void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(getLocation());
- ID.AddString(BugType);
- ID.AddString(VerboseDesc);
- ID.AddString(Category);
-}
-
-void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
- Profile(ID);
- 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() = 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 auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
- if (!CE)
- return {};
-
- // Check if one of the parameters are set to the interesting symbol.
- unsigned ArgIndex = 0;
- for (CallExpr::const_arg_iterator I = CE->arg_begin(),
- E = CE->arg_end(); I != E; ++I, ++ArgIndex){
- SVal SV = N->getSVal(*I);
-
- // Check if the variable corresponding to the symbol is passed by value.
- SymbolRef AS = SV.getAsLocSymbol();
- if (AS == Sym) {
- return getMessageForArg(*I, ArgIndex);
- }
-
- // Check if the parameter is a pointer to the symbol.
- if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
- // 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);
- }
- }
- }
-
- // Check if we are returning the interesting symbol.
- SVal SV = N->getSVal(CE);
- SymbolRef RetSym = SV.getAsLocSymbol();
- if (RetSym == Sym) {
- return getMessageForReturn(CE);
- }
-
- return getMessageForSymbolNotFound();
-}
-
-std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
- unsigned ArgIndex) {
- // Printed parameters start at 1, not 0.
- ++ArgIndex;
-
- SmallString<200> buf;
- llvm::raw_svector_ostream os(buf);
-
- os << Msg << " via " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
- << " parameter";
-
- 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 PathDiagnosticPopUpPiece::dump() const {
- llvm::errs() << "POP-UP\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 8387512792974..3a3942a8301bd 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -10,20 +10,22 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/PlistSupport.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Version.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Frontend/ASTUnit.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/TokenConcatenation.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/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
using namespace clang;
@@ -39,12 +41,13 @@ namespace {
class PlistDiagnostics : public PathDiagnosticConsumer {
const std::string OutputFile;
const Preprocessor &PP;
+ const cross_tu::CrossTranslationUnitContext &CTU;
AnalyzerOptions &AnOpts;
const bool SupportsCrossFileDiagnostics;
public:
- PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
- const std::string& prefix,
+ PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix,
const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU,
bool supportsMultipleFiles);
~PlistDiagnostics() override {}
@@ -73,12 +76,14 @@ class PlistPrinter {
const FIDMap& FM;
AnalyzerOptions &AnOpts;
const Preprocessor &PP;
+ const cross_tu::CrossTranslationUnitContext &CTU;
llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
public:
PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts,
- const Preprocessor &PP)
- : FM(FM), AnOpts(AnOpts), PP(PP) {
+ const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU)
+ : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) {
}
void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
@@ -129,6 +134,7 @@ private:
void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges,
unsigned indent);
void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
+ void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent);
void ReportControlFlow(raw_ostream &o,
const PathDiagnosticControlFlowPiece& P,
@@ -160,8 +166,8 @@ struct ExpansionInfo {
} // end of anonymous namespace
static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts,
- const Preprocessor &PP,
+ AnalyzerOptions &AnOpts, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU,
const PathPieces &Path);
/// Print coverage information to output stream {@code o}.
@@ -172,8 +178,9 @@ static void printCoverage(const PathDiagnostic *D,
FIDMap &FM,
llvm::raw_fd_ostream &o);
-static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
- const Preprocessor &PP);
+static ExpansionInfo
+getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU);
//===----------------------------------------------------------------------===//
// Methods of PlistPrinter.
@@ -216,6 +223,33 @@ void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
EmitString(o, Message) << '\n';
}
+void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits,
+ unsigned indent) {
+ if (fixits.size() == 0)
+ return;
+
+ const SourceManager &SM = PP.getSourceManager();
+ const LangOptions &LangOpts = PP.getLangOpts();
+
+ Indent(o, indent) << "<key>fixits</key>\n";
+ Indent(o, indent) << "<array>\n";
+ for (const auto &fixit : fixits) {
+ assert(!fixit.isNull());
+ // FIXME: Add support for InsertFromRange and BeforePreviousInsertion.
+ assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!");
+ assert(!fixit.BeforePreviousInsertions && "Not implemented yet!");
+ Indent(o, indent) << " <dict>\n";
+ Indent(o, indent) << " <key>remove_range</key>\n";
+ EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts),
+ FM, indent + 2);
+ Indent(o, indent) << " <key>insert_string</key>";
+ EmitString(o, fixit.CodeToInsert);
+ o << "\n";
+ Indent(o, indent) << " </dict>\n";
+ }
+ Indent(o, indent) << "</array>\n";
+}
+
void PlistPrinter::ReportControlFlow(raw_ostream &o,
const PathDiagnosticControlFlowPiece& P,
unsigned indent) {
@@ -266,6 +300,9 @@ void PlistPrinter::ReportControlFlow(raw_ostream &o,
EmitString(o, s) << '\n';
}
+ assert(P.getFixits().size() == 0 &&
+ "Fixits on constrol flow pieces are not implemented yet!");
+
--indent;
Indent(o, indent) << "</dict>\n";
}
@@ -302,6 +339,9 @@ void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P
// Output the text.
EmitMessage(o, P.getString(), indent);
+ // Output the fixits.
+ EmitFixits(o, P.getFixits(), indent);
+
// Finish up.
--indent;
Indent(o, indent); o << "</dict>\n";
@@ -329,6 +369,9 @@ void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
if (auto callExit = P.getCallExitEvent())
ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true);
+
+ assert(P.getFixits().size() == 0 &&
+ "Fixits on call pieces are not implemented yet!");
}
void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
@@ -341,13 +384,16 @@ void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
I != E; ++I) {
ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false);
}
+
+ assert(P.getFixits().size() == 0 &&
+ "Fixits on constrol flow pieces are not implemented yet!");
}
void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
for (const PathDiagnosticMacroPiece *P : MacroPieces) {
const SourceManager &SM = PP.getSourceManager();
- ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP);
+ ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP, CTU);
Indent(o, indent) << "<dict>\n";
++indent;
@@ -398,6 +444,9 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
// Output the text.
EmitMessage(o, P.getString(), indent);
+ // Output the fixits.
+ EmitFixits(o, P.getFixits(), indent);
+
// Finish up.
--indent;
Indent(o, indent); o << "</dict>\n";
@@ -426,6 +475,9 @@ void PlistPrinter::ReportPopUp(raw_ostream &o,
// Output the text.
EmitMessage(o, P.getString(), indent);
+ assert(P.getFixits().size() == 0 &&
+ "Fixits on pop-up pieces are not implemented yet!");
+
// Finish up.
--indent;
Indent(o, indent) << "</dict>\n";
@@ -469,20 +521,20 @@ static void printCoverage(const PathDiagnostic *D,
}
static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts,
- const Preprocessor &PP,
+ AnalyzerOptions &AnOpts, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU,
const PathPieces &Path) {
- PlistPrinter Printer(FM, AnOpts, PP);
- assert(std::is_partitioned(
- Path.begin(), Path.end(),
- [](const std::shared_ptr<PathDiagnosticPiece> &E)
- { return E->getKind() == PathDiagnosticPiece::Note; }) &&
+ PlistPrinter Printer(FM, AnOpts, PP, CTU);
+ assert(std::is_partitioned(Path.begin(), Path.end(),
+ [](const PathDiagnosticPieceRef &E) {
+ return E->getKind() == PathDiagnosticPiece::Note;
+ }) &&
"PathDiagnostic is not partitioned so that notes precede the rest");
PathPieces::const_iterator FirstNonNote = std::partition_point(
- Path.begin(), Path.end(),
- [](const std::shared_ptr<PathDiagnosticPiece> &E)
- { return E->getKind() == PathDiagnosticPiece::Note; });
+ Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) {
+ return E->getKind() == PathDiagnosticPiece::Note;
+ });
PathPieces::const_iterator I = Path.begin();
@@ -518,26 +570,29 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
// Methods of PlistDiagnostics.
//===----------------------------------------------------------------------===//
-PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
- const std::string& output,
- const Preprocessor &PP,
- bool supportsMultipleFiles)
- : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
-
-void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string& s,
- const Preprocessor &PP) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP,
+PlistDiagnostics::PlistDiagnostics(
+ AnalyzerOptions &AnalyzerOpts, const std::string &output,
+ const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
+ bool supportsMultipleFiles)
+ : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {
+ // FIXME: Will be used by a later planned change.
+ (void)this->CTU;
+}
+
+void ento::createPlistDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &s, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
/*supportsMultipleFiles*/ false));
}
-void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string &s,
- const Preprocessor &PP) {
- C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP,
+void ento::createPlistMultiFileDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &s, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU,
/*supportsMultipleFiles*/ true));
}
void PlistDiagnostics::FlushDiagnosticsImpl(
@@ -590,7 +645,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
// Open the file.
std::error_code EC;
- llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text);
+ llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_Text);
if (EC) {
llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
return;
@@ -614,7 +669,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <dict>\n";
const PathDiagnostic *D = *DI;
- printBugPath(o, FM, AnOpts, PP, D->path);
+ printBugPath(o, FM, AnOpts, PP, CTU, D->path);
// Output the bug type and bug category.
o << " <key>description</key>";
@@ -624,7 +679,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <key>type</key>";
EmitString(o, D->getBugType()) << '\n';
o << " <key>check_name</key>";
- EmitString(o, D->getCheckName()) << '\n';
+ EmitString(o, D->getCheckerName()) << '\n';
o << " <!-- This hash is experimental and going to change! -->\n";
o << " <key>issue_hash_content_of_line_in_context</key>";
@@ -634,7 +689,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
: D->getLocation().asLocation()),
SM);
const Decl *DeclWithIssue = D->getDeclWithIssue();
- EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(),
+ EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(),
DeclWithIssue, LangOpts))
<< '\n';
@@ -867,17 +922,23 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
// Definitions of helper functions and methods for expanding macros.
//===----------------------------------------------------------------------===//
-static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
- const Preprocessor &PP) {
+static ExpansionInfo
+getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ const Preprocessor *PPToUse = &PP;
+ if (auto LocAndUnit = CTU.getImportedFromSourceLocation(MacroLoc)) {
+ MacroLoc = LocAndUnit->first;
+ PPToUse = &LocAndUnit->second->getPreprocessor();
+ }
llvm::SmallString<200> ExpansionBuf;
llvm::raw_svector_ostream OS(ExpansionBuf);
- TokenPrinter Printer(OS, PP);
+ TokenPrinter Printer(OS, *PPToUse);
llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens;
- std::string MacroName =
- getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{},
- AlreadyProcessedTokens);
+ std::string MacroName = getMacroNameAndPrintExpansion(
+ Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens);
return { MacroName, OS.str() };
}
diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp
index a1ca0b1b84bfa..f50d82de3b28d 100644
--- a/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -15,7 +15,7 @@
#include "clang/Basic/JsonSupport.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
#include "llvm/Support/raw_ostream.h"
diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 64724227395d5..9752a0e22832c 100644
--- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -330,7 +330,7 @@ private:
std::unique_ptr<ConstraintManager>
ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
- return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
+ return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
bool RangeConstraintManager::canReasonAbout(SVal X) const {
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index d2aea1fd92dda..5d2ef59e2d66b 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -108,7 +108,7 @@ public:
Data == X.Data;
}
- void dump() const;
+ LLVM_DUMP_METHOD void dump() const;
};
} // end anonymous namespace
@@ -135,7 +135,9 @@ static inline raw_ostream &operator<<(raw_ostream &Out, BindingKey K) {
} // namespace llvm
-LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; }
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+void BindingKey::dump() const { llvm::errs() << *this; }
+#endif
//===----------------------------------------------------------------------===//
// Actual Store type.
@@ -153,28 +155,42 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
ClusterBindings> {
ClusterBindings::Factory *CBFactory;
+ // This flag indicates whether the current bindings are within the analysis
+ // that has started from main(). It affects how we perform loads from
+ // global variables that have initializers: if we have observed the
+ // program execution from the start and we know that these variables
+ // have not been overwritten yet, we can be sure that their initializers
+ // are still relevant. This flag never gets changed when the bindings are
+ // updated, so it could potentially be moved into RegionStoreManager
+ // (as if it's the same bindings but a different loading procedure)
+ // however that would have made the manager needlessly stateful.
+ bool IsMainAnalysis;
+
public:
typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>
ParentTy;
RegionBindingsRef(ClusterBindings::Factory &CBFactory,
const RegionBindings::TreeTy *T,
- RegionBindings::TreeTy::Factory *F)
+ RegionBindings::TreeTy::Factory *F,
+ bool IsMainAnalysis)
: llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F),
- CBFactory(&CBFactory) {}
+ CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
- RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory)
+ RegionBindingsRef(const ParentTy &P,
+ ClusterBindings::Factory &CBFactory,
+ bool IsMainAnalysis)
: llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P),
- CBFactory(&CBFactory) {}
+ CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {}
RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
- *CBFactory);
+ *CBFactory, IsMainAnalysis);
}
RegionBindingsRef remove(key_type_ref K) const {
return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
- *CBFactory);
+ *CBFactory, IsMainAnalysis);
}
RegionBindingsRef addBinding(BindingKey K, SVal V) const;
@@ -204,7 +220,13 @@ public:
/// Return the internal tree as a Store.
Store asStore() const {
- return asImmutableMap().getRootWithoutRetain();
+ llvm::PointerIntPair<Store, 1, bool> Ptr = {
+ asImmutableMap().getRootWithoutRetain(), IsMainAnalysis};
+ return reinterpret_cast<Store>(Ptr.getOpaqueValue());
+ }
+
+ bool isMainAnalysis() const {
+ return IsMainAnalysis;
}
void printJson(raw_ostream &Out, const char *NL = "\n",
@@ -379,8 +401,15 @@ public:
/// casts from arrays to pointers.
SVal ArrayToPointer(Loc Array, QualType ElementTy) override;
+ /// Creates the Store that correctly represents memory contents before
+ /// the beginning of the analysis of the given top-level stack frame.
StoreRef getInitialStore(const LocationContext *InitLoc) override {
- return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this);
+ bool IsMainAnalysis = false;
+ if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl()))
+ IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus;
+ return StoreRef(RegionBindingsRef(
+ RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory),
+ CBFactory, IsMainAnalysis).asStore(), *this);
}
//===-------------------------------------------------------------------===//
@@ -606,9 +635,13 @@ public: // Part of public interface to class.
//===------------------------------------------------------------------===//
RegionBindingsRef getRegionBindings(Store store) const {
- return RegionBindingsRef(CBFactory,
- static_cast<const RegionBindings::TreeTy*>(store),
- RBFactory.getTreeFactory());
+ llvm::PointerIntPair<Store, 1, bool> Ptr;
+ Ptr.setFromOpaqueValue(const_cast<void *>(store));
+ return RegionBindingsRef(
+ CBFactory,
+ static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()),
+ RBFactory.getTreeFactory(),
+ Ptr.getInt());
}
void printJson(raw_ostream &Out, Store S, const char *NL = "\n",
@@ -642,14 +675,14 @@ public: // Part of public interface to class.
std::unique_ptr<StoreManager>
ento::CreateRegionStoreManager(ProgramStateManager &StMgr) {
RegionStoreFeatures F = maximal_features_tag();
- return llvm::make_unique<RegionStoreManager>(StMgr, F);
+ return std::make_unique<RegionStoreManager>(StMgr, F);
}
std::unique_ptr<StoreManager>
ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
RegionStoreFeatures F = minimal_features_tag();
F.enableFields(true);
- return llvm::make_unique<RegionStoreManager>(StMgr, F);
+ return std::make_unique<RegionStoreManager>(StMgr, F);
}
@@ -1669,10 +1702,12 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
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.
+ // Check if the containing array has an initialized value that we can trust.
+ // We can trust a const value or a value of a global initializer in main().
const VarDecl *VD = VR->getDecl();
- // Either the array or the array element has to be const.
- if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
+ if (VD->getType().isConstQualified() ||
+ R->getElementType().isConstQualified() ||
+ (B.isMainAnalysis() && VD->hasGlobalStorage())) {
if (const Expr *Init = VD->getAnyInitializer()) {
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
// The array index has to be known.
@@ -1761,8 +1796,11 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
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())
+ // Either the record variable or the field has an initializer that we can
+ // trust. We trust initializers of constants and, additionally, respect
+ // initializers of globals when analyzing main().
+ if (RecordVarTy.isConstQualified() || Ty.isConstQualified() ||
+ (B.isMainAnalysis() && VD->hasGlobalStorage()))
if (const Expr *Init = VD->getAnyInitializer())
if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
if (Index < InitList->getNumInits()) {
@@ -1980,6 +2018,12 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
if (isa<GlobalsSpaceRegion>(MS)) {
QualType T = VD->getType();
+ // If we're in main(), then global initializers have not become stale yet.
+ if (B.isMainAnalysis())
+ if (const Expr *Init = VD->getAnyInitializer())
+ if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ return *V;
+
// Function-scoped static variables are default-initialized to 0; if they
// have an initializer, it would have been processed by now.
// FIXME: This is only true when we're starting analysis from main().
@@ -2247,8 +2291,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
const TypedValueRegion* R,
SVal V) {
QualType T = R->getValueType();
- assert(T->isVectorType());
- const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
+ const VectorType *VT = T->castAs<VectorType>(); // Use castAs for typedefs.
// Handle lazy compound values and symbolic values.
if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>())
@@ -2333,7 +2376,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
QualType T = R->getValueType();
assert(T->isStructureOrClassType());
- const RecordType* RT = T->getAs<RecordType>();
+ const RecordType* RT = T->castAs<RecordType>();
const RecordDecl *RD = RT->getDecl();
if (!RD->isCompleteDefinition())
diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
index d5c14351d3302..6ad12ca0a688f 100644
--- a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -14,5 +14,5 @@ using namespace ento;
std::unique_ptr<ConstraintManager>
ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
- return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder());
+ return std::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder());
}
diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index d1faf3f4dea9d..190ab7e21dbcc 100644
--- a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -10,10 +10,10 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
@@ -43,10 +43,10 @@ public:
};
} // end anonymous namespace
-void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string &Output,
- const Preprocessor &) {
+void ento::createSarifDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Output, const Preprocessor &,
+ const cross_tu::CrossTranslationUnitContext &) {
C.push_back(new SarifDiagnostics(AnalyzerOpts, Output));
}
@@ -106,26 +106,26 @@ static std::string fileNameToURI(StringRef Filename) {
return Ret.str().str();
}
-static json::Object createFileLocation(const FileEntry &FE) {
+static json::Object createArtifactLocation(const FileEntry &FE) {
return json::Object{{"uri", fileNameToURI(getFileName(FE))}};
}
-static json::Object createFile(const FileEntry &FE) {
- return json::Object{{"fileLocation", createFileLocation(FE)},
+static json::Object createArtifact(const FileEntry &FE) {
+ return json::Object{{"location", createArtifactLocation(FE)},
{"roles", json::Array{"resultFile"}},
{"length", FE.getSize()},
{"mimeType", "text/plain"}};
}
-static json::Object createFileLocation(const FileEntry &FE,
- json::Array &Files) {
+static json::Object createArtifactLocation(const FileEntry &FE,
+ json::Array &Artifacts) {
std::string FileURI = fileNameToURI(getFileName(FE));
- // See if the Files array contains this URI already. If it does not, create
- // a new file object to add to the array.
- auto I = llvm::find_if(Files, [&](const json::Value &File) {
+ // See if the Artifacts array contains this URI already. If it does not,
+ // create a new artifact object to add to the array.
+ auto I = llvm::find_if(Artifacts, [&](const json::Value &File) {
if (const json::Object *Obj = File.getAsObject()) {
- if (const json::Object *FileLoc = Obj->getObject("fileLocation")) {
+ if (const json::Object *FileLoc = Obj->getObject("location")) {
Optional<StringRef> URI = FileLoc->getString("uri");
return URI && URI->equals(FileURI);
}
@@ -133,28 +133,35 @@ static json::Object createFileLocation(const FileEntry &FE,
return false;
});
- // Calculate the index within the file location array so it can be stored in
+ // Calculate the index within the artifact array so it can be stored in
// the JSON object.
- auto Index = static_cast<unsigned>(std::distance(Files.begin(), I));
- if (I == Files.end())
- Files.push_back(createFile(FE));
+ auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I));
+ if (I == Artifacts.end())
+ Artifacts.push_back(createArtifact(FE));
- return json::Object{{"uri", FileURI}, {"fileIndex", Index}};
+ return json::Object{{"uri", FileURI}, {"index", Index}};
}
static json::Object createTextRegion(SourceRange R, const SourceManager &SM) {
- return json::Object{
+ json::Object Region{
{"startLine", SM.getExpansionLineNumber(R.getBegin())},
- {"endLine", SM.getExpansionLineNumber(R.getEnd())},
{"startColumn", SM.getExpansionColumnNumber(R.getBegin())},
- {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}};
+ };
+ if (R.getBegin() == R.getEnd()) {
+ Region["endColumn"] = SM.getExpansionColumnNumber(R.getBegin());
+ } else {
+ Region["endLine"] = SM.getExpansionLineNumber(R.getEnd());
+ Region["endColumn"] = SM.getExpansionColumnNumber(R.getEnd()) + 1;
+ }
+ return Region;
}
static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE,
const SourceManager &SMgr,
- json::Array &Files) {
- return json::Object{{{"fileLocation", createFileLocation(FE, Files)},
- {"region", createTextRegion(R, SMgr)}}};
+ json::Array &Artifacts) {
+ return json::Object{
+ {{"artifactLocation", createArtifactLocation(FE, Artifacts)},
+ {"region", createTextRegion(R, SMgr)}}};
}
enum class Importance { Important, Essential, Unimportant };
@@ -207,15 +214,16 @@ static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
}
static json::Object createThreadFlow(const PathPieces &Pieces,
- json::Array &Files) {
+ json::Array &Artifacts) {
const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
json::Array Locations;
for (const auto &Piece : Pieces) {
const PathDiagnosticLocation &P = Piece->getLocation();
Locations.push_back(createThreadFlowLocation(
- createLocation(createPhysicalLocation(P.asRange(),
- *P.asLocation().getFileEntry(),
- SMgr, Files),
+ createLocation(createPhysicalLocation(
+ P.asRange(),
+ *P.asLocation().getExpansionLoc().getFileEntry(),
+ SMgr, Artifacts),
Piece->getString()),
calculateImportance(*Piece)));
}
@@ -223,35 +231,30 @@ static json::Object createThreadFlow(const PathPieces &Pieces,
}
static json::Object createCodeFlow(const PathPieces &Pieces,
- json::Array &Files) {
+ json::Array &Artifacts) {
return json::Object{
- {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}};
+ {"threadFlows", json::Array{createThreadFlow(Pieces, Artifacts)}}};
}
-static json::Object createTool() {
- return json::Object{{"name", "clang"},
- {"fullName", "clang static analyzer"},
- {"language", "en-US"},
- {"version", getClangFullVersion()}};
-}
-
-static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files,
+static json::Object createResult(const PathDiagnostic &Diag,
+ json::Array &Artifacts,
const StringMap<unsigned> &RuleMapping) {
const PathPieces &Path = Diag.path.flatten(false);
const SourceManager &SMgr = Path.front()->getLocation().getManager();
- auto Iter = RuleMapping.find(Diag.getCheckName());
+ auto Iter = RuleMapping.find(Diag.getCheckerName());
assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?");
return json::Object{
{"message", createMessage(Diag.getVerboseDescription())},
- {"codeFlows", json::Array{createCodeFlow(Path, Files)}},
+ {"codeFlows", json::Array{createCodeFlow(Path, Artifacts)}},
{"locations",
json::Array{createLocation(createPhysicalLocation(
Diag.getLocation().asRange(),
- *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}},
+ *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(),
+ SMgr, Artifacts))}},
{"ruleIndex", Iter->getValue()},
- {"ruleId", Diag.getCheckName()}};
+ {"ruleId", Diag.getCheckerName()}};
}
static StringRef getRuleDescription(StringRef CheckName) {
@@ -277,10 +280,10 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) {
}
static json::Object createRule(const PathDiagnostic &Diag) {
- StringRef CheckName = Diag.getCheckName();
+ StringRef CheckName = Diag.getCheckerName();
json::Object Ret{
{"fullDescription", createMessage(getRuleDescription(CheckName))},
- {"name", createMessage(CheckName)},
+ {"name", CheckName},
{"id", CheckName}};
std::string RuleURI = getRuleHelpURIStr(CheckName);
@@ -296,7 +299,7 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags,
llvm::StringSet<> Seen;
llvm::for_each(Diags, [&](const PathDiagnostic *D) {
- StringRef RuleID = D->getCheckName();
+ StringRef RuleID = D->getCheckerName();
std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID);
if (P.second) {
RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index.
@@ -307,24 +310,28 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags,
return Rules;
}
-static json::Object createResources(std::vector<const PathDiagnostic *> &Diags,
- StringMap<unsigned> &RuleMapping) {
- return json::Object{{"rules", createRules(Diags, RuleMapping)}};
+static json::Object createTool(std::vector<const PathDiagnostic *> &Diags,
+ StringMap<unsigned> &RuleMapping) {
+ return json::Object{
+ {"driver", json::Object{{"name", "clang"},
+ {"fullName", "clang static analyzer"},
+ {"language", "en-US"},
+ {"version", getClangFullVersion()},
+ {"rules", createRules(Diags, RuleMapping)}}}};
}
static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) {
- json::Array Results, Files;
+ json::Array Results, Artifacts;
StringMap<unsigned> RuleMapping;
- json::Object Resources = createResources(Diags, RuleMapping);
+ json::Object Tool = createTool(Diags, RuleMapping);
llvm::for_each(Diags, [&](const PathDiagnostic *D) {
- Results.push_back(createResult(*D, Files, RuleMapping));
+ Results.push_back(createResult(*D, Artifacts, RuleMapping));
});
- return json::Object{{"tool", createTool()},
- {"resources", std::move(Resources)},
+ return json::Object{{"tool", std::move(Tool)},
{"results", std::move(Results)},
- {"files", std::move(Files)}};
+ {"artifacts", std::move(Artifacts)}};
}
void SarifDiagnostics::FlushDiagnosticsImpl(
@@ -335,15 +342,15 @@ void SarifDiagnostics::FlushDiagnosticsImpl(
// file can become large very quickly, so decoding into JSON to append a run
// may be an expensive operation.
std::error_code EC;
- llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
+ llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text);
if (EC) {
llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
return;
}
json::Object Sarif{
{"$schema",
- "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"},
- {"version", "2.0.0-csd.2.beta.2018-11-28"},
+ "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"},
+ {"version", "2.1.0"},
{"runs", json::Array{createRun(Diags)}}};
OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif)));
}
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index 3cf616161c669..b4ab6877726ce 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -52,7 +52,7 @@ StoreRef StoreManager::enterStackFrame(Store OldStore,
Call.getInitialStackFrameContents(LCtx, InitialBindings);
for (const auto &I : InitialBindings)
- Store = Bind(Store.getStore(), I.first, I.second);
+ Store = Bind(Store.getStore(), I.first.castAs<Loc>(), I.second);
return Store;
}
diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp
index 129d1720395e4..348552ba73a9b 100644
--- a/lib/StaticAnalyzer/Core/WorkList.cpp
+++ b/lib/StaticAnalyzer/Core/WorkList.cpp
@@ -79,11 +79,11 @@ public:
WorkList::~WorkList() = default;
std::unique_ptr<WorkList> WorkList::makeDFS() {
- return llvm::make_unique<DFS>();
+ return std::make_unique<DFS>();
}
std::unique_ptr<WorkList> WorkList::makeBFS() {
- return llvm::make_unique<BFS>();
+ return std::make_unique<BFS>();
}
namespace {
@@ -124,7 +124,7 @@ namespace {
} // namespace
std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() {
- return llvm::make_unique<BFSBlockDFSContents>();
+ return std::make_unique<BFSBlockDFSContents>();
}
namespace {
@@ -186,7 +186,7 @@ public:
} // namespace
std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() {
- return llvm::make_unique<UnexploredFirstStack>();
+ return std::make_unique<UnexploredFirstStack>();
}
namespace {
@@ -249,7 +249,7 @@ public:
} // namespace
std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() {
- return llvm::make_unique<UnexploredFirstPriorityQueue>();
+ return std::make_unique<UnexploredFirstPriorityQueue>();
}
namespace {
@@ -309,5 +309,5 @@ public:
}
std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() {
- return llvm::make_unique<UnexploredFirstPriorityLocationQueue>();
+ return std::make_unique<UnexploredFirstPriorityLocationQueue>();
}
diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 454b61fd51a14..8236907ea773b 100644
--- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -12,6 +12,7 @@
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "ModelInjector.h"
+#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
@@ -27,7 +28,6 @@
#include "clang/StaticAnalyzer/Checkers/LocalCheckers.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/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
@@ -64,30 +64,30 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
// Special PathDiagnosticConsumers.
//===----------------------------------------------------------------------===//
-void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string &prefix,
- const Preprocessor &PP) {
+void ento::createPlistHTMLDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &prefix, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
createHTMLDiagnosticConsumer(AnalyzerOpts, C,
- llvm::sys::path::parent_path(prefix), PP);
- createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
+ llvm::sys::path::parent_path(prefix), PP, CTU);
+ createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
}
-void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
- PathDiagnosticConsumers &C,
- const std::string &Prefix,
- const clang::Preprocessor &PP) {
+void ento::createTextPathDiagnosticConsumer(
+ AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ const std::string &Prefix, const clang::Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
llvm_unreachable("'text' consumer should be enabled on ClangDiags");
}
namespace {
class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
DiagnosticsEngine &Diag;
- bool IncludePath, ShouldEmitAsError;
+ bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false;
public:
ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
- : Diag(Diag), IncludePath(false), ShouldEmitAsError(false) {}
+ : Diag(Diag) {}
~ClangDiagPathDiagConsumer() override {}
StringRef getName() const override { return "ClangDiags"; }
@@ -98,11 +98,9 @@ public:
return IncludePath ? Minimal : None;
}
- void enablePaths() {
- IncludePath = true;
- }
-
+ void enablePaths() { IncludePath = true; }
void enableWerror() { ShouldEmitAsError = true; }
+ void enableFixitsAsRemarks() { FixitsAsRemarks = true; }
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override {
@@ -111,22 +109,46 @@ public:
? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")
: Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
-
- for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(),
- E = Diags.end(); I != E; ++I) {
+ unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0");
+
+ auto reportPiece =
+ [&](unsigned ID, SourceLocation Loc, StringRef String,
+ ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) {
+ if (!FixitsAsRemarks) {
+ Diag.Report(Loc, ID) << String << Ranges << Fixits;
+ } else {
+ Diag.Report(Loc, ID) << String << Ranges;
+ for (const FixItHint &Hint : Fixits) {
+ SourceManager &SM = Diag.getSourceManager();
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ // FIXME: Add support for InsertFromRange and
+ // BeforePreviousInsertion.
+ assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!");
+ assert(!Hint.BeforePreviousInsertions && "Not implemented yet!");
+ OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin())
+ << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd())
+ << ": '" << Hint.CodeToInsert << "'";
+ Diag.Report(Loc, RemarkID) << OS.str();
+ }
+ }
+ };
+
+ for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(),
+ E = Diags.end();
+ I != E; ++I) {
const PathDiagnostic *PD = *I;
- SourceLocation WarnLoc = PD->getLocation().asLocation();
- Diag.Report(WarnLoc, WarnID) << PD->getShortDescription()
- << PD->path.back()->getRanges();
+ reportPiece(WarnID, PD->getLocation().asLocation(),
+ PD->getShortDescription(), PD->path.back()->getRanges(),
+ PD->path.back()->getFixits());
// First, add extra notes, even if paths should not be included.
for (const auto &Piece : PD->path) {
if (!isa<PathDiagnosticNotePiece>(Piece.get()))
continue;
- SourceLocation NoteLoc = Piece->getLocation().asLocation();
- Diag.Report(NoteLoc, NoteID) << Piece->getString()
- << Piece->getRanges();
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(), Piece->getFixits());
}
if (!IncludePath)
@@ -138,9 +160,8 @@ public:
if (isa<PathDiagnosticNotePiece>(Piece.get()))
continue;
- SourceLocation NoteLoc = Piece->getLocation().asLocation();
- Diag.Report(NoteLoc, NoteID) << Piece->getString()
- << Piece->getRanges();
+ reportPiece(NoteID, Piece->getLocation().asLocation(),
+ Piece->getString(), Piece->getRanges(), Piece->getFixits());
}
}
}
@@ -212,13 +233,13 @@ public:
Plugins(plugins), Injector(injector), CTU(CI) {
DigestAnalyzerOptions();
if (Opts->PrintStats || Opts->ShouldSerializeStats) {
- AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>(
+ AnalyzerTimers = std::make_unique<llvm::TimerGroup>(
"analyzer", "Analyzer timers");
- SyntaxCheckTimer = llvm::make_unique<llvm::Timer>(
+ SyntaxCheckTimer = std::make_unique<llvm::Timer>(
"syntaxchecks", "Syntax-based analysis time", *AnalyzerTimers);
- ExprEngineTimer = llvm::make_unique<llvm::Timer>(
+ ExprEngineTimer = std::make_unique<llvm::Timer>(
"exprengine", "Path exploration time", *AnalyzerTimers);
- BugReporterTimer = llvm::make_unique<llvm::Timer>(
+ BugReporterTimer = std::make_unique<llvm::Timer>(
"bugreporter", "Path-sensitive report post-processing time",
*AnalyzerTimers);
llvm::EnableStatistics(/* PrintOnExit= */ false);
@@ -241,6 +262,9 @@ public:
if (Opts->AnalyzerWerror)
clangDiags->enableWerror();
+ if (Opts->ShouldEmitFixItHintsAsRemarks)
+ clangDiags->enableFixitsAsRemarks();
+
if (Opts->AnalysisDiagOpt == PD_TEXT) {
clangDiags->enablePaths();
@@ -249,7 +273,7 @@ public:
default:
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
case PD_##NAME: \
- CREATEFN(*Opts.get(), PathConsumers, OutDir, PP); \
+ CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
}
@@ -311,9 +335,9 @@ public:
checkerMgr = createCheckerManager(
*Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics());
- Mgr = llvm::make_unique<AnalysisManager>(
- *Ctx, PP.getDiagnostics(), PathConsumers, CreateStoreMgr,
- CreateConstraintMgr, checkerMgr.get(), *Opts, Injector);
+ Mgr = std::make_unique<AnalysisManager>(*Ctx, PathConsumers, CreateStoreMgr,
+ CreateConstraintMgr,
+ checkerMgr.get(), *Opts, Injector);
}
/// Store the top level decls in the set to be processed later on.
@@ -609,6 +633,7 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
// After all decls handled, run checkers on the entire TranslationUnit.
checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
+ BR.FlushReports();
RecVisitorBR = nullptr;
}
@@ -626,7 +651,7 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
if (isBisonFile(C)) {
reportAnalyzerProgress("Skipping bison-generated file\n");
- } else if (Opts->DisableAllChecks) {
+ } else if (Opts->DisableAllCheckers) {
// Don't analyze if the user explicitly asked for no checks to be performed
// on this file.
@@ -766,6 +791,9 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
if (SyntaxCheckTimer)
SyntaxCheckTimer->stopTimer();
}
+
+ BR.FlushReports();
+
if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
RunPathSensitiveChecks(D, IMode, VisitedCallees);
if (IMode != ExprEngine::Inline_Minimal)
@@ -826,7 +854,7 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) {
AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
- return llvm::make_unique<AnalysisConsumer>(
+ return std::make_unique<AnalysisConsumer>(
CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
CI.getFrontendOpts().Plugins,
hasModelPath ? new ModelInjector(CI) : nullptr);
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
index 1e45ee96145ab..f4f06e32cd1d7 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -30,7 +30,7 @@ std::unique_ptr<CheckerManager> ento::createCheckerManager(
ArrayRef<std::string> plugins,
ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns,
DiagnosticsEngine &diags) {
- auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts);
+ auto checkerMgr = std::make_unique<CheckerManager>(context, opts);
CheckerRegistry allCheckers(plugins, diags, opts, context.getLangOpts(),
checkerRegistrationFns);
@@ -74,11 +74,22 @@ void ento::printCheckerConfigList(raw_ostream &OS,
}
void ento::printAnalyzerConfigList(raw_ostream &out) {
- out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n";
- out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
- out << " -analyzer-config OPTION1=VALUE, -analyzer-config "
- "OPTION2=VALUE, ...\n\n";
- out << "OPTIONS:\n\n";
+ // FIXME: This message sounds scary, should be scary, but incorrectly states
+ // that all configs are super dangerous. In reality, many of them should be
+ // accessible to the user. We should create a user-facing subset of config
+ // options under a different frontend flag.
+ out << R"(
+OVERVIEW: Clang Static Analyzer -analyzer-config Option List
+
+The following list of configurations are meant for development purposes only, as
+some of the variables they define are set to result in the most optimal
+analysis. Setting them to other values may drastically change how the analyzer
+behaves, and may even result in instabilities, crashes!
+
+USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>
+ -analyzer-config OPTION1=VALUE, -analyzer-config OPTION2=VALUE, ...
+OPTIONS:
+)";
using OptionAndDescriptionTy = std::pair<StringRef, std::string>;
OptionAndDescriptionTy PrintableOptions[] = {
diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
index 3fd4c36947cbb..e00fd976f6b80 100644
--- a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
+++ b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -200,12 +200,12 @@ CheckerRegistry::CheckerRegistry(
// Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the
// command line.
- for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) {
+ for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) {
CheckerInfoListRange CheckerForCmdLineArg =
getMutableCheckersForCmdLineArg(Opt.first);
if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) {
- Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first;
+ Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first;
Diags.Report(diag::note_suggest_disabling_all_checkers);
}
@@ -437,7 +437,7 @@ void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const {
// Initialize the CheckerManager with all enabled checkers.
for (const auto *Checker : enabledCheckers) {
- CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName));
+ CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName));
Checker->Initialize(CheckerMgr);
}
}
@@ -468,9 +468,10 @@ isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList,
void CheckerRegistry::validateCheckerOptions() const {
for (const auto &Config : AnOpts.Config) {
- StringRef SuppliedChecker;
+ StringRef SuppliedCheckerOrPackage;
StringRef SuppliedOption;
- std::tie(SuppliedChecker, SuppliedOption) = Config.getKey().split(':');
+ std::tie(SuppliedCheckerOrPackage, SuppliedOption) =
+ Config.getKey().split(':');
if (SuppliedOption.empty())
continue;
@@ -483,21 +484,24 @@ void CheckerRegistry::validateCheckerOptions() const {
// Since lower_bound would look for the first element *not less* than "cor",
// it would return with an iterator to the first checker in the core, so we
// we really have to use find here, which uses operator==.
- auto CheckerIt = llvm::find(Checkers, CheckerInfo(SuppliedChecker));
+ auto CheckerIt =
+ llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage));
if (CheckerIt != Checkers.end()) {
- isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedChecker,
+ isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage,
SuppliedOption, AnOpts, Diags);
continue;
}
- auto PackageIt = llvm::find(Packages, PackageInfo(SuppliedChecker));
+ auto PackageIt =
+ llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage));
if (PackageIt != Packages.end()) {
- isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedChecker,
+ isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage,
SuppliedOption, AnOpts, Diags);
continue;
}
- Diags.Report(diag::err_unknown_analyzer_checker) << SuppliedChecker;
+ Diags.Report(diag::err_unknown_analyzer_checker_or_package)
+ << SuppliedCheckerOrPackage;
}
}
diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
index a8af6b3d801a8..04fbd0cea46b6 100644
--- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
+++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
@@ -23,5 +23,5 @@ ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies)
std::unique_ptr<ASTConsumer>
ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) {
- return llvm::make_unique<ModelConsumer>(Bodies);
+ return std::make_unique<ModelConsumer>(Bodies);
}
diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
index fe5f59045cde0..687fda75db48e 100644
--- a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
+++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -9,6 +9,7 @@
#include "ModelInjector.h"
#include "clang/AST/Decl.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangStandard.h"
#include "clang/Basic/Stack.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -65,7 +66,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) {
auto Invocation = std::make_shared<CompilerInvocation>(CI.getInvocation());
FrontendOptions &FrontendOpts = Invocation->getFrontendOpts();
- InputKind IK = InputKind::CXX; // FIXME
+ InputKind IK = Language::CXX; // FIXME
FrontendOpts.Inputs.clear();
FrontendOpts.Inputs.emplace_back(fileName, IK);
FrontendOpts.DisableFree = true;