aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp73
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp143
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp39
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerManager.cpp11
-rw-r--r--clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp78
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp62
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp40
27 files changed, 433 insertions, 156 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index bf35bee..3ddd659 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -104,7 +104,7 @@ class RAIIMutexDescriptor {
// this function is called instead of early returning it. To avoid this, a
// bool variable (IdentifierInfoInitialized) is used and the function will
// be run only once.
- const auto &ASTCtx = Call.getState()->getStateManager().getContext();
+ const auto &ASTCtx = Call.getASTContext();
Guard = &ASTCtx.Idents.get(GuardName);
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 0ae784c..1444114 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -251,6 +251,8 @@ public:
const Expr *Ex,
const MemRegion *MR,
bool hypothetical);
+ static const StringLiteral *getStringLiteralFromRegion(const MemRegion *MR);
+
SVal getCStringLength(CheckerContext &C,
ProgramStateRef &state,
const Expr *Ex,
@@ -983,6 +985,21 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
return strLength;
}
+const StringLiteral *
+CStringChecker::getStringLiteralFromRegion(const MemRegion *MR) {
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind:
+ return cast<StringRegion>(MR)->getStringLiteral();
+ case MemRegion::NonParamVarRegionKind:
+ if (const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl();
+ Decl->getType().isConstQualified() && Decl->hasGlobalStorage())
+ return dyn_cast_or_null<StringLiteral>(Decl->getInit());
+ return nullptr;
+ default:
+ return nullptr;
+ }
+}
+
SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
const Expr *Ex, SVal Buf,
bool hypothetical) const {
@@ -1013,30 +1030,19 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
// its length. For anything we can't figure out, just return UnknownVal.
MR = MR->StripCasts();
- switch (MR->getKind()) {
- case MemRegion::StringRegionKind: {
- // Modifying the contents of string regions is undefined [C99 6.4.5p6],
- // so we can assume that the byte length is the correct C string length.
- SValBuilder &svalBuilder = C.getSValBuilder();
- QualType sizeTy = svalBuilder.getContext().getSizeType();
- const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
- return svalBuilder.makeIntVal(strLit->getLength(), sizeTy);
- }
- case MemRegion::NonParamVarRegionKind: {
+ if (const StringLiteral *StrLit = getStringLiteralFromRegion(MR)) {
// If we have a global constant with a string literal initializer,
// compute the initializer's length.
- const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl();
- if (Decl->getType().isConstQualified() && Decl->hasGlobalStorage()) {
- if (const Expr *Init = Decl->getInit()) {
- if (auto *StrLit = dyn_cast<StringLiteral>(Init)) {
- SValBuilder &SvalBuilder = C.getSValBuilder();
- QualType SizeTy = SvalBuilder.getContext().getSizeType();
- return SvalBuilder.makeIntVal(StrLit->getLength(), SizeTy);
- }
- }
- }
- [[fallthrough]];
+ // Modifying the contents of string regions is undefined [C99 6.4.5p6],
+ // so we can assume that the byte length is the correct C string length.
+ // FIXME: Embedded null characters are not handled.
+ SValBuilder &SVB = C.getSValBuilder();
+ return SVB.makeIntVal(StrLit->getLength(), SVB.getContext().getSizeType());
}
+
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind:
+ case MemRegion::NonParamVarRegionKind:
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
case MemRegion::ParamVarRegionKind:
@@ -1046,10 +1052,28 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
case MemRegion::CompoundLiteralRegionKind:
// FIXME: Can we track this? Is it necessary?
return UnknownVal();
- case MemRegion::ElementRegionKind:
- // FIXME: How can we handle this? It's not good enough to subtract the
- // offset from the base string length; consider "123\x00567" and &a[5].
+ case MemRegion::ElementRegionKind: {
+ // If an offset into the string literal is used, use the original length
+ // minus the offset.
+ // FIXME: Embedded null characters are not handled.
+ const ElementRegion *ER = cast<ElementRegion>(MR);
+ const SubRegion *SuperReg =
+ cast<SubRegion>(ER->getSuperRegion()->StripCasts());
+ const StringLiteral *StrLit = getStringLiteralFromRegion(SuperReg);
+ if (!StrLit)
+ return UnknownVal();
+ SValBuilder &SVB = C.getSValBuilder();
+ NonLoc Idx = ER->getIndex();
+ QualType SizeTy = SVB.getContext().getSizeType();
+ NonLoc LengthVal =
+ SVB.makeIntVal(StrLit->getLength(), SizeTy).castAs<NonLoc>();
+ if (state->assume(SVB.evalBinOpNN(state, BO_LE, Idx, LengthVal,
+ SVB.getConditionType())
+ .castAs<DefinedOrUnknownSVal>(),
+ true))
+ return SVB.evalBinOp(state, BO_Sub, LengthVal, Idx, SizeTy);
return UnknownVal();
+ }
default:
// Other regions (mostly non-data) can't have a reliable C string length.
// In this case, an error is emitted and UndefinedVal is returned.
@@ -1074,6 +1098,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
ProgramStateRef &state, const Expr *expr, SVal val) const {
+ // FIXME: use getStringLiteralFromRegion (and remove unused parameters)?
// Get the memory region pointed to by the val.
const MemRegion *bufRegion = val.getAsRegion();
diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index b304350..7cc146e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -246,7 +246,7 @@ public:
bool Find(const TypedValueRegion *R) {
QualType T = R->getValueType();
if (const RecordType *RT = T->getAsStructureType()) {
- const RecordDecl *RD = RT->getOriginalDecl()->getDefinition();
+ const RecordDecl *RD = RT->getDecl()->getDefinition();
assert(RD && "Referred record has no definition");
for (const auto *I : RD->fields()) {
if (I->isUnnamedBitField())
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 9d3aeff..2420848 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -929,7 +929,7 @@ ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
SVal Arg = M.getArgSVal(0);
ProgramStateRef notNilState, nilState;
std::tie(notNilState, nilState) =
- M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
+ C.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
if (!(nilState && !notNilState))
return nullptr;
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 17af1ae..5e75c1c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -154,15 +154,15 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
.Case("mkstemp", &WalkAST::checkCall_mkstemp)
.Case("mkdtemp", &WalkAST::checkCall_mkstemp)
.Case("mkstemps", &WalkAST::checkCall_mkstemp)
- .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
- .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
- .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
- "vscanf", "vwscanf", "vfscanf", "vfwscanf",
+ .Cases({"strcpy", "__strcpy_chk"}, &WalkAST::checkCall_strcpy)
+ .Cases({"strcat", "__strcat_chk"}, &WalkAST::checkCall_strcat)
+ .Cases({"sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
+ "vscanf", "vwscanf", "vfscanf", "vfwscanf"},
&WalkAST::checkDeprecatedOrUnsafeBufferHandling)
- .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
- "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
+ .Cases({"sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
+ "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove"},
&WalkAST::checkDeprecatedOrUnsafeBufferHandling)
- .Cases("strncpy", "strncat", "memset", "fprintf",
+ .Cases({"strncpy", "strncat", "memset", "fprintf"},
&WalkAST::checkDeprecatedOrUnsafeBufferHandling)
.Case("drand48", &WalkAST::checkCall_rand)
.Case("erand48", &WalkAST::checkCall_rand)
@@ -766,12 +766,14 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
int ArgIndex =
llvm::StringSwitch<int>(Name)
- .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
- .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
- "swscanf", "vsscanf", "vswscanf", 1)
- .Cases("sprintf", "vsprintf", "fprintf", 1)
- .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
- "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
+ .Cases({"scanf", "wscanf", "vscanf", "vwscanf"}, 0)
+ .Cases({"fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf",
+ "swscanf", "vsscanf", "vswscanf"},
+ 1)
+ .Cases({"sprintf", "vsprintf", "fprintf"}, 1)
+ .Cases({"swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
+ "memmove", "memset", "strncpy", "strncat"},
+ DEPR_ONLY)
.Default(UNKNOWN_CALL);
assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 395d724..37f5ec3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -19,6 +19,7 @@
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;
@@ -39,9 +40,10 @@ public:
class DereferenceChecker
: public CheckerFamily<check::Location, check::Bind,
+ check::PreStmt<BinaryOperator>,
EventDispatcher<ImplicitNullDerefEvent>> {
- void reportBug(const DerefBugType &BT, ProgramStateRef State, const Stmt *S,
- CheckerContext &C) const;
+ void reportDerefBug(const DerefBugType &BT, ProgramStateRef State,
+ const Stmt *S, CheckerContext &C) const;
bool suppressReport(CheckerContext &C, const Expr *E) const;
@@ -50,6 +52,7 @@ public:
CheckerContext &C) const;
void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,
CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *Op, CheckerContext &C) const;
static void AddDerefSource(raw_ostream &os,
SmallVectorImpl<SourceRange> &Ranges,
@@ -57,7 +60,7 @@ public:
const LocationContext *LCtx,
bool loadedFrom = false);
- CheckerFrontend NullDerefChecker, FixedDerefChecker;
+ CheckerFrontend NullDerefChecker, FixedDerefChecker, NullPointerArithmChecker;
const DerefBugType NullBug{&NullDerefChecker, "Dereference of null pointer",
"a null pointer dereference",
"a dereference of a null pointer"};
@@ -72,9 +75,22 @@ public:
const DerefBugType FixedAddressBug{&FixedDerefChecker,
"Dereference of a fixed address",
"a dereference of a fixed address"};
+ const BugType NullPointerArithmBug{
+ &NullPointerArithmChecker,
+ "Possibly undefined arithmetic operation involving a null pointer"};
StringRef getDebugTag() const override { return "DereferenceChecker"; }
};
+
+struct ValueDescStr {
+ SmallVectorImpl<SourceRange> &Ranges;
+ const Expr *Ex;
+ const ProgramState *State;
+ const LocationContext *LCtx;
+ bool IsPointer;
+ ConditionTruthVal IsNull;
+};
+
} // end anonymous namespace
void
@@ -173,9 +189,9 @@ static bool isDeclRefExprToReference(const Expr *E) {
return false;
}
-void DereferenceChecker::reportBug(const DerefBugType &BT,
- ProgramStateRef State, const Stmt *S,
- CheckerContext &C) const {
+void DereferenceChecker::reportDerefBug(const DerefBugType &BT,
+ ProgramStateRef State, const Stmt *S,
+ CheckerContext &C) const {
if (&BT == &FixedAddressBug) {
if (!FixedDerefChecker.isEnabled())
// Deliberately don't add a sink node if check is disabled.
@@ -249,9 +265,8 @@ void DereferenceChecker::reportBug(const DerefBugType &BT,
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *BR);
- for (SmallVectorImpl<SourceRange>::iterator
- I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
- BR->addRange(*I);
+ for (const auto &R : Ranges)
+ BR->addRange(R);
C.emitReport(std::move(BR));
}
@@ -262,7 +277,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
if (l.isUndef()) {
const Expr *DerefExpr = getDereferenceExpr(S);
if (!suppressReport(C, DerefExpr))
- reportBug(UndefBug, C.getState(), DerefExpr, C);
+ reportDerefBug(UndefBug, C.getState(), DerefExpr, C);
return;
}
@@ -283,7 +298,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// we call an "explicit" null dereference.
const Expr *expr = getDereferenceExpr(S);
if (!suppressReport(C, expr)) {
- reportBug(NullBug, nullState, expr, C);
+ reportDerefBug(NullBug, nullState, expr, C);
return;
}
}
@@ -301,7 +316,7 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
if (location.isConstant()) {
const Expr *DerefExpr = getDereferenceExpr(S, isLoad);
if (!suppressReport(C, DerefExpr))
- reportBug(FixedAddressBug, notNullState, DerefExpr, C);
+ reportDerefBug(FixedAddressBug, notNullState, DerefExpr, C);
return;
}
@@ -317,7 +332,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
// One should never write to label addresses.
if (auto Label = L.getAs<loc::GotoLabel>()) {
- reportBug(LabelBug, C.getState(), S, C);
+ reportDerefBug(LabelBug, C.getState(), S, C);
return;
}
@@ -338,7 +353,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (!StNonNull) {
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
if (!suppressReport(C, expr)) {
- reportBug(NullBug, StNull, expr, C);
+ reportDerefBug(NullBug, StNull, expr, C);
return;
}
}
@@ -356,7 +371,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (V.isConstant()) {
const Expr *DerefExpr = getDereferenceExpr(S, true);
if (!suppressReport(C, DerefExpr))
- reportBug(FixedAddressBug, State, DerefExpr, C);
+ reportDerefBug(FixedAddressBug, State, DerefExpr, C);
return;
}
@@ -379,6 +394,96 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
C.addTransition(State, this);
}
+namespace llvm {
+template <> struct format_provider<ValueDescStr> {
+ static void format(const ValueDescStr &V, raw_ostream &Stream,
+ StringRef Style) {
+ static const char *ValueStr[2][3] = {
+ {"zero", "nonzero integer value", "probably nonzero integer value"},
+ {"null pointer", "non-null pointer", "probably non-null pointer"},
+ };
+ Stream
+ << ValueStr[V.IsPointer][V.IsNull.isConstrainedTrue()
+ ? 0
+ : (V.IsNull.isConstrainedFalse() ? 1 : 2)];
+ DereferenceChecker::AddDerefSource(Stream, V.Ranges, V.Ex, V.State, V.LCtx,
+ false);
+ }
+};
+} // namespace llvm
+
+void DereferenceChecker::checkPreStmt(const BinaryOperator *Op,
+ CheckerContext &C) const {
+ if (!Op->isAdditiveOp() || !NullPointerArithmChecker.isEnabled())
+ return;
+ const Expr *E1 = Op->getLHS();
+ const Expr *E2 = Op->getRHS();
+ QualType T1 = E1->getType().getCanonicalType();
+ QualType T2 = E2->getType().getCanonicalType();
+ bool T1IsPointer = T1->isPointerType();
+ bool T2IsPointer = T2->isPointerType();
+ if (T1->isIntegerType() && T2->isIntegerType())
+ return;
+ if (!T1IsPointer && !T1->isIntegerType() && !T2IsPointer &&
+ !T2->isIntegerType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ ConditionTruthVal V1IsNull = State->isNull(C.getSVal(E1));
+ ConditionTruthVal V2IsNull = State->isNull(C.getSVal(E2));
+ bool IsConstrained = true;
+
+ // Check cases 'NULL + x' and 'NULL - x'
+ if (T1IsPointer && !T2IsPointer) {
+ if (!V1IsNull.isConstrainedTrue() || V2IsNull.isConstrainedTrue())
+ return;
+ IsConstrained = V2IsNull.isConstrainedFalse();
+ }
+
+ // Check case 'x + NULL'
+ if (!T1IsPointer && T2IsPointer) {
+ if (V1IsNull.isConstrainedTrue() || !V2IsNull.isConstrainedTrue())
+ return;
+ IsConstrained = V1IsNull.isConstrainedFalse();
+ }
+
+ // Check case 'NULL - p' or 'p - NULL'
+ if (T1IsPointer && T2IsPointer) {
+ if (!V1IsNull.isConstrainedTrue() && !V2IsNull.isConstrainedTrue())
+ return;
+ if (V1IsNull.isConstrainedTrue() && V2IsNull.isConstrainedTrue())
+ return;
+ IsConstrained =
+ V1IsNull.isConstrainedFalse() || V2IsNull.isConstrainedFalse();
+ }
+
+ SmallVector<SourceRange, 2> Ranges;
+ const char *OpcodeStr =
+ Op->getOpcode() == BO_Add ? "Addition" : "Subtraction";
+ const char *ResultStr = IsConstrained ? "results" : "may result";
+ ValueDescStr DerefArg1{
+ Ranges, E1, State.get(), C.getLocationContext(), T1IsPointer, V1IsNull};
+ ValueDescStr DerefArg2{
+ Ranges, E2, State.get(), C.getLocationContext(), T2IsPointer, V2IsNull};
+ std::string Msg =
+ llvm::formatv("{0} of a {1} and a {2} {3} in undefined behavior",
+ OpcodeStr, DerefArg1, DerefArg2, ResultStr);
+
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+ auto BR =
+ std::make_unique<PathSensitiveBugReport>(NullPointerArithmBug, Msg, N);
+ if (V1IsNull.isConstrainedTrue())
+ bugreporter::trackExpressionValue(N, E1, *BR);
+ if (V2IsNull.isConstrainedTrue())
+ bugreporter::trackExpressionValue(N, E2, *BR);
+ for (const auto &R : Ranges)
+ BR->addRange(R);
+
+ C.emitReport(std::move(BR));
+}
+
void ento::registerNullDereferenceChecker(CheckerManager &Mgr) {
Mgr.getChecker<DereferenceChecker>()->NullDerefChecker.enable(Mgr);
}
@@ -395,3 +500,11 @@ bool ento::shouldRegisterFixedAddressDereferenceChecker(
const CheckerManager &) {
return true;
}
+
+void ento::registerNullPointerArithmChecker(CheckerManager &Mgr) {
+ Mgr.getChecker<DereferenceChecker>()->NullPointerArithmChecker.enable(Mgr);
+}
+
+bool ento::shouldRegisterNullPointerArithmChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index b1a7cd7..bc67391 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -148,9 +148,8 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call,
QualType T = ArgE->getType();
const RecordType *UT = T->getAsUnionType();
- if (!UT || !UT->getOriginalDecl()
- ->getMostRecentDecl()
- ->hasAttr<TransparentUnionAttr>())
+ if (!UT ||
+ !UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>())
continue;
auto CSV = DV->getAs<nonloc::CompoundVal>();
diff --git a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index f984caf..227cbfa 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -34,7 +34,7 @@ class ObjCSuperDeallocChecker
this, "[super dealloc] should not be called more than once",
categories::CoreFoundationObjectiveC};
- void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
+ void initIdentifierInfoAndSelectors(const ASTContext &Ctx) const;
bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
@@ -214,8 +214,8 @@ void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE,
}
}
-void
-ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
+void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(
+ const ASTContext &Ctx) const {
if (IIdealloc)
return;
@@ -230,7 +230,7 @@ ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
return false;
- ASTContext &Ctx = M.getState()->getStateManager().getContext();
+ const ASTContext &Ctx = M.getASTContext();
initIdentifierInfoAndSelectors(Ctx);
return M.getSelector() == SELdealloc;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
index 4fc1c57..db8bbee 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdVariantChecker.cpp
@@ -211,13 +211,13 @@ private:
if (!DefaultType)
return;
- ProgramStateRef State = ConstructorCall->getState();
+ ProgramStateRef State = C.getState();
State = State->set<VariantHeldTypeMap>(ThisMemRegion, *DefaultType);
C.addTransition(State);
}
bool handleStdGetCall(const CallEvent &Call, CheckerContext &C) const {
- ProgramStateRef State = Call.getState();
+ ProgramStateRef State = C.getState();
const auto &ArgType = Call.getArgSVal(0)
.getType(C.getASTContext())
diff --git a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
index dec4612..b8fb572 100644
--- a/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/TaggedUnionModeling.h
@@ -52,7 +52,7 @@ removeInformationStoredForDeadInstances(const CallEvent &Call,
template <class TypeMap>
void handleConstructorAndAssignment(const CallEvent &Call, CheckerContext &C,
SVal ThisSVal) {
- ProgramStateRef State = Call.getState();
+ ProgramStateRef State = C.getState();
if (!State)
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 66cfccb..84adbf3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -26,6 +26,7 @@ bool tryToFindPtrOrigin(
const Expr *E, bool StopAtFirstRefCountedObj,
std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
std::function<bool(const clang::QualType)> isSafePtrType,
+ std::function<bool(const clang::Decl *)> isSafeGlobalDecl,
std::function<bool(const clang::Expr *, bool)> callback) {
while (E) {
if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
@@ -34,6 +35,8 @@ bool tryToFindPtrOrigin(
auto IsImmortal = safeGetName(VD) == "NSApp";
if (VD->hasGlobalStorage() && (IsImmortal || QT.isConstQualified()))
return callback(E, true);
+ if (VD->hasGlobalStorage() && isSafeGlobalDecl(VD))
+ return callback(E, true);
}
}
if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(E)) {
@@ -71,9 +74,11 @@ bool tryToFindPtrOrigin(
}
if (auto *Expr = dyn_cast<ConditionalOperator>(E)) {
return tryToFindPtrOrigin(Expr->getTrueExpr(), StopAtFirstRefCountedObj,
- isSafePtr, isSafePtrType, callback) &&
+ isSafePtr, isSafePtrType, isSafeGlobalDecl,
+ callback) &&
tryToFindPtrOrigin(Expr->getFalseExpr(), StopAtFirstRefCountedObj,
- isSafePtr, isSafePtrType, callback);
+ isSafePtr, isSafePtrType, isSafeGlobalDecl,
+ callback);
}
if (auto *cast = dyn_cast<CastExpr>(E)) {
if (StopAtFirstRefCountedObj) {
@@ -93,7 +98,8 @@ bool tryToFindPtrOrigin(
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *Callee = call->getCalleeDecl()) {
if (Callee->hasAttr<CFReturnsRetainedAttr>() ||
- Callee->hasAttr<NSReturnsRetainedAttr>()) {
+ Callee->hasAttr<NSReturnsRetainedAttr>() ||
+ Callee->hasAttr<NSReturnsAutoreleasedAttr>()) {
return callback(E, true);
}
}
@@ -158,13 +164,23 @@ bool tryToFindPtrOrigin(
auto Name = safeGetName(callee);
if (Name == "__builtin___CFStringMakeConstantString" ||
- Name == "NSClassFromString")
+ Name == "NSStringFromSelector" || Name == "NSSelectorFromString" ||
+ Name == "NSStringFromClass" || Name == "NSClassFromString" ||
+ Name == "NSStringFromProtocol" || Name == "NSProtocolFromString")
return callback(E, true);
} else if (auto *CalleeE = call->getCallee()) {
if (auto *E = dyn_cast<DeclRefExpr>(CalleeE->IgnoreParenCasts())) {
if (isSingleton(E->getFoundDecl()))
return callback(E, true);
}
+
+ if (auto *MemberExpr = dyn_cast<CXXDependentScopeMemberExpr>(CalleeE)) {
+ auto *Base = MemberExpr->getBase();
+ auto MemberName = MemberExpr->getMember().getAsString();
+ bool IsGetter = MemberName == "get" || MemberName == "ptr";
+ if (Base && isSafePtrType(Base->getType()) && IsGetter)
+ return callback(E, true);
+ }
}
// Sometimes, canonical type erroneously turns Ref<T> into T.
@@ -176,7 +192,7 @@ bool tryToFindPtrOrigin(
if (auto *Subst = dyn_cast<SubstTemplateTypeParmType>(RetType)) {
if (auto *SubstType = Subst->desugar().getTypePtr()) {
if (auto *RD = dyn_cast<RecordType>(SubstType)) {
- if (auto *CXX = dyn_cast<CXXRecordDecl>(RD->getOriginalDecl()))
+ if (auto *CXX = dyn_cast<CXXRecordDecl>(RD->getDecl()))
if (isSafePtr(CXX))
return callback(E, true);
}
@@ -196,6 +212,8 @@ bool tryToFindPtrOrigin(
!Selector.getNumArgs())
return callback(E, true);
}
+ if (auto *ObjCProtocol = dyn_cast<ObjCProtocolExpr>(E))
+ return callback(ObjCProtocol, true);
if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(E))
return callback(ObjCDict, true);
if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(E))
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index 3a009d6..9fff456 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -56,6 +56,7 @@ bool tryToFindPtrOrigin(
const clang::Expr *E, bool StopAtFirstRefCountedObj,
std::function<bool(const clang::CXXRecordDecl *)> isSafePtr,
std::function<bool(const clang::QualType)> isSafePtrType,
+ std::function<bool(const clang::Decl *)> isSafeGlobalDecl,
std::function<bool(const clang::Expr *, bool)> callback);
/// For \p E referring to a ref-countable/-counted pointer/reference we return
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
index d8539ea..1d4e6dd 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
@@ -263,18 +263,43 @@ public:
void visitCallArg(const Expr *Arg, const ParmVarDecl *Param,
const Decl *DeclWithIssue) const {
auto *ArgExpr = Arg->IgnoreParenCasts();
- if (auto *InnerCE = dyn_cast<CallExpr>(Arg)) {
- auto *InnerCallee = InnerCE->getDirectCallee();
- if (InnerCallee && InnerCallee->isInStdNamespace() &&
- safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
- ArgExpr = InnerCE->getArg(0);
- if (ArgExpr)
- ArgExpr = ArgExpr->IgnoreParenCasts();
+ while (ArgExpr) {
+ ArgExpr = ArgExpr->IgnoreParenCasts();
+ if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {
+ auto *InnerCallee = InnerCE->getDirectCallee();
+ if (InnerCallee && InnerCallee->isInStdNamespace() &&
+ safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
+ ArgExpr = InnerCE->getArg(0);
+ continue;
+ }
+ }
+ if (auto *UO = dyn_cast<UnaryOperator>(ArgExpr)) {
+ auto OpCode = UO->getOpcode();
+ if (OpCode == UO_Deref || OpCode == UO_AddrOf) {
+ ArgExpr = UO->getSubExpr();
+ continue;
+ }
}
+ break;
}
+
+ if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(ArgExpr)) {
+ if (isOwnerPtrType(MemberCallExpr->getObjectType()))
+ return;
+ }
+
+ if (auto *OpCE = dyn_cast<CXXOperatorCallExpr>(ArgExpr)) {
+ auto *Method = dyn_cast_or_null<CXXMethodDecl>(OpCE->getDirectCallee());
+ if (Method && isOwnerPtr(safeGetName(Method->getParent()))) {
+ if (OpCE->getOperator() == OO_Star && OpCE->getNumArgs() == 1)
+ return;
+ }
+ }
+
if (isNullPtr(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
isa<CXXDefaultArgExpr>(ArgExpr))
return;
+
if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
if (auto *ValDecl = DRE->getDecl()) {
if (isa<ParmVarDecl>(ValDecl))
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index e5c74bb..d3d1f13 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -138,6 +138,11 @@ bool isCheckedPtr(const std::string &Name) {
return Name == "CheckedPtr" || Name == "CheckedRef";
}
+bool isOwnerPtr(const std::string &Name) {
+ return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" ||
+ Name == "UniqueRef" || Name == "LazyUniqueRef";
+}
+
bool isSmartPtrClass(const std::string &Name) {
return isRefType(Name) || isCheckedPtr(Name) || isRetainPtrOrOSPtr(Name) ||
Name == "WeakPtr" || Name == "WeakPtrFactory" ||
@@ -206,10 +211,7 @@ bool isRetainPtrOrOSPtrType(const clang::QualType T) {
}
bool isOwnerPtrType(const clang::QualType T) {
- return isPtrOfType(T, [](auto Name) {
- return isRefType(Name) || isCheckedPtr(Name) || Name == "unique_ptr" ||
- Name == "UniqueRef" || Name == "LazyUniqueRef";
- });
+ return isPtrOfType(T, [](auto Name) { return isOwnerPtr(Name); });
}
std::optional<bool> isUncounted(const QualType T) {
@@ -255,7 +257,7 @@ void RetainTypeChecker::visitTypedef(const TypedefDecl *TD) {
return;
}
- for (auto *Redecl : RT->getOriginalDecl()->getMostRecentDecl()->redecls()) {
+ for (auto *Redecl : RT->getDecl()->getMostRecentDecl()->redecls()) {
if (Redecl->getAttr<ObjCBridgeAttr>() ||
Redecl->getAttr<ObjCBridgeMutableAttr>()) {
CFPointees.insert(RT);
@@ -296,7 +298,7 @@ std::optional<bool> isUnretained(const QualType T, bool IsARCEnabled) {
auto *Record = PointeeType->getAsStructureType();
if (!Record)
return false;
- auto *Decl = Record->getOriginalDecl();
+ auto *Decl = Record->getDecl();
if (!Decl)
return false;
auto TypeName = Decl->getName();
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 8300a6c..12e2e2d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -143,6 +143,10 @@ bool isCheckedPtr(const std::string &Name);
/// \returns true if \p Name is RetainPtr or its variant, false if not.
bool isRetainPtrOrOSPtr(const std::string &Name);
+/// \returns true if \p Name is an owning smar pointer such as Ref, CheckedPtr,
+/// and unique_ptr.
+bool isOwnerPtr(const std::string &Name);
+
/// \returns true if \p Name is a smart pointer type name, false if not.
bool isSmartPtrClass(const std::string &Name);
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 9585ceb..791e709 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -29,12 +29,12 @@ namespace {
class RawPtrRefCallArgsChecker
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
BugType Bug;
- mutable BugReporter *BR;
TrivialFunctionAnalysis TFA;
EnsureFunctionAnalysis EFA;
protected:
+ mutable BugReporter *BR;
mutable std::optional<RetainTypeChecker> RTC;
public:
@@ -46,6 +46,7 @@ public:
virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0;
virtual bool isSafePtrType(const QualType type) const = 0;
virtual bool isSafeExpr(const Expr *) const { return false; }
+ virtual bool isSafeDecl(const Decl *) const { return false; }
virtual const char *ptrKind() const = 0;
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -214,6 +215,7 @@ public:
Arg, /*StopAtFirstRefCountedObj=*/true,
[&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); },
[&](const clang::QualType T) { return isSafePtrType(T); },
+ [&](const clang::Decl *D) { return isSafeDecl(D); },
[&](const clang::Expr *ArgOrigin, bool IsSafe) {
if (IsSafe)
return true;
@@ -479,6 +481,11 @@ public:
isa<ObjCMessageExpr>(E);
}
+ bool isSafeDecl(const Decl *D) const final {
+ // Treat NS/CF globals in system header as immortal.
+ return BR->getSourceManager().isInSystemHeader(D->getLocation());
+ }
+
const char *ptrKind() const final { return "unretained"; }
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index dd9701f..c13df479 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -166,10 +166,10 @@ bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
class RawPtrRefLocalVarsChecker
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
BugType Bug;
- mutable BugReporter *BR;
EnsureFunctionAnalysis EFA;
protected:
+ mutable BugReporter *BR;
mutable std::optional<RetainTypeChecker> RTC;
public:
@@ -180,6 +180,7 @@ public:
virtual bool isSafePtr(const CXXRecordDecl *) const = 0;
virtual bool isSafePtrType(const QualType) const = 0;
virtual bool isSafeExpr(const Expr *) const { return false; }
+ virtual bool isSafeDecl(const Decl *) const { return false; }
virtual const char *ptrKind() const = 0;
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
@@ -288,6 +289,7 @@ public:
return isSafePtr(Record);
},
[&](const clang::QualType Type) { return isSafePtrType(Type); },
+ [&](const clang::Decl *D) { return isSafeDecl(D); },
[&](const clang::Expr *InitArgOrigin, bool IsSafe) {
if (!InitArgOrigin || IsSafe)
return true;
@@ -443,6 +445,10 @@ public:
return ento::cocoa::isCocoaObjectRef(E->getType()) &&
isa<ObjCMessageExpr>(E);
}
+ bool isSafeDecl(const Decl *D) const final {
+ // Treat NS/CF globals in system header as immortal.
+ return BR->getSourceManager().isInSystemHeader(D->getLocation());
+ }
const char *ptrKind() const final { return "unretained"; }
};
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 6f3a280..c6421f8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -121,13 +121,13 @@ public:
return true;
}
} else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
- if (declaresSameEntity(RD->getOriginalDecl(), ClassDecl))
+ if (declaresSameEntity(RD->getDecl(), ClassDecl))
return true;
} else if (auto *ST =
dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
auto Type = ST->getReplacementType();
if (auto *RD = dyn_cast<RecordType>(Type)) {
- if (declaresSameEntity(RD->getOriginalDecl(), ClassDecl))
+ if (declaresSameEntity(RD->getDecl(), ClassDecl))
return true;
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 02f34bc..c905ee6 100644
--- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -173,7 +173,7 @@ const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
return D;
}
-LLVM_ATTRIBUTE_UNUSED static bool hasNoRepeatedElements(
+[[maybe_unused]] static bool hasNoRepeatedElements(
llvm::ImmutableList<const CXXBaseSpecifier *> BaseSpecList) {
llvm::SmallPtrSet<QualType, 16> BaseSpecSeen;
for (const CXXBaseSpecifier *BaseSpec : BaseSpecList) {
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 06ba015..62460cc 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -89,7 +89,7 @@ static bool isCallback(QualType T) {
T = T->getPointeeType();
if (const RecordType *RT = T->getAsStructureType()) {
- const RecordDecl *RD = RT->getOriginalDecl()->getDefinitionOrSelf();
+ const RecordDecl *RD = RT->getDecl()->getDefinitionOrSelf();
for (const auto *I : RD->fields()) {
QualType FieldT = I->getType();
if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
@@ -391,9 +391,8 @@ bool CallEvent::isVariadic(const Decl *D) {
static bool isTransparentUnion(QualType T) {
const RecordType *UT = T->getAsUnionType();
- return UT && UT->getOriginalDecl()
- ->getMostRecentDecl()
- ->hasAttr<TransparentUnionAttr>();
+ return UT &&
+ UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>();
}
// In some cases, symbolic cases should be transformed before we associate
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 44c6f9f..8ee4832 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -731,19 +731,22 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
ExplodedNodeSet checkDst;
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<> UpdatedCall = Call.cloneWithState(State);
+
// Check if any of the EvalCall callbacks can evaluate the call.
for (const auto &EvalCallChecker : EvalCallCheckers) {
// TODO: Support the situation when the call doesn't correspond
// to any Expr.
ProgramPoint L = ProgramPoint::getProgramPoint(
- Call.getOriginExpr(), ProgramPoint::PostStmtKind,
+ UpdatedCall->getOriginExpr(), ProgramPoint::PostStmtKind,
Pred->getLocationContext(), EvalCallChecker.Checker);
bool evaluated = false;
- { // CheckerContext generates transitions(populates checkDest) on
+ { // CheckerContext generates transitions (populates checkDest) on
// destruction, so introduce the scope to make sure it gets properly
// populated.
CheckerContext C(B, Eng, Pred, L);
- evaluated = EvalCallChecker(Call, C);
+ evaluated = EvalCallChecker(*UpdatedCall, C);
}
#ifndef NDEBUG
if (evaluated && evaluatorChecker) {
@@ -774,7 +777,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
if (!evaluatorChecker) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
- Eng.defaultEvalCall(B, Pred, Call, CallOpts);
+ Eng.defaultEvalCall(B, Pred, *UpdatedCall, CallOpts);
}
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
index abfb176..c207a7b 100644
--- a/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
+++ b/clang/lib/StaticAnalyzer/Core/EntryPointStats.cpp
@@ -24,15 +24,21 @@ using namespace ento;
namespace {
struct Registry {
+ std::vector<UnsignedEPStat *> ExplicitlySetStats;
+ std::vector<UnsignedMaxEPStat *> MaxStats;
std::vector<CounterEPStat *> CounterStats;
- std::vector<UnsignedMaxEPStat *> UnsignedMaxStats;
- std::vector<UnsignedEPStat *> UnsignedStats;
bool IsLocked = false;
struct Snapshot {
const Decl *EntryPoint;
- std::vector<unsigned> UnsignedStatValues;
+ // Explicitly set statistics may not have a value set, so they are separate
+ // from other unsigned statistics
+ std::vector<std::optional<unsigned>> ExplicitlySetStatValues;
+ // These are counting and maximizing statistics that initialize to 0, which
+ // is meaningful even if they are never updated, so their value is always
+ // present.
+ std::vector<unsigned> MaxOrCountStatValues;
void dumpAsCSV(llvm::raw_ostream &OS) const;
};
@@ -46,10 +52,16 @@ static llvm::ManagedStatic<Registry> StatsRegistry;
namespace {
template <typename Callback> void enumerateStatVectors(const Callback &Fn) {
+ // This order is important, it matches the order of the Snapshot fields:
+ // - ExplicitlySetStatValues
+ Fn(StatsRegistry->ExplicitlySetStats);
+ // - MaxOrCountStatValues
+ Fn(StatsRegistry->MaxStats);
Fn(StatsRegistry->CounterStats);
- Fn(StatsRegistry->UnsignedMaxStats);
- Fn(StatsRegistry->UnsignedStats);
}
+
+void clearSnapshots(void *) { StatsRegistry->Snapshots.clear(); }
+
} // namespace
static void checkStatName(const EntryPointStat *M) {
@@ -69,7 +81,8 @@ static void checkStatName(const EntryPointStat *M) {
}
}
-void EntryPointStat::lockRegistry(llvm::StringRef CPPFileName) {
+void EntryPointStat::lockRegistry(llvm::StringRef CPPFileName,
+ ASTContext &Ctx) {
auto CmpByNames = [](const EntryPointStat *L, const EntryPointStat *R) {
return L->name() < R->name();
};
@@ -80,6 +93,10 @@ void EntryPointStat::lockRegistry(llvm::StringRef CPPFileName) {
StatsRegistry->IsLocked = true;
llvm::raw_string_ostream OS(StatsRegistry->EscapedCPPFileName);
llvm::printEscapedString(CPPFileName, OS);
+ // Make sure snapshots (that reference function Decl's) do not persist after
+ // the AST is destroyed. This is especially relevant in the context of unit
+ // tests that construct and destruct multiple ASTs in the same process.
+ Ctx.AddDeallocation(clearSnapshots, nullptr);
}
[[maybe_unused]] static bool isRegistered(llvm::StringLiteral Name) {
@@ -101,30 +118,36 @@ UnsignedMaxEPStat::UnsignedMaxEPStat(llvm::StringLiteral Name)
: EntryPointStat(Name) {
assert(!StatsRegistry->IsLocked);
assert(!isRegistered(Name));
- StatsRegistry->UnsignedMaxStats.push_back(this);
+ StatsRegistry->MaxStats.push_back(this);
}
UnsignedEPStat::UnsignedEPStat(llvm::StringLiteral Name)
: EntryPointStat(Name) {
assert(!StatsRegistry->IsLocked);
assert(!isRegistered(Name));
- StatsRegistry->UnsignedStats.push_back(this);
+ StatsRegistry->ExplicitlySetStats.push_back(this);
}
-static std::vector<unsigned> consumeUnsignedStats() {
- std::vector<unsigned> Result;
- Result.reserve(StatsRegistry->CounterStats.size() +
- StatsRegistry->UnsignedMaxStats.size() +
- StatsRegistry->UnsignedStats.size());
- for (auto *M : StatsRegistry->CounterStats) {
+static std::vector<std::optional<unsigned>> consumeExplicitlySetStats() {
+ std::vector<std::optional<unsigned>> Result;
+ Result.reserve(StatsRegistry->ExplicitlySetStats.size());
+ for (auto *M : StatsRegistry->ExplicitlySetStats) {
Result.push_back(M->value());
M->reset();
}
- for (auto *M : StatsRegistry->UnsignedMaxStats) {
+ return Result;
+}
+
+static std::vector<unsigned> consumeMaxAndCounterStats() {
+ std::vector<unsigned> Result;
+ Result.reserve(StatsRegistry->CounterStats.size() +
+ StatsRegistry->MaxStats.size());
+ // Order is important, it must match the order in enumerateStatVectors
+ for (auto *M : StatsRegistry->MaxStats) {
Result.push_back(M->value());
M->reset();
}
- for (auto *M : StatsRegistry->UnsignedStats) {
+ for (auto *M : StatsRegistry->CounterStats) {
Result.push_back(M->value());
M->reset();
}
@@ -150,20 +173,33 @@ static std::string getUSR(const Decl *D) {
}
void Registry::Snapshot::dumpAsCSV(llvm::raw_ostream &OS) const {
+ auto PrintAsUnsignOpt = [&OS](std::optional<unsigned> U) {
+ OS << (U.has_value() ? std::to_string(*U) : "");
+ };
+ auto CommaIfNeeded = [&OS](const auto &Vec1, const auto &Vec2) {
+ if (!Vec1.empty() && !Vec2.empty())
+ OS << ",";
+ };
+ auto PrintAsUnsigned = [&OS](unsigned U) { OS << U; };
+
OS << '"';
llvm::printEscapedString(getUSR(EntryPoint), OS);
OS << "\",\"";
OS << StatsRegistry->EscapedCPPFileName << "\",\"";
llvm::printEscapedString(
clang::AnalysisDeclContext::getFunctionName(EntryPoint), OS);
- OS << "\"";
- OS << (UnsignedStatValues.empty() ? "" : ",");
- llvm::interleave(UnsignedStatValues, OS, [&OS](unsigned U) { OS << U; }, ",");
+ OS << "\",";
+ llvm::interleave(ExplicitlySetStatValues, OS, PrintAsUnsignOpt, ",");
+ CommaIfNeeded(ExplicitlySetStatValues, MaxOrCountStatValues);
+ llvm::interleave(MaxOrCountStatValues, OS, PrintAsUnsigned, ",");
}
void EntryPointStat::takeSnapshot(const Decl *EntryPoint) {
- auto UnsignedValues = consumeUnsignedStats();
- StatsRegistry->Snapshots.push_back({EntryPoint, std::move(UnsignedValues)});
+ auto ExplicitlySetValues = consumeExplicitlySetStats();
+ auto MaxOrCounterValues = consumeMaxAndCounterStats();
+ StatsRegistry->Snapshots.push_back({EntryPoint,
+ std::move(ExplicitlySetValues),
+ std::move(MaxOrCounterValues)});
}
void EntryPointStat::dumpStatsAsCSV(llvm::StringRef FileName) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 0c491b8..ac6c1d7 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -628,6 +628,8 @@ void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State,
const CallEvent &Call) {
+ // WARNING: The state attached to 'Call' may be obsolete, do not call any
+ // methods that rely on it!
const Expr *E = Call.getOriginExpr();
// FIXME: Constructors to placement arguments of operator new
// are not supported yet.
@@ -653,6 +655,8 @@ ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State,
void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst,
ExplodedNode *Pred,
const CallEvent &Call) {
+ // WARNING: The state attached to 'Call' may be obsolete, do not call any
+ // methods that rely on it!
ProgramStateRef State = Pred->getState();
ProgramStateRef CleanedState = finishArgumentConstruction(State, Call);
if (CleanedState == State) {
@@ -670,35 +674,33 @@ void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst,
}
void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
- const CallEvent &Call) {
- // WARNING: At this time, the state attached to 'Call' may be older than the
- // state in 'Pred'. This is a minor optimization since CheckerManager will
- // use an updated CallEvent instance when calling checkers, but if 'Call' is
- // ever used directly in this function all callers should be updated to pass
- // the most recent state. (It is probably not worth doing the work here since
- // for some callers this will not be necessary.)
+ const CallEvent &CallTemplate) {
+ // NOTE: CallTemplate is called a "template" because its attached state may
+ // be obsolete (compared to the state of Pred). The state-dependent methods
+ // of CallEvent should be used only after a `cloneWithState` call that
+ // attaches the up-to-date state to this template object.
// Run any pre-call checks using the generic call interface.
ExplodedNodeSet dstPreVisit;
- getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred,
- Call, *this);
+ getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, CallTemplate,
+ *this);
// Actually evaluate the function call. We try each of the checkers
// to see if the can evaluate the function call, and get a callback at
// defaultEvalCall if all of them fail.
ExplodedNodeSet dstCallEvaluated;
- getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
- Call, *this, EvalCallOptions());
+ getCheckerManager().runCheckersForEvalCall(
+ dstCallEvaluated, dstPreVisit, CallTemplate, *this, EvalCallOptions());
// If there were other constructors called for object-type arguments
// of this call, clean them up.
ExplodedNodeSet dstArgumentCleanup;
for (ExplodedNode *I : dstCallEvaluated)
- finishArgumentConstruction(dstArgumentCleanup, I, Call);
+ finishArgumentConstruction(dstArgumentCleanup, I, CallTemplate);
ExplodedNodeSet dstPostCall;
getCheckerManager().runCheckersForPostCall(dstPostCall, dstArgumentCleanup,
- Call, *this);
+ CallTemplate, *this);
// Escaping symbols conjured during invalidating the regions above.
// Note that, for inlined calls the nodes were put back into the worklist,
@@ -708,12 +710,13 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
// Run pointerEscape callback with the newly conjured symbols.
SmallVector<std::pair<SVal, SVal>, 8> Escaped;
for (ExplodedNode *I : dstPostCall) {
- NodeBuilder B(I, Dst, *currBldrCtx);
ProgramStateRef State = I->getState();
+ CallEventRef<> Call = CallTemplate.cloneWithState(State);
+ NodeBuilder B(I, Dst, *currBldrCtx);
Escaped.clear();
{
unsigned Arg = -1;
- for (const ParmVarDecl *PVD : Call.parameters()) {
+ for (const ParmVarDecl *PVD : Call->parameters()) {
++Arg;
QualType ParamTy = PVD->getType();
if (ParamTy.isNull() ||
@@ -722,13 +725,13 @@ void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
QualType Pointee = ParamTy->getPointeeType();
if (Pointee.isConstQualified() || Pointee->isVoidType())
continue;
- if (const MemRegion *MR = Call.getArgSVal(Arg).getAsRegion())
+ if (const MemRegion *MR = Call->getArgSVal(Arg).getAsRegion())
Escaped.emplace_back(loc::MemRegionVal(MR), State->getSVal(MR, Pointee));
}
}
State = processPointerEscapedOnBind(State, Escaped, I->getLocationContext(),
- PSK_EscapeOutParameters, &Call);
+ PSK_EscapeOutParameters, &*Call);
if (State == I->getState())
Dst.insert(I);
@@ -1212,48 +1215,47 @@ static bool isTrivialObjectAssignment(const CallEvent &Call) {
}
void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
- const CallEvent &CallTemplate,
+ const CallEvent &Call,
const EvalCallOptions &CallOpts) {
// Make sure we have the most recent state attached to the call.
ProgramStateRef State = Pred->getState();
- CallEventRef<> Call = CallTemplate.cloneWithState(State);
// Special-case trivial assignment operators.
- if (isTrivialObjectAssignment(*Call)) {
- performTrivialCopy(Bldr, Pred, *Call);
+ if (isTrivialObjectAssignment(Call)) {
+ performTrivialCopy(Bldr, Pred, Call);
return;
}
// Try to inline the call.
// The origin expression here is just used as a kind of checksum;
// this should still be safe even for CallEvents that don't come from exprs.
- const Expr *E = Call->getOriginExpr();
+ const Expr *E = Call.getOriginExpr();
ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
if (InlinedFailedState) {
// If we already tried once and failed, make sure we don't retry later.
State = InlinedFailedState;
} else {
- RuntimeDefinition RD = Call->getRuntimeDefinition();
- Call->setForeign(RD.isForeign());
+ RuntimeDefinition RD = Call.getRuntimeDefinition();
+ Call.setForeign(RD.isForeign());
const Decl *D = RD.getDecl();
- if (shouldInlineCall(*Call, D, Pred, CallOpts)) {
+ if (shouldInlineCall(Call, D, Pred, CallOpts)) {
if (RD.mayHaveOtherDefinitions()) {
AnalyzerOptions &Options = getAnalysisManager().options;
// Explore with and without inlining the call.
if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) {
- BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred);
+ BifurcateCall(RD.getDispatchRegion(), Call, D, Bldr, Pred);
return;
}
// Don't inline if we're not in any dynamic dispatch mode.
if (Options.getIPAMode() != IPAK_DynamicDispatch) {
- conservativeEvalCall(*Call, Bldr, Pred, State);
+ conservativeEvalCall(Call, Bldr, Pred, State);
return;
}
}
- ctuBifurcate(*Call, D, Bldr, Pred, State);
+ ctuBifurcate(Call, D, Bldr, Pred, State);
return;
}
}
@@ -1261,10 +1263,10 @@ void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
// If we can't inline it, clean up the state traits used only if the function
// is inlined.
State = removeStateTraitsUsedForArrayEvaluation(
- State, dyn_cast_or_null<CXXConstructExpr>(E), Call->getLocationContext());
+ State, dyn_cast_or_null<CXXConstructExpr>(E), Call.getLocationContext());
// Also handle the return value and invalidate the regions.
- conservativeEvalCall(*Call, Bldr, Pred, State);
+ conservativeEvalCall(Call, Bldr, Pred, State);
}
void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index ab45e67..245a730 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -983,7 +983,7 @@ public:
}
/// Check equivalence data for consistency.
- [[nodiscard]] LLVM_ATTRIBUTE_UNUSED static bool
+ [[nodiscard]] [[maybe_unused]] static bool
isClassDataConsistent(ProgramStateRef State);
[[nodiscard]] QualType getType() const {
@@ -1041,8 +1041,7 @@ private:
// Constraint functions
//===----------------------------------------------------------------------===//
-[[nodiscard]] LLVM_ATTRIBUTE_UNUSED bool
-areFeasible(ConstraintRangeTy Constraints) {
+[[nodiscard]] [[maybe_unused]] bool areFeasible(ConstraintRangeTy Constraints) {
return llvm::none_of(
Constraints,
[](const std::pair<EquivalenceClass, RangeSet> &ClassConstraint) {
@@ -1134,7 +1133,7 @@ template <class EndTy>
return End;
}
-[[nodiscard]] LLVM_ATTRIBUTE_UNUSED inline std::optional<RangeSet>
+[[nodiscard]] [[maybe_unused]] inline std::optional<RangeSet>
intersect(RangeSet::Factory &F, const RangeSet *End) {
// This is an extraneous conversion from a raw pointer into
// std::optional<RangeSet>
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index af0ef52..2838533 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2457,7 +2457,7 @@ NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B,
SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B,
const TypedValueRegion *R) {
const RecordDecl *RD =
- R->getValueType()->castAsCanonical<RecordType>()->getOriginalDecl();
+ R->getValueType()->castAsCanonical<RecordType>()->getDecl();
if (!RD->getDefinition())
return UnknownVal();
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 84a9c43..6108931 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1111,6 +1111,10 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
assert(!BinaryOperator::isComparisonOp(op) &&
"arguments to comparison ops must be of the same type");
+ SVal simplifiedRhs = simplifySVal(state, rhs);
+ if (auto simplifiedRhsAsNonLoc = simplifiedRhs.getAs<NonLoc>())
+ rhs = *simplifiedRhsAsNonLoc;
+
// Special case: rhs is a zero constant.
if (rhs.isZeroConstant())
return lhs;
diff --git a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index cf01e2f..f6a3e79 100644
--- a/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -39,6 +39,7 @@
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
+#include <cmath>
#include <memory>
#include <utility>
@@ -61,7 +62,9 @@ ALWAYS_ENABLED_STATISTIC(
"The # of visited basic blocks in the analyzed functions.");
ALWAYS_ENABLED_STATISTIC(PercentReachableBlocks,
"The % of reachable basic blocks.");
-STAT_MAX(MaxCFGSize, "The maximum number of basic blocks in a function.");
+ALWAYS_ENABLED_STATISTIC(MaxCFGSize,
+ "The maximum number of basic blocks in a function.");
+static UnsignedEPStat CFGSize("CFGSize");
//===----------------------------------------------------------------------===//
// AnalysisConsumer declaration.
//===----------------------------------------------------------------------===//
@@ -125,6 +128,7 @@ public:
std::unique_ptr<llvm::Timer> SyntaxCheckTimer;
std::unique_ptr<llvm::Timer> ExprEngineTimer;
std::unique_ptr<llvm::Timer> BugReporterTimer;
+ bool ShouldClearTimersToPreventDisplayingThem;
/// The information about analyzed functions shared throughout the
/// translation unit.
@@ -138,11 +142,12 @@ public:
Injector(std::move(injector)), CTU(CI),
MacroExpansions(CI.getLangOpts()) {
- EntryPointStat::lockRegistry(getMainFileName(CI.getInvocation()));
+ EntryPointStat::lockRegistry(getMainFileName(CI.getInvocation()),
+ CI.getASTContext());
DigestAnalyzerOptions();
if (Opts.AnalyzerDisplayProgress || Opts.PrintStats ||
- Opts.ShouldSerializeStats) {
+ Opts.ShouldSerializeStats || !Opts.DumpEntryPointStatsToCSV.empty()) {
AnalyzerTimers = std::make_unique<llvm::TimerGroup>(
"analyzer", "Analyzer timers");
SyntaxCheckTimer = std::make_unique<llvm::Timer>(
@@ -154,6 +159,12 @@ public:
*AnalyzerTimers);
}
+ // Avoid displaying the timers created above in case we only want to record
+ // per-entry-point stats.
+ ShouldClearTimersToPreventDisplayingThem = !Opts.AnalyzerDisplayProgress &&
+ !Opts.PrintStats &&
+ !Opts.ShouldSerializeStats;
+
if (Opts.PrintStats || Opts.ShouldSerializeStats) {
llvm::EnableStatistics(/* DoPrintOnExit= */ false);
}
@@ -276,6 +287,9 @@ public:
checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
if (SyntaxCheckTimer)
SyntaxCheckTimer->stopTimer();
+ if (AnalyzerTimers && ShouldClearTimersToPreventDisplayingThem) {
+ AnalyzerTimers->clear();
+ }
}
return true;
}
@@ -569,6 +583,9 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
if (SyntaxCheckTimer)
SyntaxCheckTimer->stopTimer();
+ if (AnalyzerTimers && ShouldClearTimersToPreventDisplayingThem) {
+ AnalyzerTimers->clear();
+ }
// Run the AST-only checks using the order in which functions are defined.
// If inlining is not turned on, use the simplest function order for path
@@ -745,6 +762,9 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
llvm::TimeRecord CheckerEndTime = SyntaxCheckTimer->getTotalTime();
CheckerEndTime -= CheckerStartTime;
DisplayTime(CheckerEndTime);
+ if (AnalyzerTimers && ShouldClearTimersToPreventDisplayingThem) {
+ AnalyzerTimers->clear();
+ }
}
}
@@ -765,15 +785,19 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
ExprEngine::InliningModes IMode,
SetOfConstDecls *VisitedCallees) {
+ auto *CFG = Mgr->getCFG(D);
+
// Construct the analysis engine. First check if the CFG is valid.
// FIXME: Inter-procedural analysis will need to handle invalid CFGs.
- if (!Mgr->getCFG(D))
+ if (!CFG)
return;
// See if the LiveVariables analysis scales.
if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
return;
+ CFGSize.set(CFG->size());
+
ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode);
// Execute the worklist algorithm.
@@ -788,7 +812,12 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
ExprEngineTimer->stopTimer();
llvm::TimeRecord ExprEngineEndTime = ExprEngineTimer->getTotalTime();
ExprEngineEndTime -= ExprEngineStartTime;
+ PathRunningTime.set(static_cast<unsigned>(
+ std::lround(ExprEngineEndTime.getWallTime() * 1000)));
DisplayTime(ExprEngineEndTime);
+ if (AnalyzerTimers && ShouldClearTimersToPreventDisplayingThem) {
+ AnalyzerTimers->clear();
+ }
}
if (!Mgr->options.DumpExplodedGraphTo.empty())
@@ -799,6 +828,9 @@ void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
Eng.ViewGraph(Mgr->options.TrimGraph);
flushReports(BugReporterTimer.get(), Eng.getBugReporter());
+ if (AnalyzerTimers && ShouldClearTimersToPreventDisplayingThem) {
+ AnalyzerTimers->clear();
+ }
}
//===----------------------------------------------------------------------===//