Skip to content

Commit d4878a6

Browse files
committed
[NFC][analyzer] Multipart checker refactor 2: NullabilityChecker
Simplify `NullabilityChecker.cpp` with new multipart checker framework introduced in 2709998. This is part of a commit series that will perform analogous changes in all checker classes that implement multiple user-facing checker parts (with separate names).
1 parent ff3341c commit d4878a6

File tree

1 file changed

+81
-76
lines changed

1 file changed

+81
-76
lines changed

clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp

Lines changed: 81 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,38 @@ class NullabilityChecker
112112
void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
113113
const char *Sep) const override;
114114

115-
enum CheckKind {
116-
CK_NullPassedToNonnull,
117-
CK_NullReturnedFromNonnull,
118-
CK_NullableDereferenced,
119-
CK_NullablePassedToNonnull,
120-
CK_NullableReturnedFromNonnull,
121-
CK_NumCheckKinds
115+
// FIXME: This enumeration of checker parts is extremely similar to the
116+
// ErrorKind enum. It would be nice to unify them to simplify the code.
117+
// FIXME: The modeling checker NullabilityBase is a dummy "empty checker
118+
// part" that registers this checker class without enabling any of the real
119+
// checker parts. As far as I understand no other checker references it, so
120+
// it should be removed.
121+
enum : CheckerPartIdx {
122+
NullabilityBase,
123+
NullPassedToNonnullChecker,
124+
NullReturnedFromNonnullChecker,
125+
NullableDereferencedChecker,
126+
NullablePassedToNonnullChecker,
127+
NullableReturnedFromNonnullChecker,
128+
NumCheckerParts
122129
};
123130

124-
bool ChecksEnabled[CK_NumCheckKinds] = {false};
125-
CheckerNameRef CheckNames[CK_NumCheckKinds];
126-
mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds];
127-
128-
const std::unique_ptr<BugType> &getBugType(CheckKind Kind) const {
129-
if (!BTs[Kind])
130-
BTs[Kind].reset(new BugType(CheckNames[Kind], "Nullability",
131-
categories::MemoryError));
132-
return BTs[Kind];
133-
}
131+
// FIXME: Currently the `Description` fields of these `BugType`s are all
132+
// identical ("Nullability") -- they should be more descriptive than this.
133+
// NOTE: NullabilityBase is a dummy checker part that does nothing, so its
134+
// bug type is left empty.
135+
BugType BugTypes[NumCheckerParts] = {
136+
{this, NullabilityBase, "", ""},
137+
{this, NullPassedToNonnullChecker, "Nullability",
138+
categories::MemoryError},
139+
{this, NullReturnedFromNonnullChecker, "Nullability",
140+
categories::MemoryError},
141+
{this, NullableDereferencedChecker, "Nullability",
142+
categories::MemoryError},
143+
{this, NullablePassedToNonnullChecker, "Nullability",
144+
categories::MemoryError},
145+
{this, NullableReturnedFromNonnullChecker, "Nullability",
146+
categories::MemoryError}};
134147

135148
// When set to false no nullability information will be tracked in
136149
// NullabilityMap. It is possible to catch errors like passing a null pointer
@@ -163,17 +176,16 @@ class NullabilityChecker
163176
///
164177
/// When \p SuppressPath is set to true, no more bugs will be reported on this
165178
/// path by this checker.
166-
void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, CheckKind CK,
167-
ExplodedNode *N, const MemRegion *Region,
168-
CheckerContext &C,
179+
void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error,
180+
CheckerPartIdx Idx, ExplodedNode *N,
181+
const MemRegion *Region, CheckerContext &C,
169182
const Stmt *ValueExpr = nullptr,
170183
bool SuppressPath = false) const;
171184

