@@ -81,11 +81,12 @@ enum class ErrorKind : int {
81
81
};
82
82
83
83
class NullabilityChecker
84
- : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
85
- check::PostCall, check::PostStmt<ExplicitCastExpr>,
86
- check::PostObjCMessage, check::DeadSymbols, eval::Assume,
87
- check::Location, check::Event<ImplicitNullDerefEvent>,
88
- check::BeginFunction> {
84
+ : public CheckerFamily<
85
+ check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
86
+ check::PostCall, check::PostStmt<ExplicitCastExpr>,
87
+ check::PostObjCMessage, check::DeadSymbols, eval::Assume,
88
+ check::Location, check::Event<ImplicitNullDerefEvent>,
89
+ check::BeginFunction> {
89
90
90
91
public:
91
92
// If true, the checker will not diagnose nullabilility issues for calls
@@ -113,25 +114,21 @@ class NullabilityChecker
113
114
void printState (raw_ostream &Out, ProgramStateRef State, const char *NL,
114
115
const char *Sep) const override ;
115
116
116
- enum CheckKind {
117
- CK_NullPassedToNonnull,
118
- CK_NullReturnedFromNonnull,
119
- CK_NullableDereferenced,
120
- CK_NullablePassedToNonnull,
121
- CK_NullableReturnedFromNonnull,
122
- CK_NumCheckKinds
123
- };
124
-
125
- bool ChecksEnabled[CK_NumCheckKinds] = {false };
126
- CheckerNameRef CheckNames[CK_NumCheckKinds];
127
- mutable std::unique_ptr<BugType> BTs[CK_NumCheckKinds];
128
-
129
- const std::unique_ptr<BugType> &getBugType (CheckKind Kind) const {
130
- if (!BTs[Kind])
131
- BTs[Kind].reset (new BugType (CheckNames[Kind], " Nullability" ,
132
- categories::MemoryError));
133
- return BTs[Kind];
134
- }
117
+ StringRef getDebugTag () const override { return " NullabilityChecker" ; }
118
+
119
+ // FIXME: All bug types share the same Description ("Nullability") since the
120
+ // creation of this checker. We should write more descriptive descriptions...
121
+ // or just eliminate the Description field if it is meaningless?
122
+ CheckerFrontendWithBugType NullPassedToNonnull{" Nullability" ,
123
+ categories::MemoryError};
124
+ CheckerFrontendWithBugType NullReturnedFromNonnull{" Nullability" ,
125
+ categories::MemoryError};
126
+ CheckerFrontendWithBugType NullableDereferenced{" Nullability" ,
127
+ categories::MemoryError};
128
+ CheckerFrontendWithBugType NullablePassedToNonnull{" Nullability" ,
129
+ categories::MemoryError};
130
+ CheckerFrontendWithBugType NullableReturnedFromNonnull{
131
+ " Nullability" , categories::MemoryError};
135
132
136
133
// When set to false no nullability information will be tracked in
137
134
// NullabilityMap. It is possible to catch errors like passing a null pointer
@@ -164,17 +161,16 @@ class NullabilityChecker
164
161
// /
165
162
// / When \p SuppressPath is set to true, no more bugs will be reported on this
166
163
// / path by this checker.
167
- void reportBugIfInvariantHolds (StringRef Msg, ErrorKind Error, CheckKind CK,
168
- ExplodedNode *N, const MemRegion *Region ,
169
- CheckerContext &C,
164
+ void reportBugIfInvariantHolds (StringRef Msg, ErrorKind Error,
165
+ const BugType &BT, ExplodedNode *N ,
166
+ const MemRegion *Region, CheckerContext &C,
170
167
const Stmt *ValueExpr = nullptr ,
171
168
bool SuppressPath = false ) const ;
172
169
173
- void reportBug (StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N ,
174
- const MemRegion *Region, BugReporter &BR,
170
+ void reportBug (StringRef Msg, ErrorKind Error, const BugType &BT ,
171
+ ExplodedNode *N, const MemRegion *Region, BugReporter &BR,
175
172
const Stmt *ValueExpr = nullptr ) const {
176
- const std::unique_ptr<BugType> &BT = getBugType (CK);
177
- auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
173
+ auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
178
174
if (Region) {
179
175
R->markInteresting (Region);
180
176
R->addVisitor <NullabilityBugVisitor>(Region);
@@ -480,7 +476,7 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
480
476
}
481
477
482
478
void NullabilityChecker::reportBugIfInvariantHolds (
483
- StringRef Msg, ErrorKind Error, CheckKind CK , ExplodedNode *N,
479
+ StringRef Msg, ErrorKind Error, const BugType &BT , ExplodedNode *N,
484
480
const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
485
481
bool SuppressPath) const {
486
482
ProgramStateRef OriginalState = N->getState ();
@@ -492,7 +488,7 @@ void NullabilityChecker::reportBugIfInvariantHolds(
492
488
N = C.addTransition (OriginalState, N);
493
489
}
494
490
495
- reportBug (Msg, Error, CK , N, Region, C.getBugReporter (), ValueExpr);
491
+ reportBug (Msg, Error, BT , N, Region, C.getBugReporter (), ValueExpr);
496
492
}
497
493
498
494
// / Cleaning up the program state.
@@ -546,19 +542,19 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
546
542
if (!TrackedNullability)
547
543
return ;
548
544
549
- if (ChecksEnabled[CK_NullableDereferenced] &&
545
+ if (NullableDereferenced. isEnabled () &&
550
546
TrackedNullability->getValue () == Nullability::Nullable) {
551
547
BugReporter &BR = *Event.BR ;
552
548
// Do not suppress errors on defensive code paths, because dereferencing
553
549
// a nullable pointer is always an error.
554
550
if (Event.IsDirectDereference )
555
551
reportBug (" Nullable pointer is dereferenced" ,
556
- ErrorKind::NullableDereferenced, CK_NullableDereferenced ,
552
+ ErrorKind::NullableDereferenced, NullableDereferenced ,
557
553
Event.SinkNode , Region, BR);
558
554
else {
559
555
reportBug (" Nullable pointer is passed to a callee that requires a "
560
556
" non-null" ,
561
- ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced ,
557
+ ErrorKind::NullablePassedToNonnull, NullableDereferenced ,
562
558
Event.SinkNode , Region, BR);
563
559
}
564
560
}
@@ -710,29 +706,28 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
710
706
Nullability RetExprTypeLevelNullability =
711
707
getNullabilityAnnotation (lookThroughImplicitCasts (RetExpr)->getType ());
712
708
713
- bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
714
- Nullness == NullConstraint::IsNull);
715
- if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
716
- RetExprTypeLevelNullability != Nullability::Nonnull &&
717
- !InSuppressedMethodFamily) {
718
- ExplodedNode *N = C.generateErrorNode (State);
719
- if (!N)
720
- return ;
709
+ if (RequiredNullability == Nullability::Nonnull &&
710
+ Nullness == NullConstraint::IsNull) {
711
+ if (NullReturnedFromNonnull. isEnabled () &&
712
+ RetExprTypeLevelNullability != Nullability::Nonnull &&
713
+ !InSuppressedMethodFamily) {
714
+ ExplodedNode *N = C.generateErrorNode (State);
715
+ if (!N)
716
+ return ;
721
717
722
- SmallString<256 > SBuf;
723
- llvm::raw_svector_ostream OS (SBuf);
724
- OS << (RetExpr->getType ()->isObjCObjectPointerType () ? " nil" : " Null" );
725
- OS << " returned from a " << C.getDeclDescription (D) <<
726
- " that is expected to return a non-null value" ;
727
- reportBugIfInvariantHolds (OS.str (), ErrorKind::NilReturnedToNonnull,
728
- CK_NullReturnedFromNonnull , N, nullptr , C,
729
- RetExpr);
730
- return ;
731
- }
718
+ SmallString<256 > SBuf;
719
+ llvm::raw_svector_ostream OS (SBuf);
720
+ OS << (RetExpr->getType ()->isObjCObjectPointerType () ? " nil" : " Null" );
721
+ OS << " returned from a " << C.getDeclDescription (D)
722
+ << " that is expected to return a non-null value" ;
723
+ reportBugIfInvariantHolds (OS.str (), ErrorKind::NilReturnedToNonnull,
724
+ NullReturnedFromNonnull , N, nullptr , C,
725
+ RetExpr);
726
+ return ;
727
+ }
732
728
733
- // If null was returned from a non-null function, mark the nullability
734
- // invariant as violated even if the diagnostic was suppressed.
735
- if (NullReturnedFromNonNull) {
729
+ // If null was returned from a non-null function, mark the nullability
730
+ // invariant as violated even if the diagnostic was suppressed.
736
731
State = State->set <InvariantViolated>(true );
737
732
C.addTransition (State);
738
733
return ;
@@ -746,7 +741,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
746
741
State->get <NullabilityMap>(Region);
747
742
if (TrackedNullability) {
748
743
Nullability TrackedNullabValue = TrackedNullability->getValue ();
749
- if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
744
+ if (NullableReturnedFromNonnull. isEnabled () &&
750
745
Nullness != NullConstraint::IsNotNull &&
751
746
TrackedNullabValue == Nullability::Nullable &&
752
747
RequiredNullability == Nullability::Nonnull) {
@@ -758,7 +753,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
758
753
" that is expected to return a non-null value" ;
759
754
760
755
reportBugIfInvariantHolds (OS.str (), ErrorKind::NullableReturnedToNonnull,
761
- CK_NullableReturnedFromNonnull , N, Region, C);
756
+ NullableReturnedFromNonnull , N, Region, C);
762
757
}
763
758
return ;
764
759
}
@@ -809,8 +804,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
809
804
810
805
unsigned ParamIdx = Param->getFunctionScopeIndex () + 1 ;
811
806
812
- if (ChecksEnabled[CK_NullPassedToNonnull] &&
813
- Nullness == NullConstraint::IsNull &&
807
+ if (NullPassedToNonnull.isEnabled () && Nullness == NullConstraint::IsNull &&
814
808
ArgExprTypeLevelNullability != Nullability::Nonnull &&
815
809
RequiredNullability == Nullability::Nonnull &&
816
810
isDiagnosableCall (Call)) {
@@ -824,7 +818,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
824
818
OS << " passed to a callee that requires a non-null " << ParamIdx
825
819
<< llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
826
820
reportBugIfInvariantHolds (OS.str (), ErrorKind::NilPassedToNonnull,
827
- CK_NullPassedToNonnull , N, nullptr , C, ArgExpr,
821
+ NullPassedToNonnull , N, nullptr , C, ArgExpr,
828
822
/* SuppressPath=*/ false );
829
823
return ;
830
824
}
@@ -841,7 +835,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
841
835
TrackedNullability->getValue () != Nullability::Nullable)
842
836
continue ;
843
837
844
- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
838
+ if (NullablePassedToNonnull. isEnabled () &&
845
839
RequiredNullability == Nullability::Nonnull &&
846
840
isDiagnosableCall (Call)) {
847
841
ExplodedNode *N = C.addTransition (State);
@@ -850,17 +844,16 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
850
844
OS << " Nullable pointer is passed to a callee that requires a non-null "
851
845
<< ParamIdx << llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
852
846
reportBugIfInvariantHolds (OS.str (), ErrorKind::NullablePassedToNonnull,
853
- CK_NullablePassedToNonnull , N, Region, C,
847
+ NullablePassedToNonnull , N, Region, C,
854
848
ArgExpr, /* SuppressPath=*/ true );
855
849
return ;
856
850
}
857
- if (ChecksEnabled[CK_NullableDereferenced] &&
851
+ if (NullableDereferenced. isEnabled () &&
858
852
Param->getType ()->isReferenceType ()) {
859
853
ExplodedNode *N = C.addTransition (State);
860
- reportBugIfInvariantHolds (" Nullable pointer is dereferenced" ,
861
- ErrorKind::NullableDereferenced,
862
- CK_NullableDereferenced, N, Region, C,
863
- ArgExpr, /* SuppressPath=*/ true );
854
+ reportBugIfInvariantHolds (
855
+ " Nullable pointer is dereferenced" , ErrorKind::NullableDereferenced,
856
+ NullableDereferenced, N, Region, C, ArgExpr, /* SuppressPath=*/ true );
864
857
return ;
865
858
}
866
859
continue ;
@@ -1294,7 +1287,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1294
1287
1295
1288
bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
1296
1289
RhsNullness == NullConstraint::IsNull);
1297
- if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
1290
+ if (NullPassedToNonnull. isEnabled () && NullAssignedToNonNull &&
1298
1291
ValNullability != Nullability::Nonnull &&
1299
1292
ValueExprTypeLevelNullability != Nullability::Nonnull &&
1300
1293
!isARCNilInitializedLocal (C, S)) {
@@ -1312,7 +1305,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1312
1305
OS << (LocType->isObjCObjectPointerType () ? " nil" : " Null" );
1313
1306
OS << " assigned to a pointer which is expected to have non-null value" ;
1314
1307
reportBugIfInvariantHolds (OS.str (), ErrorKind::NilAssignedToNonnull,
1315
- CK_NullPassedToNonnull , N, nullptr , C, ValueStmt);
1308
+ NullPassedToNonnull , N, nullptr , C, ValueStmt);
1316
1309
return ;
1317
1310
}
1318
1311
@@ -1338,13 +1331,13 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1338
1331
if (RhsNullness == NullConstraint::IsNotNull ||
1339
1332
TrackedNullability->getValue () != Nullability::Nullable)
1340
1333
return ;
1341
- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
1334
+ if (NullablePassedToNonnull. isEnabled () &&
1342
1335
LocNullability == Nullability::Nonnull) {
1343
1336
ExplodedNode *N = C.addTransition (State, C.getPredecessor ());
1344
1337
reportBugIfInvariantHolds (" Nullable pointer is assigned to a pointer "
1345
1338
" which is expected to have non-null value" ,
1346
1339
ErrorKind::NullableAssignedToNonnull,
1347
- CK_NullablePassedToNonnull , N, ValueRegion, C);
1340
+ NullablePassedToNonnull , N, ValueRegion, C);
1348
1341
}
1349
1342
return ;
1350
1343
}
@@ -1391,28 +1384,26 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
1391
1384
}
1392
1385
}
1393
1386
1394
- void ento::registerNullabilityBase (CheckerManager &mgr) {
1395
- mgr.registerChecker <NullabilityChecker>();
1396
- }
1397
-
1398
- bool ento::shouldRegisterNullabilityBase (const CheckerManager &mgr) {
1399
- return true ;
1400
- }
1401
-
1402
- #define REGISTER_CHECKER (name, trackingRequired ) \
1403
- void ento::register ##name##Checker(CheckerManager &mgr) { \
1404
- NullabilityChecker *checker = mgr.getChecker <NullabilityChecker>(); \
1405
- checker->ChecksEnabled [NullabilityChecker::CK_##name] = true ; \
1406
- checker->CheckNames [NullabilityChecker::CK_##name] = \
1407
- mgr.getCurrentCheckerName (); \
1408
- checker->NeedTracking = checker->NeedTracking || trackingRequired; \
1409
- checker->NoDiagnoseCallsToSystemHeaders = \
1410
- checker->NoDiagnoseCallsToSystemHeaders || \
1411
- mgr.getAnalyzerOptions ().getCheckerBooleanOption ( \
1412
- checker, " NoDiagnoseCallsToSystemHeaders" , true ); \
1387
+ // The checker group "nullability" (which consists of the checkers that are
1388
+ // implemented in this file) has a group-level configuration option which
1389
+ // affects all the checkers in the group. As this is a completely unique
1390
+ // remnant of old design (this is the only group option in the analyzer), there
1391
+ // is no machinery to inject the group name from `Checkers.td`, so it is simply
1392
+ // hardcoded here:
1393
+ constexpr llvm::StringLiteral GroupName = " nullability" ;
1394
+ constexpr llvm::StringLiteral GroupOptName = " NoDiagnoseCallsToSystemHeaders" ;
1395
+
1396
+ #define REGISTER_CHECKER (NAME, TRACKING_REQUIRED ) \
1397
+ void ento::register ##NAME##Checker(CheckerManager &Mgr) { \
1398
+ NullabilityChecker *Chk = Mgr.getChecker <NullabilityChecker>(); \
1399
+ Chk->NAME .enable (Mgr); \
1400
+ Chk->NeedTracking = Chk->NeedTracking || TRACKING_REQUIRED; \
1401
+ Chk->NoDiagnoseCallsToSystemHeaders = \
1402
+ Mgr.getAnalyzerOptions ().getCheckerBooleanOption (GroupName, \
1403
+ GroupOptName, true ); \
1413
1404
} \
1414
1405
\
1415
- bool ento::shouldRegister##name ##Checker(const CheckerManager &mgr ) { \
1406
+ bool ento::shouldRegister##NAME ##Checker(const CheckerManager &) { \
1416
1407
return true ; \
1417
1408
}
1418
1409
0 commit comments