Skip to content

Commit 4c8f434

Browse files
authored
[analyzer] Conversion to CheckerFamily: NullabilityChecker (#143735)
This commit converts NullabilityChecker to the new checker family framework that was introduced in the recent commit 6833076 This commit removes the dummy checker `nullability.NullabilityBase` because it was hidden from the users and didn't have any useful role except for helping the registration of the checker parts in the old ad-hoc system (which is replaced by the new standardized framework). Except for the removal of this dummy checker, no functional changes intended.
1 parent 00709c3 commit 4c8f434

File tree

5 files changed

+109
-125
lines changed

5 files changed

+109
-125
lines changed

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -326,39 +326,34 @@ def StdVariantChecker : Checker<"StdVariant">,
326326

327327
let ParentPackage = Nullability in {
328328

329-
def NullabilityBase : Checker<"NullabilityBase">,
330-
HelpText<"Stores information during the analysis about nullability.">,
331-
Documentation<NotDocumented>,
332-
Hidden;
333-
334-
def NullPassedToNonnullChecker : Checker<"NullPassedToNonnull">,
335-
HelpText<"Warns when a null pointer is passed to a pointer which has a "
336-
"_Nonnull type.">,
337-
Dependencies<[NullabilityBase]>,
338-
Documentation<HasDocumentation>;
329+
def NullPassedToNonnullChecker
330+
: Checker<"NullPassedToNonnull">,
331+
HelpText<"Warns when a null pointer is passed to a pointer which has a "
332+
"_Nonnull type.">,
333+
Documentation<HasDocumentation>;
339334

340-
def NullReturnedFromNonnullChecker : Checker<"NullReturnedFromNonnull">,
341-
HelpText<"Warns when a null pointer is returned from a function that has "
342-
"_Nonnull return type.">,
343-
Dependencies<[NullabilityBase]>,
344-
Documentation<HasDocumentation>;
335+
def NullReturnedFromNonnullChecker
336+
: Checker<"NullReturnedFromNonnull">,
337+
HelpText<"Warns when a null pointer is returned from a function that "
338+
"has _Nonnull return type.">,
339+
Documentation<HasDocumentation>;
345340

346-
def NullableDereferencedChecker : Checker<"NullableDereferenced">,
347-
HelpText<"Warns when a nullable pointer is dereferenced.">,
348-
Dependencies<[NullabilityBase]>,
349-
Documentation<HasDocumentation>;
341+
def NullableDereferencedChecker
342+
: Checker<"NullableDereferenced">,
343+
HelpText<"Warns when a nullable pointer is dereferenced.">,
344+
Documentation<HasDocumentation>;
350345

351-
def NullablePassedToNonnullChecker : Checker<"NullablePassedToNonnull">,
352-
HelpText<"Warns when a nullable pointer is passed to a pointer which has a "
353-
"_Nonnull type.">,
354-
Dependencies<[NullabilityBase]>,
355-
Documentation<HasDocumentation>;
346+
def NullablePassedToNonnullChecker
347+
: Checker<"NullablePassedToNonnull">,
348+
HelpText<"Warns when a nullable pointer is passed to a pointer which "
349+
"has a _Nonnull type.">,
350+
Documentation<HasDocumentation>;
356351

357-
def NullableReturnedFromNonnullChecker : Checker<"NullableReturnedFromNonnull">,
358-
HelpText<"Warns when a nullable pointer is returned from a function that has "
359-
"_Nonnull return type.">,
360-
Dependencies<[NullabilityBase]>,
361-
Documentation<NotDocumented>;
352+
def NullableReturnedFromNonnullChecker
353+
: Checker<"NullableReturnedFromNonnull">,
354+
HelpText<"Warns when a nullable pointer is returned from a function "
355+
"that has _Nonnull return type.">,
356+
Documentation<NotDocumented>;
362357

363358
} // end "nullability"
364359

clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp

Lines changed: 84 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ enum class ErrorKind : int {
8181
};
8282

8383
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> {
8990

9091
public:
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

482478
void 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

clang/test/Analysis/analyzer-enabled-checkers.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
// CHECK-NEXT: core.uninitialized.CapturedBlockVariable
3535
// CHECK-NEXT: core.uninitialized.UndefReturn
3636
// CHECK-NEXT: deadcode.DeadStores
37-
// CHECK-NEXT: nullability.NullabilityBase
3837
// CHECK-NEXT: nullability.NullPassedToNonnull
3938
// CHECK-NEXT: nullability.NullReturnedFromNonnull
4039
// CHECK-NEXT: security.insecureAPI.SecuritySyntaxChecker

0 commit comments

Comments
 (0)