@@ -112,25 +112,38 @@ class NullabilityChecker
112
112
void printState (raw_ostream &Out, ProgramStateRef State, const char *NL,
113
113
const char *Sep) const override ;
114
114
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
122
129
};
123
130
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}};
134
147
135
148
// When set to false no nullability information will be tracked in
136
149
// NullabilityMap. It is possible to catch errors like passing a null pointer
@@ -163,17 +176,16 @@ class NullabilityChecker
163
176
// /
164
177
// / When \p SuppressPath is set to true, no more bugs will be reported on this
165
178
// / 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,
169
182
const Stmt *ValueExpr = nullptr ,
170
183
bool SuppressPath = false ) const ;
171
184
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,
174
187
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);
177
189
if (Region) {
178
190
R->markInteresting (Region);
179
191
R->addVisitor <NullabilityBugVisitor>(Region);
@@ -479,7 +491,7 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
479
491
}
480
492
481
493
void NullabilityChecker::reportBugIfInvariantHolds (
482
- StringRef Msg, ErrorKind Error, CheckKind CK , ExplodedNode *N,
494
+ StringRef Msg, ErrorKind Error, CheckerPartIdx Idx , ExplodedNode *N,
483
495
const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
484
496
bool SuppressPath) const {
485
497
ProgramStateRef OriginalState = N->getState ();
@@ -491,7 +503,7 @@ void NullabilityChecker::reportBugIfInvariantHolds(
491
503
N = C.addTransition (OriginalState, N);
492
504
}
493
505
494
- reportBug (Msg, Error, CK , N, Region, C.getBugReporter (), ValueExpr);
506
+ reportBug (Msg, Error, Idx , N, Region, C.getBugReporter (), ValueExpr);
495
507
}
496
508
497
509
// / Cleaning up the program state.
@@ -545,19 +557,19 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
545
557
if (!TrackedNullability)
546
558
return ;
547
559
548
- if (ChecksEnabled[CK_NullableDereferenced] &&
560
+ if (isPartEnabled (NullableDereferencedChecker) &&
549
561
TrackedNullability->getValue () == Nullability::Nullable) {
550
562
BugReporter &BR = *Event.BR ;
551
563
// Do not suppress errors on defensive code paths, because dereferencing
552
564
// a nullable pointer is always an error.
553
565
if (Event.IsDirectDereference )
554
566
reportBug (" Nullable pointer is dereferenced" ,
555
- ErrorKind::NullableDereferenced, CK_NullableDereferenced ,
567
+ ErrorKind::NullableDereferenced, NullableDereferencedChecker ,
556
568
Event.SinkNode , Region, BR);
557
569
else {
558
570
reportBug (" Nullable pointer is passed to a callee that requires a "
559
571
" non-null" ,
560
- ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced ,
572
+ ErrorKind::NullablePassedToNonnull, NullableDereferencedChecker ,
561
573
Event.SinkNode , Region, BR);
562
574
}
563
575
}
@@ -711,7 +723,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
711
723
712
724
bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
713
725
Nullness == NullConstraint::IsNull);
714
- if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
726
+ if (isPartEnabled (NullReturnedFromNonnullChecker) &&
727
+ NullReturnedFromNonNull &&
715
728
RetExprTypeLevelNullability != Nullability::Nonnull &&
716
729
!InSuppressedMethodFamily) {
717
730
static CheckerProgramPointTag Tag (this , " NullReturnedFromNonnull" );
@@ -725,7 +738,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
725
738
OS << " returned from a " << C.getDeclDescription (D) <<
726
739
" that is expected to return a non-null value" ;
727
740
reportBugIfInvariantHolds (OS.str (), ErrorKind::NilReturnedToNonnull,
728
- CK_NullReturnedFromNonnull , N, nullptr , C,
741
+ NullReturnedFromNonnullChecker , N, nullptr , C,
729
742
RetExpr);
730
743
return ;
731
744
}
@@ -746,7 +759,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
746
759
State->get <NullabilityMap>(Region);
747
760
if (TrackedNullability) {
748
761
Nullability TrackedNullabValue = TrackedNullability->getValue ();
749
- if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
762
+ if (isPartEnabled (NullableReturnedFromNonnullChecker) &&
750
763
Nullness != NullConstraint::IsNotNull &&
751
764
TrackedNullabValue == Nullability::Nullable &&
752
765
RequiredNullability == Nullability::Nonnull) {
@@ -759,7 +772,8 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
759
772
" that is expected to return a non-null value" ;
760
773
761
774
reportBugIfInvariantHolds (OS.str (), ErrorKind::NullableReturnedToNonnull,
762
- CK_NullableReturnedFromNonnull, N, Region, C);
775
+ NullableReturnedFromNonnullChecker, N, Region,
776
+ C);
763
777
}
764
778
return ;
765
779
}
@@ -810,7 +824,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
810
824
811
825
unsigned ParamIdx = Param->getFunctionScopeIndex () + 1 ;
812
826
813
- if (ChecksEnabled[CK_NullPassedToNonnull] &&
827
+ if (isPartEnabled (NullPassedToNonnullChecker) &&
814
828
Nullness == NullConstraint::IsNull &&
815
829
ArgExprTypeLevelNullability != Nullability::Nonnull &&
816
830
RequiredNullability == Nullability::Nonnull &&
@@ -825,7 +839,8 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
825
839
OS << " passed to a callee that requires a non-null " << ParamIdx
826
840
<< llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
827
841
reportBugIfInvariantHolds (OS.str (), ErrorKind::NilPassedToNonnull,
828
- CK_NullPassedToNonnull, N, nullptr , C, ArgExpr,
842
+ NullPassedToNonnullChecker, N, nullptr , C,
843
+ ArgExpr,
829
844
/* SuppressPath=*/ false );
830
845
return ;
831
846
}
@@ -842,7 +857,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
842
857
TrackedNullability->getValue () != Nullability::Nullable)
843
858
continue ;
844
859
845
- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
860
+ if (isPartEnabled (NullablePassedToNonnullChecker) &&
846
861
RequiredNullability == Nullability::Nonnull &&
847
862
isDiagnosableCall (Call)) {
848
863
ExplodedNode *N = C.addTransition (State);
@@ -851,16 +866,16 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
851
866
OS << " Nullable pointer is passed to a callee that requires a non-null "
852
867
<< ParamIdx << llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
853
868
reportBugIfInvariantHolds (OS.str (), ErrorKind::NullablePassedToNonnull,
854
- CK_NullablePassedToNonnull , N, Region, C,
869
+ NullablePassedToNonnullChecker , N, Region, C,
855
870
ArgExpr, /* SuppressPath=*/ true );
856
871
return ;
857
872
}
858
- if (ChecksEnabled[CK_NullableDereferenced] &&
873
+ if (isPartEnabled (NullableDereferencedChecker) &&
859
874
Param->getType ()->isReferenceType ()) {
860
875
ExplodedNode *N = C.addTransition (State);
861
876
reportBugIfInvariantHolds (" Nullable pointer is dereferenced" ,
862
877
ErrorKind::NullableDereferenced,
863
- CK_NullableDereferenced , N, Region, C,
878
+ NullableDereferencedChecker , N, Region, C,
864
879
ArgExpr, /* SuppressPath=*/ true );
865
880
return ;
866
881
}
@@ -1295,7 +1310,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1295
1310
1296
1311
bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
1297
1312
RhsNullness == NullConstraint::IsNull);
1298
- if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
1313
+ if (isPartEnabled (NullPassedToNonnullChecker) && NullAssignedToNonNull &&
1299
1314
ValNullability != Nullability::Nonnull &&
1300
1315
ValueExprTypeLevelNullability != Nullability::Nonnull &&
1301
1316
!isARCNilInitializedLocal (C, S)) {
@@ -1314,7 +1329,8 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1314
1329
OS << (LocType->isObjCObjectPointerType () ? " nil" : " Null" );
1315
1330
OS << " assigned to a pointer which is expected to have non-null value" ;
1316
1331
reportBugIfInvariantHolds (OS.str (), ErrorKind::NilAssignedToNonnull,
1317
- CK_NullPassedToNonnull, N, nullptr , C, ValueStmt);
1332
+ NullPassedToNonnullChecker, N, nullptr , C,
1333
+ ValueStmt);
1318
1334
return ;
1319
1335
}
1320
1336
@@ -1340,14 +1356,15 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1340
1356
if (RhsNullness == NullConstraint::IsNotNull ||
1341
1357
TrackedNullability->getValue () != Nullability::Nullable)
1342
1358
return ;
1343
- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
1359
+ if (isPartEnabled (NullablePassedToNonnullChecker) &&
1344
1360
LocNullability == Nullability::Nonnull) {
1345
1361
static CheckerProgramPointTag Tag (this , " NullablePassedToNonnull" );
1346
1362
ExplodedNode *N = C.addTransition (State, C.getPredecessor (), &Tag);
1347
1363
reportBugIfInvariantHolds (" Nullable pointer is assigned to a pointer "
1348
1364
" which is expected to have non-null value" ,
1349
1365
ErrorKind::NullableAssignedToNonnull,
1350
- CK_NullablePassedToNonnull, N, ValueRegion, C);
1366
+ NullablePassedToNonnullChecker, N, ValueRegion,
1367
+ C);
1351
1368
}
1352
1369
return ;
1353
1370
}
@@ -1394,38 +1411,26 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
1394
1411
}
1395
1412
}
1396
1413
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 ); \
1416
1423
} \
1417
1424
\
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