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;

148 ID.AddPointer(&X);

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 (D)

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 (C.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 (Call.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 (C.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.