@@ -81,11 +81,12 @@ enum class ErrorKind : int {
8181};
8282
8383class 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> {
8990
9091public:
9192 // If true, the checker will not diagnose nullabilility issues for calls
@@ -113,25 +114,21 @@ class NullabilityChecker
113114 void printState (raw_ostream &Out, ProgramStateRef State, const char *NL,
114115 const char *Sep) const override ;
115116
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};
135132
136133 // When set to false no nullability information will be tracked in
137134 // NullabilityMap. It is possible to catch errors like passing a null pointer
@@ -164,17 +161,16 @@ class NullabilityChecker
164161 // /
165162 // / When \p SuppressPath is set to true, no more bugs will be reported on this
166163 // / 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,
170167 const Stmt *ValueExpr = nullptr ,
171168 bool SuppressPath = false ) const ;
172169
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,
175172 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);
178174 if (Region) {
179175 R->markInteresting (Region);
180176 R->addVisitor <NullabilityBugVisitor>(Region);
@@ -480,7 +476,7 @@ static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
480476}
481477
482478void NullabilityChecker::reportBugIfInvariantHolds (
483- StringRef Msg, ErrorKind Error, CheckKind CK , ExplodedNode *N,
479+ StringRef Msg, ErrorKind Error, const BugType &BT , ExplodedNode *N,
484480 const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
485481 bool SuppressPath) const {
486482 ProgramStateRef OriginalState = N->getState ();
@@ -492,7 +488,7 @@ void NullabilityChecker::reportBugIfInvariantHolds(
492488 N = C.addTransition (OriginalState, N);
493489 }
494490
495- reportBug (Msg, Error, CK , N, Region, C.getBugReporter (), ValueExpr);
491+ reportBug (Msg, Error, BT , N, Region, C.getBugReporter (), ValueExpr);
496492}
497493
498494// / Cleaning up the program state.
@@ -546,19 +542,19 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
546542 if (!TrackedNullability)
547543 return ;
548544
549- if (ChecksEnabled[CK_NullableDereferenced] &&
545+ if (NullableDereferenced. isEnabled () &&
550546 TrackedNullability->getValue () == Nullability::Nullable) {
551547 BugReporter &BR = *Event.BR ;
552548 // Do not suppress errors on defensive code paths, because dereferencing
553549 // a nullable pointer is always an error.
554550 if (Event.IsDirectDereference )
555551 reportBug (" Nullable pointer is dereferenced" ,
556- ErrorKind::NullableDereferenced, CK_NullableDereferenced ,
552+ ErrorKind::NullableDereferenced, NullableDereferenced ,
557553 Event.SinkNode , Region, BR);
558554 else {
559555 reportBug (" Nullable pointer is passed to a callee that requires a "
560556 " non-null" ,
561- ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced ,
557+ ErrorKind::NullablePassedToNonnull, NullableDereferenced ,
562558 Event.SinkNode , Region, BR);
563559 }
564560 }
@@ -710,29 +706,28 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
710706 Nullability RetExprTypeLevelNullability =
711707 getNullabilityAnnotation (lookThroughImplicitCasts (RetExpr)->getType ());
712708
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 ;
721717
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+ }
732728
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.
736731 State = State->set <InvariantViolated>(true );
737732 C.addTransition (State);
738733 return ;
@@ -746,7 +741,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
746741 State->get <NullabilityMap>(Region);
747742 if (TrackedNullability) {
748743 Nullability TrackedNullabValue = TrackedNullability->getValue ();
749- if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
744+ if (NullableReturnedFromNonnull. isEnabled () &&
750745 Nullness != NullConstraint::IsNotNull &&
751746 TrackedNullabValue == Nullability::Nullable &&
752747 RequiredNullability == Nullability::Nonnull) {
@@ -758,7 +753,7 @@ void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
758753 " that is expected to return a non-null value" ;
759754
760755 reportBugIfInvariantHolds (OS.str (), ErrorKind::NullableReturnedToNonnull,
761- CK_NullableReturnedFromNonnull , N, Region, C);
756+ NullableReturnedFromNonnull , N, Region, C);
762757 }
763758 return ;
764759 }
@@ -809,8 +804,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
809804
810805 unsigned ParamIdx = Param->getFunctionScopeIndex () + 1 ;
811806
812- if (ChecksEnabled[CK_NullPassedToNonnull] &&
813- Nullness == NullConstraint::IsNull &&
807+ if (NullPassedToNonnull.isEnabled () && Nullness == NullConstraint::IsNull &&
814808 ArgExprTypeLevelNullability != Nullability::Nonnull &&
815809 RequiredNullability == Nullability::Nonnull &&
816810 isDiagnosableCall (Call)) {
@@ -824,7 +818,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
824818 OS << " passed to a callee that requires a non-null " << ParamIdx
825819 << llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
826820 reportBugIfInvariantHolds (OS.str (), ErrorKind::NilPassedToNonnull,
827- CK_NullPassedToNonnull , N, nullptr , C, ArgExpr,
821+ NullPassedToNonnull , N, nullptr , C, ArgExpr,
828822 /* SuppressPath=*/ false );
829823 return ;
830824 }
@@ -841,7 +835,7 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
841835 TrackedNullability->getValue () != Nullability::Nullable)
842836 continue ;
843837
844- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
838+ if (NullablePassedToNonnull. isEnabled () &&
845839 RequiredNullability == Nullability::Nonnull &&
846840 isDiagnosableCall (Call)) {
847841 ExplodedNode *N = C.addTransition (State);
@@ -850,17 +844,16 @@ void NullabilityChecker::checkPreCall(const CallEvent &Call,
850844 OS << " Nullable pointer is passed to a callee that requires a non-null "
851845 << ParamIdx << llvm::getOrdinalSuffix (ParamIdx) << " parameter" ;
852846 reportBugIfInvariantHolds (OS.str (), ErrorKind::NullablePassedToNonnull,
853- CK_NullablePassedToNonnull , N, Region, C,
847+ NullablePassedToNonnull , N, Region, C,
854848 ArgExpr, /* SuppressPath=*/ true );
855849 return ;
856850 }
857- if (ChecksEnabled[CK_NullableDereferenced] &&
851+ if (NullableDereferenced. isEnabled () &&
858852 Param->getType ()->isReferenceType ()) {
859853 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 );
864857 return ;
865858 }
866859 continue ;
@@ -1294,7 +1287,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
12941287
12951288 bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
12961289 RhsNullness == NullConstraint::IsNull);
1297- if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
1290+ if (NullPassedToNonnull. isEnabled () && NullAssignedToNonNull &&
12981291 ValNullability != Nullability::Nonnull &&
12991292 ValueExprTypeLevelNullability != Nullability::Nonnull &&
13001293 !isARCNilInitializedLocal (C, S)) {
@@ -1312,7 +1305,7 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
13121305 OS << (LocType->isObjCObjectPointerType () ? " nil" : " Null" );
13131306 OS << " assigned to a pointer which is expected to have non-null value" ;
13141307 reportBugIfInvariantHolds (OS.str (), ErrorKind::NilAssignedToNonnull,
1315- CK_NullPassedToNonnull , N, nullptr , C, ValueStmt);
1308+ NullPassedToNonnull , N, nullptr , C, ValueStmt);
13161309 return ;
13171310 }
13181311
@@ -1338,13 +1331,13 @@ void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
13381331 if (RhsNullness == NullConstraint::IsNotNull ||
13391332 TrackedNullability->getValue () != Nullability::Nullable)
13401333 return ;
1341- if (ChecksEnabled[CK_NullablePassedToNonnull] &&
1334+ if (NullablePassedToNonnull. isEnabled () &&
13421335 LocNullability == Nullability::Nonnull) {
13431336 ExplodedNode *N = C.addTransition (State, C.getPredecessor ());
13441337 reportBugIfInvariantHolds (" Nullable pointer is assigned to a pointer "
13451338 " which is expected to have non-null value" ,
13461339 ErrorKind::NullableAssignedToNonnull,
1347- CK_NullablePassedToNonnull , N, ValueRegion, C);
1340+ NullablePassedToNonnull , N, ValueRegion, C);
13481341 }
13491342 return ;
13501343 }
@@ -1391,28 +1384,26 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
13911384 }
13921385}
13931386
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 ); \
14131404 } \
14141405 \
1415- bool ento::shouldRegister##name ##Checker(const CheckerManager &mgr ) { \
1406+ bool ento::shouldRegister##NAME ##Checker(const CheckerManager &) { \
14161407 return true ; \
14171408 }
14181409
0 commit comments