172-
void reportBug(StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
173-
const MemRegion *Region, BugReporter &BR,
185+
void reportBug(StringRef Msg, ErrorKind Error, CheckerPartIdx Idx,
186+
ExplodedNode *N, const MemRegion *Region, BugReporter &BR,
174187
const Stmt *ValueExpr = nullptr) const {
175-
const std::unique_ptr<BugType> &BT = getBugType(CK);
176-
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
188+
auto R = std::make_unique<PathSensitiveBugReport>(BugTypes[Idx], Msg, N);
177189
if (Region) {
178190
R->markInteresting(Region);
179191
R->addVisitor<NullabilityBugVisitor>(Region);
@@ -479,7 +491,7 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
479491
}
480492

481493
void NullabilityChecker::reportBugIfInvariantHolds(
482-
StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
494+
StringRef Msg, ErrorKind Error, CheckerPartIdx Idx, ExplodedNode *N,
483495
const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
484496
bool SuppressPath) const {
485497
ProgramStateRef OriginalState = N->getState();
@@ -491,7 +503,7 @@ void NullabilityChecker::reportBugIfInvariantHolds(
491503
N = C.addTransition(OriginalState, N);
492504
}
493505

494-
reportBug(Msg, Error, CK, N, Region, C.getBugReporter(), ValueExpr);
506+
reportBug(Msg, Error, Idx, N, Region, C.getBugReporter(), ValueExpr);
495507
}
496508

497509
/// Cleaning up the program state.
@@ -545,19 +557,19 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
545557
if (!TrackedNullability)
546558
return;
547559

548-
if (ChecksEnabled[CK_NullableDereferenced] &&
560+
if (isPartEnabled(NullableDereferencedChecker) &&
549561
TrackedNullability->getValue() == Nullability::Nullable) {
550562
BugReporter &BR = *Event.BR;
551563
// Do not suppress errors on defensive code paths, because dereferencing
552564
// a nullable pointer is always an error.
553565
if (Event.IsDirectDereference)
554566
reportBug("Nullable pointer is dereferenced",
555-
ErrorKind::NullableDereferenced, CK_NullableDereferenced,
567+
ErrorKind::NullableDereferenced, NullableDereferencedChecker,
556568
Event.SinkNode, Region, BR);
557569
else {
558570
reportBug("Nullable pointer is passed to a callee that requires a "
559571
"non-null",
560-
ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced,
572+
ErrorKind::NullablePassedToNonnull, NullableDereferencedChecker,
561573
Event.SinkNode, Region, BR);
562574
}
563575
}
@@ -711,7 +723,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
711723

712724
bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
713725
Nullness == NullConstraint::IsNull);
714-
if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
726+
if (isPartEnabled(NullReturnedFromNonnullChecker) &&
727+
NullReturnedFromNonNull &&
715728
RetExprTypeLevelNullability != Nullability::Nonnull &&
716729
!InSuppressedMethodFamily) {
717730
static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
@@ -725,7 +738,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
725738
OS << " returned from a " << C.getDeclDescription(D) <<
726739
" that is expected to return a non-null value";
727740
reportBugIfInvariantHolds(OS.str(), ErrorKind::NilReturnedToNonnull,
728-
CK_NullReturnedFromNonnull, N, nullptr, C,
741+
NullReturnedFromNonnullChecker, N, nullptr, C,
729742
RetExpr);
730743
return;
731744
}
@@ -746,7 +759,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
746759
State->get<NullabilityMap>(Region);
747760
if (TrackedNullability) {
748761
Nullability TrackedNullabValue = TrackedNullability->getValue();
749-
if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
762+
if (isPartEnabled(NullableReturnedFromNonnullChecker) &&
750763
Nullness != NullConstraint::IsNotNull &&
751764
TrackedNullabValue == Nullability::Nullable &&
752765
RequiredNullability == Nullability::Nonnull) {
@@ -759,7 +772,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
759772
" that is expected to return a non-null value";
760773

761774
reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull,
762-
CK_NullableReturnedFromNonnull, N, Region, C);
775+
NullableReturnedFromNonnullChecker, N, Region,
776+
C);
763777
}
764778
return;
765779
}
@@ -810,7 +824,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
810824

811825
unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
812826

813-
if (ChecksEnabled[CK_NullPassedToNonnull] &&
827+
if (isPartEnabled(NullPassedToNonnullChecker) &&
814828
Nullness == NullConstraint::IsNull &&
815829
ArgExprTypeLevelNullability != Nullability::Nonnull &&
816830
RequiredNullability == Nullability::Nonnull &&
@@ -825,7 +839,8 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
825839
OS << " passed to a callee that requires a non-null " << ParamIdx
826840
<< llvm::getOrdinalSuffix(ParamIdx) << " parameter";
827841
reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull,
828-
CK_NullPassedToNonnull, N, nullptr, C, ArgExpr,
842+
NullPassedToNonnullChecker, N, nullptr, C,
843+
ArgExpr,
829844
/*SuppressPath=*/false);
830845
return;
831846
}
@@ -842,7 +857,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
842857
TrackedNullability->getValue() != Nullability::Nullable)
843858
continue;
844859

845-
if (ChecksEnabled[CK_NullablePassedToNonnull] &&
860+
if (isPartEnabled(NullablePassedToNonnullChecker) &&
846861
RequiredNullability == Nullability::Nonnull &&
847862
isDiagnosableCall(Call)) {
848863
ExplodedNode *N = C.addTransition(State);
@@ -851,16 +866,16 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
851866
OS << "Nullable pointer is passed to a callee that requires a non-null "
852867
<< ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
853868
reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull,
854-
CK_NullablePassedToNonnull, N, Region, C,
869+
NullablePassedToNonnullChecker, N, Region, C,
855870
ArgExpr, /*SuppressPath=*/true);
856871
return;
857872
}
858-
if (ChecksEnabled[CK_NullableDereferenced] &&
873+
if (isPartEnabled(NullableDereferencedChecker) &&
859874
Param->getType()->isReferenceType()) {
860875
ExplodedNode *N = C.addTransition(State);
861876
reportBugIfInvariantHolds("Nullable pointer is dereferenced",
862877
ErrorKind::NullableDereferenced,
863-
CK_NullableDereferenced, N, Region, C,
878+
NullableDereferencedChecker, N, Region, C,
864879
ArgExpr, /*SuppressPath=*/true);
865880
return;
866881
}
@@ -1295,7 +1310,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
12951310

12961311
bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
12971312
RhsNullness == NullConstraint::IsNull);
1298-
if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
1313+
if (isPartEnabled(NullPassedToNonnullChecker) && NullAssignedToNonNull &&
12991314
ValNullability != Nullability::Nonnull &&
13001315
ValueExprTypeLevelNullability != Nullability::Nonnull &&
13011316
!isARCNilInitializedLocal(C, S)) {
@@ -1314,7 +1329,8 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
13141329
OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null");
13151330
OS << " assigned to a pointer which is expected to have non-null value";
13161331
reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull,
1317-
CK_NullPassedToNonnull, N, nullptr, C, ValueStmt);
1332+
NullPassedToNonnullChecker, N, nullptr, C,
1333+
ValueStmt);
13181334
return;
13191335
}
13201336

