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
72
73enum class ErrorKind : int {
74 NilAssignedToNonnull,
75 NilPassedToNonnull,
76 NilReturnedToNonnull,
77 NullableAssignedToNonnull,
78 NullableReturnedToNonnull,
79 NullableDereferenced,
80 NullablePassedToNonnull
81};
82
83class NullabilityChecker
85 check::Bind, check::PreCall, check::PreStmt,
86 check::PostCall, check::PostStmt,
87 check::PostObjCMessage, check::DeadSymbols, eval::Assume,
88 check::Location, check::Event,
89 check::BeginFunction> {
90
91public:
92
93
94
95
96
97
98 bool NoDiagnoseCallsToSystemHeaders = false;
99
100 void checkBind(SVal L, SVal V, const Stmt *S, bool AtDeclInit,
101 CheckerContext &C) const;
102 void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const;
103 void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
104 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
105 void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
106 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
107 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
108 void checkEvent(ImplicitNullDerefEvent Event) const;
109 void checkLocation(SVal Location, bool IsLoad, const Stmt *S,
110 CheckerContext &C) const;
111 void checkBeginFunction(CheckerContext &Ctx) const;
113 bool Assumption) const;
114
115 void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
116 const char *Sep) const override;
117
118 StringRef getDebugTag() const override { return "NullabilityChecker"; }
119
120
121
122
123 CheckerFrontendWithBugType NullPassedToNonnull{"Nullability",
125 CheckerFrontendWithBugType NullReturnedFromNonnull{"Nullability",
127 CheckerFrontendWithBugType NullableDereferenced{"Nullability",
129 CheckerFrontendWithBugType NullablePassedToNonnull{"Nullability",
131 CheckerFrontendWithBugType NullableReturnedFromNonnull{
133
134
135
136
137
138 bool NeedTracking = false;
139
140private:
141 class NullabilityBugVisitor : public BugReporterVisitor {
142 public:
143 NullabilityBugVisitor(const MemRegion *M) : Region(M) {}
144
145 void Profile(llvm::FoldingSetNodeID &ID) const override {
146 static int X = 0;
148 ID.AddPointer(Region);
149 }
150
152 BugReporterContext &BRC,
153 PathSensitiveBugReport &BR) override;
154
155 private:
156
157 const MemRegion *Region;
158 };
159
160
161
162
163
164
165 void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error,
166 const BugType &BT, ExplodedNode *N,
167 const MemRegion *Region, CheckerContext &C,
168 const Stmt *ValueExpr = nullptr,
169 bool SuppressPath = false) const;
170
171 void reportBug(StringRef Msg, ErrorKind Error, const BugType &BT,
172 ExplodedNode *N, const MemRegion *Region, BugReporter &BR,
173 const Stmt *ValueExpr = nullptr) const {
174 auto R = std::make_unique(BT, Msg, N);
175 if (Region) {
176 R->markInteresting(Region);
177 R->addVisitor(Region);
178 }
179 if (ValueExpr) {
180 R->addRange(ValueExpr->getSourceRange());
181 if (Error == ErrorKind::NilAssignedToNonnull ||
182 Error == ErrorKind::NilPassedToNonnull ||
183 Error == ErrorKind::NilReturnedToNonnull)
184 if (const auto *Ex = dyn_cast(ValueExpr))
186 }
188 }
189
190
191
192 const SymbolicRegion *getTrackRegion(SVal Val,
193 bool CheckSuperRegion = false) const;
194
195
196
197 bool isDiagnosableCall(const CallEvent &Call) const {
198 if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader())
199 return false;
200
201 return true;
202 }
203};
204
205class NullabilityState {
206public:
207 NullabilityState(Nullability Nullab, const Stmt *Source = nullptr)
208 : Nullab(Nullab), Source(Source) {}
209
210 const Stmt *getNullabilitySource() const { return Source; }
211
212 Nullability getValue() const { return Nullab; }
213
214 void Profile(llvm::FoldingSetNodeID &ID) const {
215 ID.AddInteger(static_cast<char>(Nullab));
216 ID.AddPointer(Source);
217 }
218
219 void print(raw_ostream &Out) const {
220 Out << getNullabilityString(Nullab) << "\n";
221 }
222
223private:
225
226
227
228
229 const Stmt *Source;
230};
231
232bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
233 return Lhs.getValue() == Rhs.getValue() &&
234 Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
235}
236
237
238
239
240using ObjectPropPair = std::pair<const MemRegion *, const IdentifierInfo *>;
241
242
243struct ConstrainedPropertyVal {
244
245
246 DefinedOrUnknownSVal Value;
247
248
249 bool isConstrainedNonnull;
250
251 ConstrainedPropertyVal(DefinedOrUnknownSVal SV)
252 : Value(SV), isConstrainedNonnull(false) {}
253
254 void Profile(llvm::FoldingSetNodeID &ID) const {
255 Value.Profile(ID);
256 ID.AddInteger(isConstrainedNonnull ? 1 : 0);
257 }
258};
259
260bool operator==(const ConstrainedPropertyVal &Lhs,
261 const ConstrainedPropertyVal &Rhs) {
262 return Lhs.Value == Rhs.Value &&
263 Lhs.isConstrainedNonnull == Rhs.isConstrainedNonnull;
264}
265
266}
267
269 NullabilityState)
271 ConstrainedPropertyVal)
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
298
300
310
312 return T->isAnyPointerType() || T->isBlockPointerType();
313}
314
316NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
317 if (!NeedTracking)
318 return nullptr;
319
320 auto RegionSVal = Val.getAsloc::MemRegionVal();
321 if (!RegionSVal)
322 return nullptr;
323
324 const MemRegion *Region = RegionSVal->getRegion();
325
326 if (CheckSuperRegion) {
327 if (const SubRegion *FieldReg = Region->getAs()) {
328 if (const auto *ER = dyn_cast(FieldReg->getSuperRegion()))
329 FieldReg = ER;
330 return dyn_cast(FieldReg->getSuperRegion());
331 }
332 if (auto ElementReg = Region->getAs())
333 return dyn_cast(ElementReg->getSuperRegion());
334 }
335
336 return dyn_cast(Region);
337}
338
340 const ExplodedNode *N, BugReporterContext &BRC,
341 PathSensitiveBugReport &BR) {
344
345 const NullabilityState *TrackedNullab = State->get(Region);
346 const NullabilityState *TrackedNullabPrev =
347 StatePrev->get(Region);
348 if (!TrackedNullab)
349 return nullptr;
350
351 if (TrackedNullabPrev &&
352 TrackedNullabPrev->getValue() == TrackedNullab->getValue())
353 return nullptr;
354
355
356 const Stmt *S = TrackedNullab->getNullabilitySource();
359 }
360
361 if (!S)
362 return nullptr;
363
364 std::string InfoText =
365 (llvm::Twine("Nullability '") +
366 getNullabilityString(TrackedNullab->getValue()) + "' is inferred")
367 .str();
368
369
372 return std::make_shared(Pos, InfoText, true);
373}
374
375
376
380 return false;
381
383 if (!RegionVal)
384 return false;
385
386
387
388
389
390
391
394 return false;
395
397 return true;
398
399 return false;
400}
401
402static bool
406 for (const auto *ParamDecl : Params) {
407 if (ParamDecl->isParameterPack())
408 break;
409
410 SVal LV = State->getLValue(ParamDecl, LocCtxt);
413 return true;
414 }
415 }
416 return false;
417}
418
419static bool
422 auto *MD = dyn_cast(LocCtxt->getDecl());
423 if (!MD || !MD->isInstanceMethod())
424 return false;
425
427 if (!SelfDecl)
428 return false;
429
430 SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt));
431
433 dyn_cast(SelfDecl->getType());
434 if (!SelfType)
435 return false;
436
438 if (!ID)
439 return false;
440
441 for (const auto *IvarDecl : ID->ivars()) {
442 SVal LV = State->getLValue(IvarDecl, SelfVal);
444 return true;
445 }
446 }
447 return false;
448}
449
452 if (State->get())
453 return true;
454
457 if (!D)
458 return false;
459
461 if (const auto *BD = dyn_cast(D))
462 Params = BD->parameters();
463 else if (const auto *FD = dyn_cast(D))
464 Params = FD->parameters();
465 else if (const auto *MD = dyn_cast(D))
466 Params = MD->parameters();
467 else
468 return false;
469
473 C.addTransition(State->set(true), N);
474 return true;
475 }
476 return false;
477}
478
479void NullabilityChecker::reportBugIfInvariantHolds(
480 StringRef Msg, ErrorKind Error, const BugType &BT, ExplodedNode *N,
481 const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr,
482 bool SuppressPath) const {
484
486 return;
487 if (SuppressPath) {
488 OriginalState = OriginalState->set(true);
489 N = C.addTransition(OriginalState, N);
490 }
491
492 reportBug(Msg, Error, BT, N, Region, C.getBugReporter(), ValueExpr);
493}
494
495
496void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
497 CheckerContext &C) const {
499 NullabilityMapTy Nullabilities = State->get();
500 for (const MemRegion *Reg : llvm::make_first_range(Nullabilities)) {
501 const auto *Region = Reg->getAs();
502 assert(Region && "Non-symbolic region is tracked.");
503 if (SR.isDead(Region->getSymbol())) {
504 State = State->remove(Reg);
505 }
506 }
507
508
509
510 PropertyAccessesMapTy PropertyAccesses = State->get();
511 for (ObjectPropPair PropKey : llvm::make_first_range(PropertyAccesses)) {
512 const MemRegion *ReceiverRegion = PropKey.first;
514 State = State->remove(PropKey);
515 }
516 }
517
518
519
520
521
523 return;
524 C.addTransition(State);
525}
526
527
528
529
530void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
532 return;
533
534 const MemRegion *Region =
535 getTrackRegion(Event.Location, true);
536 if (!Region)
537 return;
538
540 const NullabilityState *TrackedNullability =
541 State->get(Region);
542
543 if (!TrackedNullability)
544 return;
545
546 if (NullableDereferenced.isEnabled() &&
547 TrackedNullability->getValue() == Nullability::Nullable) {
548 BugReporter &BR = *Event.BR;
549
550
552 reportBug("Nullable pointer is dereferenced",
553 ErrorKind::NullableDereferenced, NullableDereferenced,
555 else {
556 reportBug("Nullable pointer is passed to a callee that requires a "
557 "non-null",
558 ErrorKind::NullablePassedToNonnull, NullableDereferenced,
560 }
561 }
562}
563
564void NullabilityChecker::checkBeginFunction(CheckerContext &C) const {
565 if (.inTopFrame())
566 return;
567
568 const LocationContext *LCtx = C.getLocationContext();
570 if (!AbstractCall || AbstractCall->parameters().empty())
571 return;
572
574 for (const ParmVarDecl *Param : AbstractCall->parameters()) {
576 continue;
577
580 if (RequiredNullability != Nullability::Nullable)
581 continue;
582
583 const VarRegion *ParamRegion = State->getRegion(Param, LCtx);
584 const MemRegion *ParamPointeeRegion =
585 State->getSVal(ParamRegion).getAsRegion();
586 if (!ParamPointeeRegion)
587 continue;
588
589 State = State->set(ParamPointeeRegion,
590 NullabilityState(RequiredNullability));
591 }
592 C.addTransition(State);
593}
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608void NullabilityChecker::checkLocation(SVal Location, bool IsLoad,
609 const Stmt *S,
610 CheckerContext &Context) const {
611
612
613
614 if (!IsLoad)
615 return;
616
617
618 const auto *Region =
619 dyn_cast_or_null(Location.getAsRegion());
620 if (!Region)
621 return;
622
624
625 auto StoredVal = State->getSVal(Region).getAsloc::MemRegionVal();
626 if (!StoredVal)
627 return;
628
629 Nullability NullabilityOfTheLoadedValue =
631
632 if (NullabilityOfTheLoadedValue == Nullability::Nonnull) {
633
634
635 if (ProgramStateRef NewState = State->assume(*StoredVal, true)) {
636 Context.addTransition(NewState);
637 }
638 }
639}
640
641
642
643
644
648
649
650
651void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
652 CheckerContext &C) const {
654 if (!RetExpr)
655 return;
656
658 return;
659
661 if (State->get())
662 return;
663
664 auto RetSVal = C.getSVal(S).getAs();
665 if (!RetSVal)
666 return;
667
668 bool InSuppressedMethodFamily = false;
669
670 QualType RequiredRetType;
671 AnalysisDeclContext *DeclCtxt =
672 C.getLocationContext()->getAnalysisDeclContext();
674 if (auto *MD = dyn_cast(D)) {
675
676
677
678
681 InSuppressedMethodFamily = true;
682
683 RequiredRetType = MD->getReturnType();
684 } else if (auto *FD = dyn_cast(D)) {
685 RequiredRetType = FD->getReturnType();
686 } else {
687 return;
688 }
689
691
693 if (const auto *FunDecl = C.getLocationContext()->getDecl();
694 FunDecl && FunDecl->getAttr() &&
695 (RequiredNullability == Nullability::Unspecified ||
696 RequiredNullability == Nullability::Nullable)) {
697
698
699 RequiredNullability = Nullability::Nonnull;
700 }
701
702
703
704
705
706
707 Nullability RetExprTypeLevelNullability =
709
710 if (RequiredNullability == Nullability::Nonnull &&
712 if (NullReturnedFromNonnull.isEnabled() &&
713 RetExprTypeLevelNullability != Nullability::Nonnull &&
714 !InSuppressedMethodFamily) {
715 ExplodedNode *N = C.generateErrorNode(State);
716 if (!N)
717 return;
718
719 SmallString<256> SBuf;
720 llvm::raw_svector_ostream OS(SBuf);
721 OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null");
722 OS << " returned from a " << C.getDeclDescription(D)
723 << " that is expected to return a non-null value";
724 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilReturnedToNonnull,
725 NullReturnedFromNonnull, N, nullptr, C,
726 RetExpr);
727 return;
728 }
729
730
731
732 State = State->set(true);
733 C.addTransition(State);
734 return;
735 }
736
737 const MemRegion *Region = getTrackRegion(*RetSVal);
738 if (!Region)
739 return;
740
741 const NullabilityState *TrackedNullability =
742 State->get(Region);
743 if (TrackedNullability) {
744 Nullability TrackedNullabValue = TrackedNullability->getValue();
745 if (NullableReturnedFromNonnull.isEnabled() &&
747 TrackedNullabValue == Nullability::Nullable &&
748 RequiredNullability == Nullability::Nonnull) {
749 ExplodedNode *N = C.addTransition(State, C.getPredecessor());
750
751 SmallString<256> SBuf;
752 llvm::raw_svector_ostream OS(SBuf);
753 OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) <<
754 " that is expected to return a non-null value";
755
756 reportBugIfInvariantHolds(OS.str(), ErrorKind::NullableReturnedToNonnull,
757 NullableReturnedFromNonnull, N, Region, C);
758 }
759 return;
760 }
761 if (RequiredNullability == Nullability::Nullable) {
762 State = State->set(Region,
763 NullabilityState(RequiredNullability,
764 S));
765 C.addTransition(State);
766 }
767}
768
769
770
771void NullabilityChecker::checkPreCall(const CallEvent &Call,
772 CheckerContext &C) const {
773 if (.getDecl())
774 return;
775
777 if (State->get())
778 return;
779
781
782 unsigned Idx = 0;
783 for (const ParmVarDecl *Param : Call.parameters()) {
784 if (Param->isParameterPack())
785 break;
786
787 if (Idx >= Call.getNumArgs())
788 break;
789
790 const Expr *ArgExpr = Call.getArgExpr(Idx);
791 auto ArgSVal = Call.getArgSVal(Idx++).getAs();
792 if (!ArgSVal)
793 continue;
794
796 !Param->getType()->isReferenceType())
797 continue;
798
800
803 Nullability ArgExprTypeLevelNullability =
805
806 unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
807
809 ArgExprTypeLevelNullability != Nullability::Nonnull &&
810 RequiredNullability == Nullability::Nonnull &&
811 isDiagnosableCall(Call)) {
812 ExplodedNode *N = C.generateErrorNode(State);
813 if (!N)
814 return;
815
816 SmallString<256> SBuf;
817 llvm::raw_svector_ostream OS(SBuf);
818 OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null");
819 OS << " passed to a callee that requires a non-null " << ParamIdx
820 << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
821 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull,
822 NullPassedToNonnull, N, nullptr, C, ArgExpr,
823 false);
824 return;
825 }
826
827 const MemRegion *Region = getTrackRegion(*ArgSVal);
828 if (!Region)
829 continue;
830
831 const NullabilityState *TrackedNullability =
832 State->get(Region);
833
834 if (TrackedNullability) {
836 TrackedNullability->getValue() != Nullability::Nullable)
837 continue;
838
839 if (NullablePassedToNonnull.isEnabled() &&
840 RequiredNullability == Nullability::Nonnull &&
841 isDiagnosableCall(Call)) {
842 ExplodedNode *N = C.addTransition(State);
843 SmallString<256> SBuf;
844 llvm::raw_svector_ostream OS(SBuf);
845 OS << "Nullable pointer is passed to a callee that requires a non-null "
846 << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
847 reportBugIfInvariantHolds(OS.str(), ErrorKind::NullablePassedToNonnull,
848 NullablePassedToNonnull, N, Region, C,
849 ArgExpr, true);
850 return;
851 }
852 if (NullableDereferenced.isEnabled() &&
853 Param->getType()->isReferenceType()) {
854 ExplodedNode *N = C.addTransition(State);
855 reportBugIfInvariantHolds(
856 "Nullable pointer is dereferenced", ErrorKind::NullableDereferenced,
857 NullableDereferenced, N, Region, C, ArgExpr, true);
858 return;
859 }
860 continue;
861 }
862 }
863 if (State != OrigState)
864 C.addTransition(State);
865}
866
867
868void NullabilityChecker::checkPostCall(const CallEvent &Call,
869 CheckerContext &C) const {
871 if (!Decl)
872 return;
873
875 return;
876 const FunctionType *FuncType = Decl->getFunctionType();
877 if (!FuncType)
878 return;
881 return;
883 if (State->get())
884 return;
885
886 const MemRegion *Region = getTrackRegion(Call.getReturnValue());
887 if (!Region)
888 return;
889
890
891
892 const SourceManager &SM = C.getSourceManager();
893 StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc()));
894 if (llvm::sys::path::filename(FilePath).starts_with("CG")) {
895 State = State->set(Region, Nullability::Contradicted);
896 C.addTransition(State);
897 return;
898 }
899
900 const NullabilityState *TrackedNullability =
901 State->get(Region);
902
903
904
905
906
907
908 if (const Expr *E = Call.getOriginExpr())
909 ReturnType = E->getType();
910
911 if (!TrackedNullability &&
913 State = State->set(Region, Nullability::Nullable);
914 C.addTransition(State);
915 }
916}
917
921
922
923 return Nullability::Nonnull;
924 }
925
928
929
932 return Nullability::Nonnull;
933 }
935 if (ValueRegionSVal) {
936 const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
937 assert(SelfRegion);
938
939 const NullabilityState *TrackedSelfNullability =
940 State->get(SelfRegion);
941 if (TrackedSelfNullability)
942 return TrackedSelfNullability->getValue();
943 }
944 return Nullability::Unspecified;
945}
946
947
948
949
950
952 bool Assumption) const {
953 PropertyAccessesMapTy PropertyAccesses = State->get();
954 for (auto [PropKey, PropVal] : PropertyAccesses) {
955 if (!PropVal.isConstrainedNonnull) {
956 ConditionTruthVal IsNonNull = State->isNonNull(PropVal.Value);
957 if (IsNonNull.isConstrainedTrue()) {
958 ConstrainedPropertyVal Replacement = PropVal;
959 Replacement.isConstrainedNonnull = true;
960 State = State->set(PropKey, Replacement);
961 } else if (IsNonNull.isConstrainedFalse()) {
962
963 State = State->remove(PropKey);
964 }
965 }
966 }
967
968 return State;
969}
970
971
972
973
974void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
975 CheckerContext &C) const {
977 if (!Decl)
978 return;
979 QualType RetType = Decl->getReturnType();
981 return;
982
984 if (State->get())
985 return;
986
987 const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue());
988 if (!ReturnRegion)
989 return;
990
993
994
995
996 if (Name.starts_with("NS")) {
997
998
999
1000
1001
1002
1003
1004
1006 State =
1007 State->set(ReturnRegion, Nullability::Contradicted);
1008 C.addTransition(State);
1009 return;
1010 }
1011
1013 if (Name.contains("Array") &&
1014 (FirstSelectorSlot == "firstObject" ||
1015 FirstSelectorSlot == "lastObject")) {
1016 State =
1017 State->set(ReturnRegion, Nullability::Contradicted);
1018 C.addTransition(State);
1019 return;
1020 }
1021
1022
1023
1024
1025
1026 if (Name.contains("String")) {
1027 for (auto *Param : M.parameters()) {
1028 if (Param->getName() == "encoding") {
1029 State = State->set(ReturnRegion,
1030 Nullability::Contradicted);
1031 C.addTransition(State);
1032 return;
1033 }
1034 }
1035 }
1036 }
1037
1040
1041 const NullabilityState *NullabilityOfReturn =
1042 State->get(ReturnRegion);
1043
1044 if (NullabilityOfReturn) {
1045
1046
1047
1048 Nullability RetValTracked = NullabilityOfReturn->getValue();
1050 getMostNullable(RetValTracked, SelfNullability);
1051 if (ComputedNullab != RetValTracked &&
1052 ComputedNullab != Nullability::Unspecified) {
1053 const Stmt *NullabilitySource =
1054 ComputedNullab == RetValTracked
1055 ? NullabilityOfReturn->getNullabilitySource()
1056 : Message->getInstanceReceiver();
1057 State = State->set(
1058 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
1059 C.addTransition(State);
1060 }
1061 return;
1062 }
1063
1064
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082 if (RetNullability != Nullability::Nonnull &&
1084 bool LookupResolved = false;
1085 if (const MemRegion *ReceiverRegion = getTrackRegion(M.getReceiverSVal())) {
1086 if (const IdentifierInfo *Ident =
1088 LookupResolved = true;
1089 ObjectPropPair Key = std::make_pair(ReceiverRegion, Ident);
1090 const ConstrainedPropertyVal *PrevPropVal =
1091 State->get(Key);
1092 if (PrevPropVal && PrevPropVal->isConstrainedNonnull) {
1093 RetNullability = Nullability::Nonnull;
1094 } else {
1095
1096
1097
1098
1099
1100
1101 if (auto ReturnSVal =
1103 State = State->set(
1104 Key, ConstrainedPropertyVal(*ReturnSVal));
1105 }
1106 }
1107 }
1108 }
1109
1110 if (!LookupResolved) {
1111
1112 RetNullability = Nullability::Nonnull;
1113 }
1114 }
1115
1116 Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
1117 if (ComputedNullab == Nullability::Nullable) {
1118 const Stmt *NullabilitySource = ComputedNullab == RetNullability
1120 : Message->getInstanceReceiver();
1121 State = State->set(
1122 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
1123 C.addTransition(State);
1124 }
1125}
1126
1127
1128
1129
1130
1131void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
1132 CheckerContext &C) const {
1134 QualType DestType = CE->getType();
1136 return;
1138 return;
1139
1141 if (State->get())
1142 return;
1143
1145
1146
1147
1148 if (DestNullability == Nullability::Unspecified)
1149 return;
1150
1151 auto RegionSVal = C.getSVal(CE).getAs();
1152 const MemRegion *Region = getTrackRegion(*RegionSVal);
1153 if (!Region)
1154 return;
1155
1156
1157 if (DestNullability == Nullability::Nonnull) {
1160 State = State->set(Region, Nullability::Contradicted);
1161 C.addTransition(State);
1162 return;
1163 }
1164 }
1165
1166 const NullabilityState *TrackedNullability =
1167 State->get(Region);
1168
1169 if (!TrackedNullability) {
1170 if (DestNullability != Nullability::Nullable)
1171 return;
1172 State = State->set(Region,
1173 NullabilityState(DestNullability, CE));
1174 C.addTransition(State);
1175 return;
1176 }
1177
1178 if (TrackedNullability->getValue() != DestNullability &&
1179 TrackedNullability->getValue() != Nullability::Contradicted) {
1180 State = State->set(Region, Nullability::Contradicted);
1181 C.addTransition(State);
1182 }
1183}
1184
1185
1186
1188
1189 if (auto *BinOp = dyn_cast(S)) {
1190 if (BinOp->getOpcode() == BO_Assign)
1191 return BinOp->getRHS();
1192 }
1193
1194
1195 if (auto *DS = dyn_cast(S)) {
1196 if (DS->isSingleDecl()) {
1197 auto *VD = dyn_cast(DS->getSingleDecl());
1198 if (!VD)
1199 return nullptr;
1200
1201 if (const Expr *Init = VD->getInit())
1202 return Init;
1203 }
1204 }
1205
1206 return nullptr;
1207}
1208
1209
1210
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226 if (.getASTContext().getLangOpts().ObjCAutoRefCount)
1227 return false;
1228
1229 auto *DS = dyn_cast(S);
1230 if (!DS || !DS->isSingleDecl())
1231 return false;
1232
1233 auto *VD = dyn_cast(DS->getSingleDecl());
1234 if (!VD)
1235 return false;
1236
1237
1238 if(!VD->getType().getQualifiers().hasObjCLifetime())
1239 return false;
1240
1241 const Expr *Init = VD->getInit();
1242 assert(Init && "ObjC local under ARC without initializer");
1243
1244
1246 return false;
1247
1248 return true;
1249}
1250
1251
1252
1253void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
1254 bool AtDeclInit, CheckerContext &C) const {
1255 const TypedValueRegion *TVR =
1256 dyn_cast_or_null(L.getAsRegion());
1257 if (!TVR)
1258 return;
1259
1262 return;
1263
1265 if (State->get())
1266 return;
1267
1268 auto ValDefOrUnknown = V.getAs();
1269 if (!ValDefOrUnknown)
1270 return;
1271
1273
1274 Nullability ValNullability = Nullability::Unspecified;
1275 if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
1277
1279
1280
1281
1282 Nullability ValueExprTypeLevelNullability = Nullability::Unspecified;
1284 if (ValueExpr) {
1285 ValueExprTypeLevelNullability =
1287 }
1288
1289 bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
1291 if (NullPassedToNonnull.isEnabled() && NullAssignedToNonNull &&
1292 ValNullability != Nullability::Nonnull &&
1293 ValueExprTypeLevelNullability != Nullability::Nonnull &&
1295 ExplodedNode *N = C.generateErrorNode(State);
1296 if (!N)
1297 return;
1298
1299
1300 const Stmt *ValueStmt = S;
1301 if (ValueExpr)
1302 ValueStmt = ValueExpr;
1303
1304 SmallString<256> SBuf;
1305 llvm::raw_svector_ostream OS(SBuf);
1307 OS << " assigned to a pointer which is expected to have non-null value";
1308 reportBugIfInvariantHolds(OS.str(), ErrorKind::NilAssignedToNonnull,
1309 NullPassedToNonnull, N, nullptr, C, ValueStmt);
1310 return;
1311 }
1312
1313
1314
1315 if (NullAssignedToNonNull) {
1316 State = State->set(true);
1317 C.addTransition(State);
1318 return;
1319 }
1320
1321
1322
1323
1324 const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
1325 if (!ValueRegion)
1326 return;
1327
1328 const NullabilityState *TrackedNullability =
1329 State->get(ValueRegion);
1330
1331 if (TrackedNullability) {
1333 TrackedNullability->getValue() != Nullability::Nullable)
1334 return;
1335 if (NullablePassedToNonnull.isEnabled() &&
1336 LocNullability == Nullability::Nonnull) {
1337 ExplodedNode *N = C.addTransition(State, C.getPredecessor());
1338 reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer "
1339 "which is expected to have non-null value",
1340 ErrorKind::NullableAssignedToNonnull,
1341 NullablePassedToNonnull, N, ValueRegion, C);
1342 }
1343 return;
1344 }
1345
1346 const auto *BinOp = dyn_cast(S);
1347
1348 if (ValNullability == Nullability::Nullable) {
1349
1350
1351 const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S;
1352 State = State->set(
1353 ValueRegion, NullabilityState(ValNullability, NullabilitySource));
1354 C.addTransition(State);
1355 return;
1356 }
1357
1358 if (LocNullability == Nullability::Nullable) {
1359 const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S;
1360 State = State->set(
1361 ValueRegion, NullabilityState(LocNullability, NullabilitySource));
1362 C.addTransition(State);
1363 }
1364}
1365
1366void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
1367 const char *NL, const char *Sep) const {
1368
1369 NullabilityMapTy B = State->get();
1370
1371 if (State->get())
1372 Out << Sep << NL
1373 << "Nullability invariant was violated, warnings suppressed." << NL;
1374
1375 if (B.isEmpty())
1376 return;
1377
1378 if (!State->get())
1379 Out << Sep << NL;
1380
1381 for (auto [Region, State] : B) {
1382 Out << Region << " : ";
1383 State.print(Out);
1384 Out << NL;
1385 }
1386}
1387
1388
1389
1390
1391
1392
1393
1394constexpr llvm::StringLiteral GroupName = "nullability";
1395constexpr llvm::StringLiteral GroupOptName = "NoDiagnoseCallsToSystemHeaders";
1396
1397#define REGISTER_CHECKER(NAME, TRACKING_REQUIRED) \
1398 void ento::register##NAME##Checker(CheckerManager &Mgr) { \
1399 NullabilityChecker *Chk = Mgr.getChecker(); \
1400 Chk->NAME.enable(Mgr); \
1401 Chk->NeedTracking = Chk->NeedTracking || TRACKING_REQUIRED; \
1402 Chk->NoDiagnoseCallsToSystemHeaders = \
1403 Mgr.getAnalyzerOptions().getCheckerBooleanOption(GroupName, \
1404 GroupOptName, true); \
1405 } \
1406 \
1407 bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \
1408 return true; \
1409 }
1410
1411
1412
1413
1414
1417
#define REGISTER_CHECKER(name)
TokenType getType() const
Returns the token's type, e.g.
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx, QualType Ty)
static bool isValidPointerType(QualType T)
Definition NullabilityChecker.cpp:311
static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, ProgramStateRef State)
Definition NullabilityChecker.cpp:301
static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S)
Returns true if.
Definition NullabilityChecker.cpp:1211
constexpr llvm::StringLiteral GroupOptName
Definition NullabilityChecker.cpp:1395
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...
Definition NullabilityChecker.cpp:377
static const Expr * matchValueExprForBind(const Stmt *S)
For a given statement performing a bind, attempt to syntactically match the expression resulting in t...
Definition NullabilityChecker.cpp:1187
NullConstraint
Definition NullabilityChecker.cpp:299
@ Unknown
Definition NullabilityChecker.cpp:299
@ IsNotNull
Definition NullabilityChecker.cpp:299
@ IsNull
Definition NullabilityChecker.cpp:299
static bool checkSelfIvarsForInvariantViolation(ProgramStateRef State, const LocationContext *LocCtxt)
Definition NullabilityChecker.cpp:420
static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, CheckerContext &C)
Definition NullabilityChecker.cpp:450
constexpr llvm::StringLiteral GroupName
Definition NullabilityChecker.cpp:1394
static const Expr * lookThroughImplicitCasts(const Expr *E)
Find the outermost subexpression of E that is not an implicit cast.
Definition NullabilityChecker.cpp:645
static bool checkParamsForPreconditionViolation(ArrayRef< ParmVarDecl * > Params, ProgramStateRef State, const LocationContext *LocCtxt)
Definition NullabilityChecker.cpp:403
static Nullability getReceiverNullability(const ObjCMethodCall &M, ProgramStateRef State)
Definition NullabilityChecker.cpp:918
ObjectPropPair
Definition NullabilityChecker.cpp:270
#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.
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.
This represents one expression.
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
QualType getReturnType() const
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.
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 (possibly-)qualified type.
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.
Stmt - This represents one statement.
SourceLocation getBeginLoc() const LLVM_READONLY
bool isObjCObjectPointerType() const
const SourceManager & getSourceManager() const
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
SVal getReturnValue() const
Returns the return value of the call.
Checker families (where a single backend class implements multiple related frontends) should derive f...
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'.
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
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.
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.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
bool IsNonNull(InterpState &S, CodePtr OpPC)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
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.