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;

147 ID.AddPointer(&X);

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