@@ -1340,14 +1356,15 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
13401356
if (RhsNullness == NullConstraint::IsNotNull ||
13411357
TrackedNullability->getValue() != Nullability::Nullable)
13421358
return;
1343-
if (ChecksEnabled[CK_NullablePassedToNonnull] &&
1359+
if (isPartEnabled(NullablePassedToNonnullChecker) &&
13441360
LocNullability == Nullability::Nonnull) {
13451361
static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
13461362
ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
13471363
reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer "
13481364
"which is expected to have non-null value",
13491365
ErrorKind::NullableAssignedToNonnull,
1350-
CK_NullablePassedToNonnull, N, ValueRegion, C);
1366+
NullablePassedToNonnullChecker, N, ValueRegion,
1367+
C);
13511368
}
13521369
return;
13531370
}
@@ -1394,38 +1411,26 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
13941411
}
13951412
}
13961413

1397-
void ento::registerNullabilityBase(CheckerManager &mgr) {
1398-
mgr.registerChecker<NullabilityChecker>();
1399-
}
1400-
1401-
bool ento::shouldRegisterNullabilityBase(const CheckerManager &mgr) {
1402-
return true;
1403-
}
1404-
1405-
#define REGISTER_CHECKER(name, trackingRequired) \
1406-
void ento::register##name##Checker(CheckerManager &mgr) { \
1407-
NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \
1408-
checker->ChecksEnabled[NullabilityChecker::CK_##name] = true; \
1409-
checker->CheckNames[NullabilityChecker::CK_##name] = \
1410-
mgr.getCurrentCheckerName(); \
1411-
checker->NeedTracking = checker->NeedTracking || trackingRequired; \
1412-
checker->NoDiagnoseCallsToSystemHeaders = \
1413-
checker->NoDiagnoseCallsToSystemHeaders || \
1414-
mgr.getAnalyzerOptions().getCheckerBooleanOption( \
1415-
checker, "NoDiagnoseCallsToSystemHeaders", true); \
1414+
#define REGISTER_CHECKER(Part, TrackingRequired) \
1415+
void ento::register##Part(CheckerManager &Mgr) { \
1416+
auto *Checker = \
1417+
Mgr.registerChecker<NullabilityChecker, NullabilityChecker::Part>(); \
1418+
Checker->NeedTracking = Checker->NeedTracking || TrackingRequired; \
1419+
Checker->NoDiagnoseCallsToSystemHeaders = \
1420+
Checker->NoDiagnoseCallsToSystemHeaders || \
1421+
Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
1422+
Checker, "NoDiagnoseCallsToSystemHeaders", true); \
14161423
} \
14171424
\
1418-
bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
1419-
return true; \
1420-
}
1421-
1422-
// The checks are likely to be turned on by default and it is possible to do
1423-
// them without tracking any nullability related information. As an optimization
1424-
// no nullability information will be tracked when only these two checks are
1425-
// enables.
1426-
REGISTER_CHECKER(NullPassedToNonnull, false)
1427-
REGISTER_CHECKER(NullReturnedFromNonnull, false)
1428-
1429-
REGISTER_CHECKER(NullableDereferenced, true)
1430-
REGISTER_CHECKER(NullablePassedToNonnull, true)
1431-
REGISTER_CHECKER(NullableReturnedFromNonnull, true)
1425+
bool ento::shouldRegister##Part(const CheckerManager &) { return true; }
1426+
1427+
// These checker parts are likely to be turned on by default and they don't
1428+
// need the tracking of nullability related information. As an optimization
1429+
// nullability information won't be tracked when the rest are disabled.
1430+
REGISTER_CHECKER(NullabilityBase, false)
1431+
REGISTER_CHECKER(NullPassedToNonnullChecker, false)
1432+
REGISTER_CHECKER(NullReturnedFromNonnullChecker, false)
1433+
1434+
REGISTER_CHECKER(NullableDereferencedChecker, true)
1435+
REGISTER_CHECKER(NullablePassedToNonnullChecker, true)
1436+
REGISTER_CHECKER(NullableReturnedFromNonnullChecker, true)

0 commit comments

Comments
 (0)