clang: lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
28
36
37#include "llvm/ADT/STLExtras.h"
38#include "llvm/ADT/StringExtras.h"
39#include "llvm/Support/Path.h"
40
41using namespace clang;
42using namespace ento;
43
44namespace {
45
46
47
48
49
50
53 std::min(static_cast<char>(Lhs), static_cast<char>(Rhs)));
54}
55
56const char *getNullabilityString(Nullability Nullab) {
57 switch (Nullab) {
58 case Nullability::Contradicted:
59 return "contradicted";
60 case Nullability::Nullable:
61 return "nullable";
62 case Nullability::Unspecified:
63 return "unspecified";
64 case Nullability::Nonnull:
65 return "nonnull";
66 }
67 llvm_unreachable("Unexpected enumeration.");
68 return "";
69}
70
71
72enum class ErrorKind : int {
73 NilAssignedToNonnull,
74 NilPassedToNonnull,
75 NilReturnedToNonnull,
76 NullableAssignedToNonnull,
77 NullableReturnedToNonnull,
78 NullableDereferenced,
79 NullablePassedToNonnull
80};
81
82class NullabilityChecker
83 : public Checker<check::Bind, check::PreCall, check::PreStmt,
84 check::PostCall, check::PostStmt,
85 check::PostObjCMessage, check::DeadSymbols, eval::Assume,
86 check::Location, check::Event,
87 check::BeginFunction> {
88
89public:
90
91
92
93
94
95
96 bool NoDiagnoseCallsToSystemHeaders = false;
97
106 void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
110 bool Assumption) const;
111
113 const char *Sep) const override;
114
115 enum CheckKind {
116 CK_NullPassedToNonnull,
117 CK_NullReturnedFromNonnull,
118 CK_NullableDereferenced,
119 CK_NullablePassedToNonnull,
120 CK_NullableReturnedFromNonnull,
121 CK_NumCheckKinds
122 };
123
124 bool ChecksEnabled[CK_NumCheckKinds] = {false};
126 mutable std::unique_ptr BTs[CK_NumCheckKinds];
127
128 const std::unique_ptr &getBugType(CheckKind Kind) const {
129 if (!BTs[Kind])
130 BTs[Kind].reset(new BugType(CheckNames[Kind], "Nullability",
132 return BTs[Kind];
133 }
134
135
136
137
138
139 bool NeedTracking = false;
140
141private:
143 public:
144 NullabilityBugVisitor(const MemRegion *M) : Region(M) {}
145
146 void Profile(llvm::FoldingSetNodeID &ID) const override {
147 static int X = 0;
149 ID.AddPointer(Region);
150 }
151
155
156 private:
157
159 };
160
161
162
163
164
165
166 void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, CheckKind CK,
169 const Stmt *ValueExpr = nullptr,
170 bool SuppressPath = false) const;
171
172 void reportBug(StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
174 const Stmt *ValueExpr = nullptr) const {
175 const std::unique_ptr &BT = getBugType(CK);
176 auto R = std::make_unique(*BT, Msg, N);
177 if (Region) {
178 R->markInteresting(Region);
179 R->addVisitor(Region);
180 }
181 if (ValueExpr) {
182 R->addRange(ValueExpr->getSourceRange());
183 if (Error == ErrorKind::NilAssignedToNonnull ||
184 Error == ErrorKind::NilPassedToNonnull ||
185 Error == ErrorKind::NilReturnedToNonnull)
186 if (const auto *Ex = dyn_cast(ValueExpr))
188 }
190 }
191
192
193
195 bool CheckSuperRegion = false) const;
196
197
198
199 bool isDiagnosableCall(const CallEvent &Call) const {
200 if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader())
201 return false;
202
203 return true;
204 }
205};
206
207class NullabilityState {
208public:
209 NullabilityState(Nullability Nullab, const Stmt *Source = nullptr)
210 : Nullab(Nullab), Source(Source) {}
211
212 const Stmt *getNullabilitySource() const { return Source; }
213
214 Nullability getValue() const { return Nullab; }
215
216 void Profile(llvm::FoldingSetNodeID &ID) const {
217 ID.AddInteger(static_cast<char>(Nullab));
218 ID.AddPointer(Source);
219 }
220
221 void print(raw_ostream &Out) const {
222 Out << getNullabilityString(Nullab) << "\n";
223 }
224
225private:
227
228
229
230
231 const Stmt *Source;
232};
233
234bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
235 return Lhs.getValue() == Rhs.getValue() &&
236 Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
237}
238
239
240
241
242using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>;
243
244
245struct ConstrainedPropertyVal {
246
247
249
250
251 bool isConstrainedNonnull;
252
254 : Value(SV), isConstrainedNonnull(false) {}
255
256 void Profile(llvm::FoldingSetNodeID &ID) const {
257 Value.Profile(ID);
258 ID.AddInteger(isConstrainedNonnull ? 1 : 0);
259 }
260};
261
262bool operator==(const ConstrainedPropertyVal &Lhs,
263 const ConstrainedPropertyVal &Rhs) {
264 return Lhs.Value == Rhs.Value &&
265 Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull;
266}
267
268}
269
271 NullabilityState)
273 ConstrainedPropertyVal)
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
300
302
311}
312
315}
316
318NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
319 if (!NeedTracking)
320 return nullptr;
321
323 if (!RegionSVal)
324 return nullptr;
325
326 const MemRegion *Region = RegionSVal->getRegion();
327
328 if (CheckSuperRegion) {
330 if (const auto *ER = dyn_cast(FieldReg->getSuperRegion()))
331 FieldReg = ER;
332 return dyn_cast(FieldReg->getSuperRegion());
333 }
335 return dyn_cast(ElementReg->getSuperRegion());
336 }
337
338 return dyn_cast(Region);
339}
340
346
347 const NullabilityState *TrackedNullab = State->get(Region);
348 const NullabilityState *TrackedNullabPrev =
349 StatePrev->get(Region);
350 if (!TrackedNullab)
351 return nullptr;
352
353 if (TrackedNullabPrev &&
354 TrackedNullabPrev->getValue() == TrackedNullab->getValue())
355 return nullptr;
356
357
358 const Stmt *S = TrackedNullab->getNullabilitySource();
359 if (!S || S->getBeginLoc().isInvalid()) {
361 }
362
363 if (!S)
364 return nullptr;
365
366 std::string InfoText =
367 (llvm::Twine("Nullability '") +
368 getNullabilityString(TrackedNullab->getValue()) + "' is inferred")
369 .str();
370
371
374 return std::make_shared(Pos, InfoText, true);
375}
376
377
378
382 return false;
383
385 if (!RegionVal)
386 return false;
387
388
389
390
391
392
393
394 auto StoredVal = State->getSVal(*RegionVal).getAs<loc::MemRegionVal>();
395 if (!StoredVal || !isa(StoredVal->getRegion()))
396 return false;
397
399 return true;
400
401 return false;
402}
403
404static bool
408 for (const auto *ParamDecl : Params) {
409 if (ParamDecl->isParameterPack())
410 break;
411
412 SVal LV = State->getLValue(ParamDecl, LocCtxt);
415 return true;
416 }
417 }
418 return false;
419}
420
421static bool
424 auto *MD = dyn_cast(LocCtxt->getDecl());
425 if (!MD || !MD->isInstanceMethod())
426 return false;
427
429 if (!SelfDecl)
430 return false;
431
432 SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt));
433
435 dyn_cast(SelfDecl->getType());
436 if (!SelfType)
437 return false;
438
440 if (!ID)
441 return false;
442
443 for (const auto *IvarDecl : ID->ivars()) {
444 SVal LV = State->getLValue(IvarDecl, SelfVal);
446 return true;
447 }
448 }
449 return false;
450}
451
454 if (State->get())
455 return true;
456
459 if ()
460 return false;
461
463 if (const auto *BD = dyn_cast(D))
464 Params = BD->parameters();
465 else if (const auto *FD = dyn_cast(D))
466 Params = FD->parameters();
467 else if (const auto *MD = dyn_cast(D))
468 Params = MD->parameters();
469 else
470 return false;
471
475 C.addTransition(State->set(true), N);
476 return true;
477 }
478 return false;
479}
480
481void NullabilityChecker::reportBugIfInvariantHolds(
482 StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N,
484 bool SuppressPath) const {
486
488 return;
489 if (SuppressPath) {
490 OriginalState = OriginalState->set(true);
491 N = C.addTransition(OriginalState, N);
492 }
493
494 reportBug(Msg, Error, CK, N, Region, C.getBugReporter(), ValueExpr);
495}
496
497
498void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
501 NullabilityMapTy Nullabilities = State->get();
502 for (const MemRegion *Reg : llvm::make_first_range(Nullabilities)) {
504 assert(Region && "Non-symbolic region is tracked.");
505 if (SR.isDead(Region->getSymbol())) {
506 State = State->remove(Reg);
507 }
508 }
509
510
511
512 PropertyAccessesMapTy PropertyAccesses = State->get();
513 for (ObjectPropPair PropKey : llvm::make_first_range(PropertyAccesses)) {
514 const MemRegion *ReceiverRegion = PropKey.first;
516 State = State->remove(PropKey);
517 }
518 }
519
520
521
522
523
525 return;
526 C.addTransition(State);
527}
528
529
530
531
534 return;
535
537 getTrackRegion(Event.Location, true);
538 if (!Region)
539 return;
540
542 const NullabilityState *TrackedNullability =
543 State->get(Region);
544
545 if (!TrackedNullability)
546 return;
547
548 if (ChecksEnabled[CK_NullableDereferenced] &&
549 TrackedNullability->getValue() == Nullability::Nullable) {
551
552
554 reportBug("Nullable pointer is dereferenced",
555 ErrorKind::NullableDereferenced, CK_NullableDereferenced,
557 else {
558 reportBug("Nullable pointer is passed to a callee that requires a "
559 "non-null",
560 ErrorKind::NullablePassedToNonnull, CK_NullableDereferenced,
562 }
563 }
564}
565
566void NullabilityChecker::checkBeginFunction(CheckerContext &C) const {
567 if (.inTopFrame())
568 return;
569
572 if (!AbstractCall || AbstractCall->parameters().empty())
573 return;
574
576 for (const ParmVarDecl *Param : AbstractCall->parameters()) {
578 continue;
579
582 if (RequiredNullability != Nullability::Nullable)
583 continue;
584
585 const VarRegion *ParamRegion = State->getRegion(Param, LCtx);
586 const MemRegion *ParamPointeeRegion =
587 State->getSVal(ParamRegion).getAsRegion();
588 if (!ParamPointeeRegion)
589 continue;
590
591 State = State->set(ParamPointeeRegion,
592 NullabilityState(RequiredNullability));
593 }
594 C.addTransition(State);
595}
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
611 const Stmt *S,
613
614
615
616 if (!IsLoad)
617 return;
618
619
620 const auto *Region =
621 dyn_cast_or_null(Location.getAsRegion());
622 if (!Region)
623 return;
624
626
627 auto StoredVal = State->getSVal(Region).getAs<loc::MemRegionVal>();
628 if (!StoredVal)
629 return;
630
631 Nullability NullabilityOfTheLoadedValue =
633
634 if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
635
636
637 if (ProgramStateRef NewState = State->assume(*StoredVal, true)) {
638 Context.addTransition(NewState);
639 }
640 }
641}
642
643
644
645
646
649}
650
651
652
653void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
655 auto RetExpr = S->getRetValue();
656 if (!RetExpr)
657 return;
658
660 return;
661
663 if (State->get())
664 return;
665
667 if (!RetSVal)
668 return;
669
670 bool InSuppressedMethodFamily = false;
671
674 C.getLocationContext()->getAnalysisDeclContext();
676 if (auto *MD = dyn_cast(D)) {
677
678
679
680
683 InSuppressedMethodFamily = true;
684
685 RequiredRetType = MD->getReturnType();
686 } else if (auto *FD = dyn_cast(D)) {
687 RequiredRetType = FD->getReturnType();
688 } else {
689 return;
690 }
691
693
695 if (const auto *FunDecl = C.getLocationContext()->getDecl();
696 FunDecl && FunDecl->getAttr() &&
697 (RequiredNullability == Nullability::Unspecified ||
698 RequiredNullability == Nullability::Nullable)) {
699
700
701 RequiredNullability = Nullability::Nonnull;
702 }
703
704
705
706
707
708
709 Nullability RetExprTypeLevelNullability =
711
712 bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
714 if (ChecksEnabled[CK_NullReturnedFromNonnull] && NullReturnedFromNonNull &&
715 RetExprTypeLevelNullability != Nullability::Nonnull &&
716 !InSuppressedMethodFamily) {
718 ExplodedNode *N = C.generateErrorNode(State, &Tag);
719 if (!N)
720 return;
721
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 }
732
733
734
735 if (NullReturnedFromNonNull) {
736 State = State->set(true);
737 C.addTransition(State);
738 return;
739 }
740
741 const MemRegion *Region = getTrackRegion(*RetSVal);
742 if (!Region)
743 return;
744
745 const NullabilityState *TrackedNullability =
746 State->get(Region);
747 if (TrackedNullability) {
748 Nullability TrackedNullabValue = TrackedNullability->getValue();
749 if (ChecksEnabled[CK_NullableReturnedFromNonnull] &&
751 TrackedNullabValue == Nullability::Nullable &&
752 RequiredNullability == Nullability::Nonnull) {
754 ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
755
757 llvm::raw_svector_ostream OS(SBuf);
758 OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) <<
759 " that is expected to return a non-null value";
760
761 reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull,
762 CK_NullableReturnedFromNonnull, N, Region, C);
763 }
764 return;
765 }
766 if (RequiredNullability == Nullability::Nullable) {
767 State = State->set(Region,
768 NullabilityState(RequiredNullability,
769 S));
770 C.addTransition(State);
771 }
772}
773
774
775
776void NullabilityChecker::checkPreCall(const CallEvent &Call,
778 if (.getDecl())
779 return;
780
782 if (State->get())
783 return;
784
786
787 unsigned Idx = 0;
789 if (Param->isParameterPack())
790 break;
791
792 if (Idx >= Call.getNumArgs())
793 break;
794
795 const Expr *ArgExpr = Call.getArgExpr(Idx);
797 if (!ArgSVal)
798 continue;
799
801 !Param->getType()->isReferenceType())
802 continue;
803
805
808 Nullability ArgExprTypeLevelNullability =
810
811 unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
812
813 if (ChecksEnabled[CK_NullPassedToNonnull] &&
815 ArgExprTypeLevelNullability != Nullability::Nonnull &&
816 RequiredNullability == Nullability::Nonnull &&
817 isDiagnosableCall(Call)) {
819 if (!N)
820 return;
821
823 llvm::raw_svector_ostream OS(SBuf);
824 OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null");
825 OS << " passed to a callee that requires a non-null " << ParamIdx
826 << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
827 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull,
828 CK_NullPassedToNonnull, N, nullptr, C, ArgExpr,
829 false);
830 return;
831 }
832
833 const MemRegion *Region = getTrackRegion(*ArgSVal);
834 if (!Region)
835 continue;
836
837 const NullabilityState *TrackedNullability =
838 State->get(Region);
839
840 if (TrackedNullability) {
842 TrackedNullability->getValue() != Nullability::Nullable)
843 continue;
844
845 if (ChecksEnabled[CK_NullablePassedToNonnull] &&
846 RequiredNullability == Nullability::Nonnull &&
847 isDiagnosableCall(Call)) {
850 llvm::raw_svector_ostream OS(SBuf);
851 OS << "Nullable pointer is passed to a callee that requires a non-null "
852 << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
853 reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull,
854 CK_NullablePassedToNonnull, N, Region, C,
855 ArgExpr, true);
856 return;
857 }
858 if (ChecksEnabled[CK_NullableDereferenced] &&
859 Param->getType()->isReferenceType()) {
861 reportBugIfInvariantHolds("Nullable pointer is dereferenced",
862 ErrorKind::NullableDereferenced,
863 CK_NullableDereferenced, N, Region, C,
864 ArgExpr, true);
865 return;
866 }
867 continue;
868 }
869 }
870 if (State != OrigState)
871 C.addTransition(State);
872}
873
874
875void NullabilityChecker::checkPostCall(const CallEvent &Call,
879 return;
880
882 return;
884 if (!FuncType)
885 return;
888 return;
890 if (State->get())
891 return;
892
893 const MemRegion *Region = getTrackRegion(Call.getReturnValue());
894 if (!Region)
895 return;
896
897
898
900 StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc()));
901 if (llvm::sys::path::filename(FilePath).starts_with("CG")) {
902 State = State->set(Region, Nullability::Contradicted);
903 C.addTransition(State);
904 return;
905 }
906
907 const NullabilityState *TrackedNullability =
908 State->get(Region);
909
910
911
912
913
914
915 if (const Expr *E = Call.getOriginExpr())
917
918 if (!TrackedNullability &&
920 State = State->set(Region, Nullability::Nullable);
921 C.addTransition(State);
922 }
923}
924
928
929
930 return Nullability::Nonnull;
931 }
932
935
936
939 return Nullability::Nonnull;
940 }
942 if (ValueRegionSVal) {
943 const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
944 assert(SelfRegion);
945
946 const NullabilityState *TrackedSelfNullability =
947 State->get(SelfRegion);
948 if (TrackedSelfNullability)
949 return TrackedSelfNullability->getValue();
950 }
951 return Nullability::Unspecified;
952}
953
954
955
956
957
959 bool Assumption) const {
960 PropertyAccessesMapTy PropertyAccesses = State->get();
961 for (auto [PropKey, PropVal] : PropertyAccesses) {
962 if (!PropVal.isConstrainedNonnull) {
964 if (IsNonNull.isConstrainedTrue()) {
965 ConstrainedPropertyVal Replacement = PropVal;
966 Replacement.isConstrainedNonnull = true;
967 State = State->set(PropKey, Replacement);
968 } else if (IsNonNull.isConstrainedFalse()) {
969
970 State = State->remove(PropKey);
971 }
972 }
973 }
974
975 return State;
976}
977
978
979
980
981void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
985 return;
988 return;
989
991 if (State->get())
992 return;
993
995 if (!ReturnRegion)
996 return;
997
1000
1001
1002
1003 if (Name.starts_with("NS")) {
1004
1005
1006
1007
1008
1009
1010
1011
1013 State =
1014 State->set(ReturnRegion, Nullability::Contradicted);
1015 C.addTransition(State);
1016 return;
1017 }
1018
1020 if (Name.contains("Array") &&
1021 (FirstSelectorSlot == "firstObject" ||
1022 FirstSelectorSlot == "lastObject")) {
1023 State =
1024 State->set(ReturnRegion, Nullability::Contradicted);
1025 C.addTransition(State);
1026 return;
1027 }
1028
1029
1030
1031
1032
1033 if (Name.contains("String")) {
1034 for (auto *Param : M.parameters()) {
1035 if (Param->getName() == "encoding") {
1036 State = State->set(ReturnRegion,
1037 Nullability::Contradicted);
1038 C.addTransition(State);
1039 return;
1040 }
1041 }
1042 }
1043 }
1044
1047
1048 const NullabilityState *NullabilityOfReturn =
1049 State->get(ReturnRegion);
1050
1051 if (NullabilityOfReturn) {
1052
1053
1054
1055 Nullability RetValTracked = NullabilityOfReturn->getValue();
1057 getMostNullable(RetValTracked, SelfNullability);
1058 if (ComputedNullab != RetValTracked &&
1059 ComputedNullab != Nullability::Unspecified) {
1060 const Stmt *NullabilitySource =
1061 ComputedNullab == RetValTracked
1062 ? NullabilityOfReturn->getNullabilitySource()
1063 : Message->getInstanceReceiver();
1064 State = State->set(
1065 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
1066 C.addTransition(State);
1067 }
1068 return;
1069 }
1070
1071
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089 if (RetNullability != Nullability::Nonnull &&
1091 bool LookupResolved = false;
1095 LookupResolved = true;
1096 ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident);
1097 const ConstrainedPropertyVal *PrevPropVal =
1098 State->get(Key);
1099 if (PrevPropVal && PrevPropVal->isConstrainedNonnull) {
1100 RetNullability = Nullability::Nonnull;
1101 } else {
1102
1103
1104
1105
1106
1107
1108 if (auto ReturnSVal =
1110 State = State->set(
1111 Key, ConstrainedPropertyVal(*ReturnSVal));
1112 }
1113 }
1114 }
1115 }
1116
1117 if (!LookupResolved) {
1118
1119 RetNullability = Nullability::Nonnull;
1120 }
1121 }
1122
1123 Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
1124 if (ComputedNullab == Nullability::Nullable) {
1125 const Stmt *NullabilitySource = ComputedNullab == RetNullability
1126 ? Message
1127 : Message->getInstanceReceiver();
1128 State = State->set(
1129 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
1130 C.addTransition(State);
1131 }
1132}
1133
1134
1135
1136
1137
1138void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
1143 return;
1145 return;
1146
1148 if (State->get())
1149 return;
1150
1152
1153
1154
1155 if (DestNullability == Nullability::Unspecified)
1156 return;
1157
1159 const MemRegion *Region = getTrackRegion(*RegionSVal);
1160 if (!Region)
1161 return;
1162
1163
1164 if (DestNullability == Nullability::Nonnull) {
1167 State = State->set(Region, Nullability::Contradicted);
1168 C.addTransition(State);
1169 return;
1170 }
1171 }
1172
1173 const NullabilityState *TrackedNullability =
1174 State->get(Region);
1175
1176 if (!TrackedNullability) {
1177 if (DestNullability != Nullability::Nullable)
1178 return;
1179 State = State->set(Region,
1180 NullabilityState(DestNullability, CE));
1181 C.addTransition(State);
1182 return;
1183 }
1184
1185 if (TrackedNullability->getValue() != DestNullability &&
1186 TrackedNullability->getValue() != Nullability::Contradicted) {
1187 State = State->set(Region, Nullability::Contradicted);
1188 C.addTransition(State);
1189 }
1190}
1191
1192
1193
1195
1196 if (auto *BinOp = dyn_cast(S)) {
1197 if (BinOp->getOpcode() == BO_Assign)
1198 return BinOp->getRHS();
1199 }
1200
1201
1202 if (auto *DS = dyn_cast(S)) {
1203 if (DS->isSingleDecl()) {
1204 auto *VD = dyn_cast(DS->getSingleDecl());
1205 if (!VD)
1206 return nullptr;
1207
1208 if (const Expr *Init = VD->getInit())
1209 return Init;
1210 }
1211 }
1212
1213 return nullptr;
1214}
1215
1216
1217
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233 if (.getASTContext().getLangOpts().ObjCAutoRefCount)
1234 return false;
1235
1236 auto *DS = dyn_cast(S);
1237 if (!DS || !DS->isSingleDecl())
1238 return false;
1239
1240 auto *VD = dyn_cast(DS->getSingleDecl());
1241 if (!VD)
1242 return false;
1243
1244
1245 if(!VD->getType().getQualifiers().hasObjCLifetime())
1246 return false;
1247
1248 const Expr *Init = VD->getInit();
1249 assert(Init && "ObjC local under ARC without initializer");
1250
1251
1252 if (!isa(Init))
1253 return false;
1254
1255 return true;
1256}
1257
1258
1259
1260void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1263 dyn_cast_or_null(L.getAsRegion());
1264 if (!TVR)
1265 return;
1266
1269 return;
1270
1272 if (State->get())
1273 return;
1274
1276 if (!ValDefOrUnknown)
1277 return;
1278
1280
1281 Nullability ValNullability = Nullability::Unspecified;
1282 if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
1284
1286
1287
1288
1289 Nullability ValueExprTypeLevelNullability = Nullability::Unspecified;
1291 if (ValueExpr) {
1292 ValueExprTypeLevelNullability =
1294 }
1295
1296 bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
1298 if (ChecksEnabled[CK_NullPassedToNonnull] && NullAssignedToNonNull &&
1299 ValNullability != Nullability::Nonnull &&
1300 ValueExprTypeLevelNullability != Nullability::Nonnull &&
1303 ExplodedNode *N = C.generateErrorNode(State, &Tag);
1304 if (!N)
1305 return;
1306
1307
1309 if (ValueExpr)
1311
1313 llvm::raw_svector_ostream OS(SBuf);
1315 OS << " assigned to a pointer which is expected to have non-null value";
1316 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull,
1317 CK_NullPassedToNonnull, N, nullptr, C, ValueStmt);
1318 return;
1319 }
1320
1321
1322
1323 if (NullAssignedToNonNull) {
1324 State = State->set(true);
1325 C.addTransition(State);
1326 return;
1327 }
1328
1329
1330
1331
1332 const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
1333 if (!ValueRegion)
1334 return;
1335
1336 const NullabilityState *TrackedNullability =
1337 State->get(ValueRegion);
1338
1339 if (TrackedNullability) {
1341 TrackedNullability->getValue() != Nullability::Nullable)
1342 return;
1343 if (ChecksEnabled[CK_NullablePassedToNonnull] &&
1344 LocNullability == Nullability::Nonnull) {
1346 ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
1347 reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer "
1348 "which is expected to have non-null value",
1349 ErrorKind::NullableAssignedToNonnull,
1350 CK_NullablePassedToNonnull, N, ValueRegion, C);
1351 }
1352 return;
1353 }
1354
1355 const auto *BinOp = dyn_cast(S);
1356
1357 if (ValNullability == Nullability::Nullable) {
1358
1359
1360 const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S;
1361 State = State->set(
1362 ValueRegion, NullabilityState(ValNullability, NullabilitySource));
1363 C.addTransition(State);
1364 return;
1365 }
1366
1367 if (LocNullability == Nullability::Nullable) {
1368 const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S;
1369 State = State->set(
1370 ValueRegion, NullabilityState(LocNullability, NullabilitySource));
1371 C.addTransition(State);
1372 }
1373}
1374
1375void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
1376 const char *NL, const char *Sep) const {
1377
1378 NullabilityMapTy B = State->get();
1379
1380 if (State->get())
1381 Out << Sep << NL
1382 << "Nullability invariant was violated, warnings suppressed." << NL;
1383
1384 if (B.isEmpty())
1385 return;
1386
1387 if (!State->get())
1388 Out << Sep << NL;
1389
1390 for (auto [Region, State] : B) {
1391 Out << Region << " : ";
1392 State.print(Out);
1393 Out << NL;
1394 }
1395}
1396
1397void ento::registerNullabilityBase(CheckerManager &mgr) {
1399}
1400
1401bool 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(); \
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); \
1416 } \
1417 \
1418 bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \
1419 return true; \
1420 }
1421
1422
1423
1424
1425
1428
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx, QualType Ty)
static bool isValidPointerType(QualType T)
static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, ProgramStateRef State)
#define REGISTER_CHECKER(name, trackingRequired)
static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S)
Returns true if.
static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, SVal LV, QualType T)
Returns true when the value stored at the given location has been constrained to null after being pas...
static const Expr * matchValueExprForBind(const Stmt *S)
For a given statement performing a bind, attempt to syntactically match the expression resulting in t...
static bool checkSelfIvarsForInvariantViolation(ProgramStateRef State, const LocationContext *LocCtxt)
static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, CheckerContext &C)
static const Expr * lookThroughImplicitCasts(const Expr *E)
Find the outermost subexpression of E that is not an implicit cast.
static bool checkParamsForPreconditionViolation(ArrayRef< ParmVarDecl * > Params, ProgramStateRef State, const LocationContext *LocCtxt)
static Nullability getReceiverNullability(const ObjCMethodCall &M, ProgramStateRef State)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
const Decl * getDecl() const
static std::optional< AnyCall > forDecl(const Decl *D)
If D is a callable (Objective-C method or a function), return a constructed AnyCall object.
Decl - This represents one declaration (or definition), e.g.
const FunctionType * getFunctionType(bool BlocksToo=true) const
Looks through the Decl's underlying type to extract a FunctionType when possible.
SourceLocation getBeginLoc() const LLVM_READONLY
ExplicitCastExpr - An explicit cast written in the source code.
This represents one expression.
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
FunctionType - C99 6.7.5.3 - Function Declarators.
QualType getReturnType() const
One of these records is kept for each identifier that is lexed.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
const ImplicitParamDecl * getSelfDecl() const
Represents an ObjC class declaration.
An expression that sends a message to the given Objective-C object or class.
Represents a pointer to an Objective C object.
ObjCInterfaceDecl * getInterfaceDecl() const
If this pointer points to an Objective @interface type, gets the declaration for that interface.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Represents a parameter to a function.
A (possibly-)qualified type.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
const IdentifierInfo * getIdentifierInfoForSlot(unsigned argIndex) const
Retrieve the identifier at a given position in the selector.
This class handles loading and caching of source files into memory.
Stmt - This represents one statement.
bool isBlockPointerType() const
bool isObjCObjectPointerType() const
bool isAnyPointerType() const
Represents a statement that could possibly have a value and type.
const SourceManager & getSourceManager() const
BugReporterVisitors are used to add custom diagnostics along a path.
BugReporter is a utility class for generating PathDiagnostics for analysis.
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
Represents an abstract call to a function or method along a particular path.
SVal getReturnValue() const
Returns the return value of the call.
virtual void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const
See CheckerManager::runCheckersForPrintState.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
ElementRegion is used to represent both array elements and casts.
const ProgramStateRef & getState() const
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
const RegionTy * getAs() const
Represents any expression that calls an Objective-C method.
const ObjCMethodDecl * getDecl() const override
Returns the declaration of the function or method that will be called.
bool isInstanceMessage() const
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
ArrayRef< ParmVarDecl * > parameters() const override
Return call's formal parameters.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
Selector getSelector() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
QualType getType(const ASTContext &) const
Try to get a reasonable type for the given value.
const MemRegion * getAsRegion() const
SubRegion - A region that subsets another larger region.
A class responsible for cleaning up unused symbols.
bool isDead(SymbolRef sym)
Returns whether or not a symbol has been confirmed dead.
bool isLiveRegion(const MemRegion *region)
SymbolicRegion - A special, "non-concrete" region.
TypedValueRegion - An abstract class representing regions having a typed value.
virtual QualType getValueType() const =0
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
const char *const MemoryError
Nullability getNullabilityAnnotation(QualType Type)
Get nullability annotation for a given type.
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
bool IsNonNull(InterpState &S, CodePtr OpPC)
The JSON file list parser is used to communicate input to InstallAPI.
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
ObjCMethodFamily
A family of Objective-C methods.
const FunctionProtoType * T
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
We dereferenced a location that may be null.