clang: lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

33#include "llvm/ADT/StringRef.h"

34#include "llvm/Support/ErrorHandling.h"

35#include

36#include

37

40

41

42

43template <class... NameTypes>

45 llvm::StringRef Name,

46 NameTypes... Names) {

49 return false;

50

51 if constexpr (sizeof...(NameTypes) > 0) {

53 return false;

54 if (const auto *NextNS = dyn_cast_or_null(NS.getParent()))

56 return false;

57 } else {

59 }

60}

61

64 return false;

65

66 if (RD.getName() == "optional") {

67 if (const auto *N = dyn_cast_or_null(RD.getDeclContext()))

68 return N->isStdNamespace() ||

71 return false;

72 }

73

74 if (RD.getName() == "Optional") {

75

76 const auto *N = dyn_cast_or_null(RD.getDeclContext());

79 }

80

81 if (RD.getName() == "NullableValue") {

82 const auto *N = dyn_cast_or_null(RD.getDeclContext());

83 return N != nullptr &&

85 }

86

87 return false;

88}

89

91 if (RD == nullptr)

92 return nullptr;

94 return RD;

95

97 return nullptr;

98

102 return BaseClass;

103

104 return nullptr;

105}

106

112

113namespace {

114

116

117using LatticeTransferState = TransferState;

118

120

121AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {

123}

124

125auto desugarsToOptionalType() {

126 return hasUnqualifiedDesugaredType(

128}

129

130auto desugarsToOptionalOrDerivedType() {

131 return hasUnqualifiedDesugaredType(

133}

134

135auto hasOptionalType() { return hasType(desugarsToOptionalType()); }

136

137

138

139auto hasOptionalOrDerivedType() {

140 return hasType(desugarsToOptionalOrDerivedType());

141}

142

143QualType getPublicType(const Expr *E) {

144 auto *Cast = dyn_cast(E->IgnoreParens());

145 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {

146 QualType Ty = E->getType();

147 if (Ty->isPointerType())

148 return Ty->getPointeeType();

149 return Ty;

150 }

151

152

153

154

156

157

158

159 const CXXBaseSpecifier *PublicBase = nullptr;

160 for (const CXXBaseSpecifier *Base : Cast->path()) {

161 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)

162 break;

163 PublicBase = Base;

164 CastingFromThis = false;

165 }

166

167 if (PublicBase != nullptr)

168 return PublicBase->getType();

169

170

171

172

173 return getPublicType(Cast->getSubExpr());

174}

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {

201 return getPublicType(MCE.getImplicitObjectArgument());

202}

203

204AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,

205 ast_matchers::internal::Matcher, InnerMatcher) {

206 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);

207}

208

209auto isOptionalMemberCallWithNameMatcher(

210 ast_matchers::internal::Matcher matcher,

211 const std::optional &Ignorable = std::nullopt) {

214 publicReceiverType(desugarsToOptionalType()),

216}

217

218auto isOptionalOperatorCallWithName(

219 llvm::StringRef operator_name,

220 const std::optional &Ignorable = std::nullopt) {

225}

226

227auto isMakeOptionalCall() {

230 "std::make_optional", "base::make_optional", "absl::make_optional",

231 "folly::make_optional", "bsl::make_optional"))),

232 hasOptionalType());

233}

234

235auto nulloptTypeDecl() {

237 "base::nullopt_t", "folly::None",

238 "bsl::nullopt_t"));

239}

240

241auto hasNulloptType() { return hasType(nulloptTypeDecl()); }

242

243auto inPlaceClass() {

245 "base::in_place_t", "folly::in_place_t",

246 "bsl::in_place_t"));

247}

248

249auto isOptionalNulloptConstructor() {

252 hasParameter(0, hasNulloptType()))),

253 hasOptionalOrDerivedType());

254}

255

256auto isOptionalInPlaceConstructor() {

257 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),

258 hasOptionalOrDerivedType());

259}

260

261auto isOptionalValueOrConversionConstructor() {

265 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),

266 hasOptionalOrDerivedType());

267}

268

269auto isOptionalValueOrConversionAssignment() {

272 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),

274 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),

275 argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));

276}

277

278auto isOptionalNulloptAssignment() {

281 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),

282 argumentCountIs(2), hasArgument(1, hasNulloptType()));

283}

284

285auto isStdSwapCall() {

287 argumentCountIs(2),

288 hasArgument(0, hasOptionalOrDerivedType()),

289 hasArgument(1, hasOptionalOrDerivedType()));

290}

291

292auto isStdForwardCall() {

294 argumentCountIs(1),

295 hasArgument(0, hasOptionalOrDerivedType()));

296}

297

298constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";

299

300auto isValueOrStringEmptyCall() {

301

304 onImplicitObjectArgument(ignoringImplicit(

307 ofClass(optionalClass()))),

309 .bind(ValueOrCallID))));

310}

311

312auto isValueOrNotEqX() {

313 auto ComparesToSame = [](ast_matchers::internal::Matcher Arg) {

314 return hasOperands(

315 ignoringImplicit(

318 ofClass(optionalClass()))),

319 hasArgument(0, Arg))

320 .bind(ValueOrCallID)),

321 ignoringImplicit(Arg));

322 };

323

324

325

326

327

328

333}

334

335auto isZeroParamConstMemberCall() {

337 callee(cxxMethodDecl(parameterCountIs(0), isConst())));

338}

339

340auto isZeroParamConstMemberOperatorCall() {

342 callee(cxxMethodDecl(parameterCountIs(0), isConst())));

343}

344

345auto isNonConstMemberCall() {

347}

348

349auto isNonConstMemberOperatorCall() {

351}

352

353auto isCallReturningOptional() {

355 anyOf(desugarsToOptionalOrDerivedType(),

356 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));

357}

358

359template <typename L, typename R>

360auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {

363 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),

364 hasArgument(1, rhs_arg_matcher));

365}

366

367

370 if (Value != nullptr)

371 return Value->formula();

372

373 Value = &Env.makeAtomicBoolValue();

374 Env.setValue(Expr, *Value);

375 return Value->formula();

376}

377

379 return OptionalLoc.getSyntheticField("has_value");

380}

381

383 return OptionalLoc.getSyntheticField("value");

384}

385

386

387

390 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);

391}

392

393

394

396 if (OptionalLoc == nullptr)

397 return nullptr;

398 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);

399 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);

400 if (HasValueVal == nullptr) {

401 HasValueVal = &Env.makeAtomicBoolValue();

402 Env.setValue(HasValueLoc, *HasValueVal);

403 }

404 return HasValueVal;

405}

406

407QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {

409 return CTSD.getTemplateArgs()[0].getAsType();

410}

411

412

413

414

415

416int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {

417 const CXXRecordDecl *Optional =

420 return 0;

421 return 1 + countOptionalWrappers(

422 ASTCtx,

423 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));

424}

425

426StorageLocation *getLocBehindPossiblePointer(const Expr &E,

428 if (E.isPRValue()) {

429 if (auto *PointerVal = dyn_cast_or_null(Env.getValue(E)))

430 return &PointerVal->getPointeeLoc();

431 return nullptr;

432 }

433 return Env.getStorageLocation(E);

434}

435

436void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,

437 LatticeTransferState &State) {

438 if (auto *OptionalLoc = cast_or_null(

439 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {

440 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)

441 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));

442 }

443}

444

445void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,

446 LatticeTransferState &State) {

447 if (auto *OptionalLoc = cast_or_null(

448 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))

449 State.Env.setValue(

450 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));

451}

452

453void transferMakeOptionalCall(const CallExpr *E,

454 const MatchFinder::MatchResult &,

455 LatticeTransferState &State) {

456 setHasValue(State.Env.getResultObjectLocation(*E),

457 State.Env.getBoolLiteralValue(true), State.Env);

458}

459

460void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,

461 const MatchFinder::MatchResult &,

462 LatticeTransferState &State) {

463 if (auto *HasValueVal = getHasValue(

465 State.Env.setValue(*CallExpr, *HasValueVal);

466 }

467}

468

469void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,

470 const MatchFinder::MatchResult &,

471 LatticeTransferState &State) {

472 if (auto *HasValueVal = getHasValue(

474 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));

475 }

476}

477

478

479

480void transferValueOrImpl(

481 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,

482 LatticeTransferState &State,

484 const Formula &HasValueVal)) {

485 auto &Env = State.Env;

486

487 const auto *MCE =

488 Result.Nodes.getNodeAsclang::CXXMemberCallExpr(ValueOrCallID);

489

490 auto *HasValueVal =

492 if (HasValueVal == nullptr)

493 return;

494

495 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),

496 HasValueVal->formula()));

497}

498

499void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,

500 const MatchFinder::MatchResult &Result,

501 LatticeTransferState &State) {

502 return transferValueOrImpl(ComparisonExpr, Result, State,

505 auto &A = Env.arena();

506

507

508

509

510

511

512 return A.makeImplies(A.makeNot(ExprVal),

513 HasValueVal);

514 });

515}

516

517void transferValueOrNotEqX(const Expr *ComparisonExpr,

518 const MatchFinder::MatchResult &Result,

519 LatticeTransferState &State) {

520 transferValueOrImpl(ComparisonExpr, Result, State,

523 auto &A = Env.arena();

524

525

526

527 return A.makeImplies(ExprVal, HasValueVal);

528 });

529}

530

531void transferCallReturningOptional(const CallExpr *E,

532 const MatchFinder::MatchResult &Result,

533 LatticeTransferState &State) {

535 if (E->isPRValue()) {

536 Loc = &State.Env.getResultObjectLocation(*E);

537 } else {

539 if (Loc == nullptr) {

541 State.Env.setStorageLocation(*E, *Loc);

542 }

543 }

544

545 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)

546 return;

547

548 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);

549}

550

551

552

553

554bool handleConstMemberCall(const CallExpr *CE,

555 dataflow::RecordStorageLocation *RecordLoc,

556 const MatchFinder::MatchResult &Result,

557 LatticeTransferState &State) {

559 return false;

560

561

562 if (CE->isGLValue()) {

563 const FunctionDecl *DirectCallee = CE->getDirectCallee();

564 if (DirectCallee == nullptr)

565 return false;

566

567

568

569

573 State.Env.makeAtomicBoolValue(), State.Env);

574 };

576 State.Lattice.getOrCreateConstMethodReturnStorageLocation(

578

579 State.Env.setStorageLocation(*CE, Loc);

580 return true;

581 }

582

583 if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {

584

585 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,

586 State.Env);

587 if (Val == nullptr)

588 return false;

589 State.Env.setValue(*CE, *Val);

590 return true;

591 }

593

594 const FunctionDecl *DirectCallee = CE->getDirectCallee();

595 if (DirectCallee == nullptr)

596 return false;

598 State.Lattice.getOrCreateConstMethodReturnStorageLocation(

600 setHasValue(cast(Loc),

601 State.Env.makeAtomicBoolValue(), State.Env);

602 });

603

604

605 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);

607 return true;

608 }

609

610 return false;

611}

612

613void handleConstMemberCallWithFallbacks(

614 const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc,

615 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {

617 return;

618

619

621 transferCallReturningOptional(CE, Result, State);

622}

623

624void transferConstMemberCall(const CXXMemberCallExpr *MCE,

625 const MatchFinder::MatchResult &Result,

626 LatticeTransferState &State) {

627 handleConstMemberCallWithFallbacks(

629}

630

631void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE,

632 const MatchFinder::MatchResult &Result,

633 LatticeTransferState &State) {

634 auto *RecordLoc = cast_or_nulldataflow::RecordStorageLocation(

635 State.Env.getStorageLocation(*OCE->getArg(0)));

636 handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State);

637}

638

639void handleNonConstMemberCall(const CallExpr *CE,

640 dataflow::RecordStorageLocation *RecordLoc,

641 const MatchFinder::MatchResult &Result,

642 LatticeTransferState &State) {

644

645

646

647 for (const auto &[Field, FieldLoc] : RecordLoc->children()) {

648 QualType FieldType = Field->getType();

649 if (!FieldType.isConstQualified() &&

651 auto *FieldRecordLoc = cast_or_null(FieldLoc);

652 if (FieldRecordLoc) {

653 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),

654 State.Env);

655 }

656 }

657 }

658 State.Lattice.clearConstMethodReturnValues(*RecordLoc);

659 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);

660 }

661

662

664 transferCallReturningOptional(CE, Result, State);

665 }

666}

667

668void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,

669 const MatchFinder::MatchResult &Result,

670 LatticeTransferState &State) {

671 handleNonConstMemberCall(

673}

674

675void transferValue_NonConstMemberOperatorCall(

676 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,

677 LatticeTransferState &State) {

678 auto *RecordLoc = cast_or_nulldataflow::RecordStorageLocation(

679 State.Env.getStorageLocation(*OCE->getArg(0)));

681}

682

683void constructOptionalValue(const Expr &E, Environment &Env,

686 setHasValue(Loc, HasValueVal, Env);

687}

688

689

690

691

692BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,

693 const MatchFinder::MatchResult &MatchRes,

694 LatticeTransferState &State) {

695 const int DestTypeOptionalWrappersCount =

696 countOptionalWrappers(*MatchRes.Context, DestType);

697 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(

698 *MatchRes.Context, E.getType().getNonReferenceType());

699

700

701

702

703

704

705 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)

706 return State.Env.getBoolLiteralValue(true);

707

708

709

710

711

713 if (auto *HasValueVal = getHasValue(State.Env, Loc))

714 return *HasValueVal;

715 return State.Env.makeAtomicBoolValue();

716}

717

718void transferValueOrConversionConstructor(

719 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,

720 LatticeTransferState &State) {

721 assert(E->getNumArgs() > 0);

722

723 constructOptionalValue(

724 *E, State.Env,

725 valueOrConversionHasValue(

726 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),

727 MatchRes, State));

728}

729

730void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,

731 LatticeTransferState &State) {

732 assert(E->getNumArgs() > 0);

733

735 setHasValue(*Loc, HasValueVal, State.Env);

736

737

738 State.Env.setStorageLocation(*E, *Loc);

739 }

740}

741

742void transferValueOrConversionAssignment(

743 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,

744 LatticeTransferState &State) {

745 assert(E->getNumArgs() > 1);

746 transferAssignment(

747 E,

748 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),

749 *E->getArg(1), MatchRes, State),

750 State);

751}

752

753void transferNulloptAssignment(const CXXOperatorCallExpr *E,

754 const MatchFinder::MatchResult &,

755 LatticeTransferState &State) {

756 transferAssignment(E, State.Env.getBoolLiteralValue(false), State);

757}

758

761

762

763

764

765 if (Loc1 == nullptr) {

766 if (Loc2 != nullptr)

767 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);

768 return;

769 }

770 if (Loc2 == nullptr) {

771 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);

772 return;

773 }

774

775

776

777

778

779

780

781 BoolValue *BoolVal1 = getHasValue(Env, Loc1);

782 if (BoolVal1 == nullptr)

783 BoolVal1 = &Env.makeAtomicBoolValue();

784

785 BoolValue *BoolVal2 = getHasValue(Env, Loc2);

786 if (BoolVal2 == nullptr)

787 BoolVal2 = &Env.makeAtomicBoolValue();

788

789 setHasValue(*Loc1, *BoolVal2, Env);

790 setHasValue(*Loc2, *BoolVal1, Env);

791}

792

793void transferSwapCall(const CXXMemberCallExpr *E,

794 const MatchFinder::MatchResult &,

795 LatticeTransferState &State) {

796 assert(E->getNumArgs() == 1);

799}

800

801void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,

802 LatticeTransferState &State) {

803 assert(E->getNumArgs() == 2);

806 transferSwap(Arg0Loc, Arg1Loc, State.Env);

807}

808

809void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,

810 LatticeTransferState &State) {

811 assert(E->getNumArgs() == 1);

812

813 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))

814 State.Env.setStorageLocation(*E, *Loc);

815}

816

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833 return A.makeAnd(

834 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),

835 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),

836 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));

837}

838

839void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,

840 const MatchFinder::MatchResult &,

841 LatticeTransferState &State) {

843 auto &A = Env.arena();

844 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);

846 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {

848 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {

849 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)

850 CmpValue = &A.makeNot(*CmpValue);

851 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),

852 RHasVal->formula()));

853 }

854 }

855}

856

857void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,

858 const clang::Expr *E, Environment &Env) {

859 auto &A = Env.arena();

860 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);

862 if (auto *HasVal = getHasValue(Env, Loc)) {

863 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)

864 CmpValue = &A.makeNot(*CmpValue);

865 Env.assume(

866 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));

867 }

868}

869

870void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,

871 const clang::Expr *E, Environment &Env) {

872 auto &A = Env.arena();

873 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);

875 if (auto *HasVal = getHasValue(Env, Loc)) {

876 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)

877 CmpValue = &A.makeNot(*CmpValue);

878 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),

879 A.makeLiteral(false)));

880 }

881}

882

883std::optional

885 if (Options.IgnoreSmartPointerDereference) {

888 unless(hasArgument(0, expr(hasOptionalType()))))));

890 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));

891 }

892 return std::nullopt;

893}

894

896valueCall(const std::optional &IgnorableOptional) {

897 return isOptionalMemberCallWithNameMatcher(hasName("value"),

898 IgnorableOptional);

899}

900

902valueOperatorCall(const std::optional &IgnorableOptional) {

903 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),

904 isOptionalOperatorCallWithName("->", IgnorableOptional)));

905}

906

908

909

910

912

913 .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall)

914

915

916 .CaseOfCFGStmt(

917 isOptionalInPlaceConstructor(),

918 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,

919 LatticeTransferState &State) {

920 constructOptionalValue(*E, State.Env,

921 State.Env.getBoolLiteralValue(true));

922 })

923

924 .CaseOfCFGStmt(

925 isOptionalNulloptConstructor(),

926 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,

927 LatticeTransferState &State) {

928 constructOptionalValue(*E, State.Env,

929 State.Env.getBoolLiteralValue(false));

930 })

931

932 .CaseOfCFGStmt(isOptionalValueOrConversionConstructor(),

933 transferValueOrConversionConstructor)

934

935

937 isOptionalValueOrConversionAssignment(),

938 transferValueOrConversionAssignment)

939 .CaseOfCFGStmt(isOptionalNulloptAssignment(),

940 transferNulloptAssignment)

941

942

944 valueCall(std::nullopt),

945 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,

947 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);

948 })

949

950

951 .CaseOfCFGStmt(isOptionalOperatorCallWithName("*"),

952 [](const CallExpr *E,

953 const MatchFinder::MatchResult &,

954 LatticeTransferState &State) {

955 transferUnwrapCall(E, E->getArg(0), State);

956 })

957

958

959 .CaseOfCFGStmt(isOptionalOperatorCallWithName("->"),

960 [](const CallExpr *E,

961 const MatchFinder::MatchResult &,

962 LatticeTransferState &State) {

963 transferArrowOpCall(E, E->getArg(0), State);

964 })

965

966

967

968

969 .CaseOfCFGStmt(

970 isOptionalMemberCallWithNameMatcher(

971 hasAnyName("has_value", "hasValue")),

972 transferOptionalHasValueCall)

973

974

976 isOptionalMemberCallWithNameMatcher(hasName("operator bool")),

977 transferOptionalHasValueCall)

978

979

980

981 .CaseOfCFGStmt(

982 isOptionalMemberCallWithNameMatcher(hasName("isNull")),

983 transferOptionalIsNullCall)

984

985

986

987

989 isOptionalMemberCallWithNameMatcher(

990 hasAnyName("makeValue", "makeValueInplace")),

991 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,

995 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);

996 }

997 })

998

999

1000 .CaseOfCFGStmt(

1001 isOptionalMemberCallWithNameMatcher(hasName("emplace")),

1002 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,

1003 LatticeTransferState &State) {

1006 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);

1007 }

1008 })

1009

1010

1011 .CaseOfCFGStmt(

1012 isOptionalMemberCallWithNameMatcher(hasName("reset")),

1013 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,

1014 LatticeTransferState &State) {

1017 setHasValue(*Loc, State.Env.getBoolLiteralValue(false),

1018 State.Env);

1019 }

1020 })

1021

1022

1023 .CaseOfCFGStmt(

1024 isOptionalMemberCallWithNameMatcher(hasName("swap")),

1025 transferSwapCall)

1026

1027

1028 .CaseOfCFGStmt(isStdSwapCall(), transferStdSwapCall)

1029

1030

1031 .CaseOfCFGStmt(isStdForwardCall(), transferStdForwardCall)

1032

1033

1034 .CaseOfCFGStmt(isValueOrStringEmptyCall(),

1035 transferValueOrStringEmptyCall)

1036

1037

1038 .CaseOfCFGStmt(isValueOrNotEqX(), transferValueOrNotEqX)

1039

1040

1042 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),

1043 transferOptionalAndOptionalCmp)

1044 .CaseOfCFGStmt(

1045 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),

1046 [](const clang::CXXOperatorCallExpr *Cmp,

1047 const MatchFinder::MatchResult &, LatticeTransferState &State) {

1048 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);

1049 })

1050 .CaseOfCFGStmt(

1051 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),

1052 [](const clang::CXXOperatorCallExpr *Cmp,

1053 const MatchFinder::MatchResult &, LatticeTransferState &State) {

1054 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);

1055 })

1056 .CaseOfCFGStmt(

1057 isComparisonOperatorCall(

1058 hasOptionalType(),

1059 unless(anyOf(hasOptionalType(), hasNulloptType()))),

1060 [](const clang::CXXOperatorCallExpr *Cmp,

1061 const MatchFinder::MatchResult &, LatticeTransferState &State) {

1062 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);

1063 })

1064 .CaseOfCFGStmt(

1065 isComparisonOperatorCall(

1066 unless(anyOf(hasOptionalType(), hasNulloptType())),

1067 hasOptionalType()),

1068 [](const clang::CXXOperatorCallExpr *Cmp,

1069 const MatchFinder::MatchResult &, LatticeTransferState &State) {

1070 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);

1071 })

1072

1073

1074

1075

1076 .CaseOfCFGStmt(

1078 [](const CXXOperatorCallExpr *E,

1079 const MatchFinder::MatchResult &Result,

1080 LatticeTransferState &State) {

1082 E,

1083 dyn_cast_or_null(

1084 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),

1086 })

1087 .CaseOfCFGStmt(

1089 [](const CXXOperatorCallExpr *E,

1090 const MatchFinder::MatchResult &Result,

1091 LatticeTransferState &State) {

1093 E,

1094 dyn_cast_or_null(

1095 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),

1097 })

1098 .CaseOfCFGStmt(

1100 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,

1101 LatticeTransferState &State) {

1105 })

1106 .CaseOfCFGStmt(

1108 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,

1109 LatticeTransferState &State) {

1113 })

1114

1115

1116 .CaseOfCFGStmt(isZeroParamConstMemberCall(),

1117 transferConstMemberCall)

1118 .CaseOfCFGStmt(isZeroParamConstMemberOperatorCall(),

1119 transferConstMemberOperatorCall)

1120

1121 .CaseOfCFGStmt(isNonConstMemberCall(),

1122 transferValue_NonConstMemberCall)

1124 isNonConstMemberOperatorCall(),

1125 transferValue_NonConstMemberOperatorCall)

1126

1127

1128 .CaseOfCFGStmt(isCallReturningOptional(),

1129 transferCallReturningOptional)

1130

1132}

1133

1134llvm::SmallVector

1135diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) {

1136 if (auto *OptionalLoc = cast_or_null(

1137 getLocBehindPossiblePointer(*ObjectExpr, Env))) {

1138 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));

1139 if (auto *HasValueVal = cast_or_null(Prop)) {

1140 if (Env.proves(HasValueVal->formula()))

1141 return {};

1142 }

1143 }

1144

1145

1146

1149}

1150

1151auto buildDiagnoseMatchSwitch(

1153

1154

1155

1156 const auto IgnorableOptional = ignorableOptional(Options);

1157

1158 auto DiagBuilder =

1161 llvm::SmallVector>()

1162

1163 .CaseOfCFGStmt(

1164 valueOperatorCall(IgnorableOptional),

1165 [](const CallExpr *E, const MatchFinder::MatchResult &,

1167 return diagnoseUnwrapCall(E->getArg(0), Env);

1168 });

1169

1170 auto Builder = Options.IgnoreValueCalls

1171 ? std::move(DiagBuilder)

1172 : std::move(DiagBuilder)

1173

1174 .CaseOfCFGStmt(

1175 valueCall(IgnorableOptional),

1176 [](const CXXMemberCallExpr *E,

1179 return diagnoseUnwrapCall(

1180 E->getImplicitObjectArgument(), Env);

1181 });

1182

1183 return std::move(Builder).Build();

1184}

1185

1186}

1187

1192

1197 TransferMatchSwitch(buildTransferMatchSwitch()) {

1199 [&Ctx](QualType Ty) -> llvm::StringMap {

1203 return {};

1204 return {{"value", valueTypeFromOptionalDecl(*Optional)},

1205 {"has_value", Ctx.BoolTy}};

1206 });

1207}

1208

1212 LatticeTransferState State(L, Env);

1213 TransferMatchSwitch(Elt, getASTContext(), State);

1214}

1215

1218 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}

1219

1220}

1221}

Defines the clang::ASTContext interface.

#define AST_MATCHER(Type, DefineMatcher)

AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...

#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)

AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...

Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....

Defines the clang::Expr interface and subclasses for C++ expressions.

llvm::MachO::RecordLoc RecordLoc

Defines an enumeration for C++ overloaded operators.

MatchFinder::MatchResult MatchResult

Defines the clang::SourceLocation class and associated facilities.

C Language Family Type Representation.

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

Represents a top-level expression in a basic block.

Represents a base class of a C++ class.

OverloadedOperatorKind getOperator() const

Returns the kind of overloaded operator that this expression refers to.

Represents a C++ struct/union/class.

bool hasDefinition() const

Expr * getArg(unsigned Arg)

getArg - Return the specified argument.

static CharSourceRange getTokenRange(SourceRange R)

DeclContext * getParent()

getParent - Returns the containing DeclContext.

bool isTranslationUnit() const

DeclContext * getDeclContext()

bool isIdentifier() const

Predicate functions for querying what type of name this is.

StringRef getName() const

Get the name of identifier for this declaration as a StringRef.

DeclarationName getDeclName() const

Get the actual, stored name of the declaration, which may be a special name.

Represent a C++ namespace.

A (possibly-)qualified type.

CXXRecordDecl * getAsCXXRecordDecl() const

Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...

The Arena owns the objects that model data within an analysis.

Collects cases of a "match switch": a collection of matchers paired with callbacks,...

CFGMatchSwitchBuilder && CaseOfCFGStmt(MatchSwitchMatcher< Stmt > M, MatchSwitchAction< NodeT, State, Result > A) &&

Registers an action A for CFGStmts that will be triggered by the match of the pattern M against the S...

CFGMatchSwitch< State, Result > Build() &&

ASTContext & getASTContext() final

DataflowAnalysis(ASTContext &Context)

Holds the state of the program (store and heap) at a given program point.

DataflowAnalysisContext & getDataflowAnalysisContext() const

Returns the DataflowAnalysisContext used by the environment.

Models a symbolic pointer. Specifically, any value of type T*.

A storage location for a record (struct, class, or union).

Base class for elements of the local variable store and of the heap.

UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options={})

Definition UncheckedOptionalAccessModel.cpp:1216

UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env)

Definition UncheckedOptionalAccessModel.cpp:1193

void transfer(const CFGElement &Elt, UncheckedOptionalAccessLattice &L, Environment &Env)

Definition UncheckedOptionalAccessModel.cpp:1209

static ast_matchers::DeclarationMatcher optionalClassDecl()

Returns a matcher for the optional classes covered by this model.

Definition UncheckedOptionalAccessModel.cpp:1189

Base class for all values computed by abstract interpretation.

const internal::VariadicOperatorMatcherFunc< 1, 1 > unless

Matches if the provided matcher does not match.

internal::Matcher< Decl > DeclarationMatcher

Types of matchers for the top-level classes in the AST class hierarchy.

const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral

Matches string literals (also matches wide string literals).

const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr

Matches call expressions.

const internal::VariadicDynCastAllOfMatcher< Decl, NamedDecl > namedDecl

Matches a declaration of anything that could have a name.

internal::TrueMatcher anything()

Matches any node.

const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName

Matches NamedDecl nodes that have any of the specified names.

const internal::MapAnyOfMatcher< BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator > binaryOperation

Matches nodes which can be used with binary operators.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl

Matches C++ constructor declarations.

internal::Matcher< Stmt > StatementMatcher

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr

Matches constructor call expressions (including implicit ones).

const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl

Matches function declarations.

const AstTypeMatcher< RecordType > recordType

const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr

Matches member expressions.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl

Matches C++ class declarations.

const internal::VariadicDynCastAllOfMatcher< Stmt, IntegerLiteral > integerLiteral

Matches integer literals of all sizes / encodings, e.g.

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXNullPtrLiteralExpr > cxxNullPtrLiteralExpr

Matches nullptr literal.

internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)

Matches a node if the declaration associated with that node matches the given matcher.

const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr

Matches expressions.

const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf

Matches if any of the given matchers matches.

const internal::VariadicAllOfMatcher< QualType > qualType

Matches QualTypes in the clang AST.

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr

Matches implicit and explicit this expressions.

TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState

CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)

Dataflow Directional Tag Classes.

ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(clang::StringRef MethodName="value")

static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, llvm::StringRef Name, NameTypes... Names)

Definition UncheckedOptionalAccessModel.cpp:44

void transferSmartPointerLikeCachedDeref(const CallExpr *DerefExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)

A transfer function for operator* (and value) calls that can be cached.

static bool hasOptionalClassName(const CXXRecordDecl &RD)

Definition UncheckedOptionalAccessModel.cpp:62

void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env, QualType TypeToCopy=QualType())

Copies a record (struct, class, or union) from Src to Dst.

void transferSmartPointerLikeCachedGet(const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)

A transfer function for operator-> (and get) calls that can be cached.

internal::Matcher< NamedDecl > hasName(StringRef Name)

Matches NamedDecl nodes that have the specified name.

CachedConstAccessorsLattice< NoopLattice > UncheckedOptionalAccessLattice

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr

Matches member call expressions.

static bool isSupportedOptionalType(QualType Ty)

Definition UncheckedOptionalAccessModel.cpp:107

ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow()

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr

Matches overloaded operator calls.

internal::PolymorphicMatcher< internal::HasOverloadedOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl), std::vector< std::string > > hasOverloadedOperatorName(StringRef Name)

Matches overloaded operator names.

RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)

Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...

static const CXXRecordDecl * getOptionalBaseClass(const CXXRecordDecl *RD)

Definition UncheckedOptionalAccessModel.cpp:90

ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar()

const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl

Matches method declarations.

ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall(clang::StringRef MethodName="get")

const AstTypeMatcher< ReferenceType > referenceType

bool Cast(InterpState &S, CodePtr OpPC)

bool equals(const til::SExpr *E1, const til::SExpr *E2)

The JSON file list parser is used to communicate input to InstallAPI.

bool isa(CodeGen::Address addr)

@ Result

The result type of a method or function.

@ Type

The name was classified as a type.

U cast(CodeGen::Address addr)

Diagnostic information for an unchecked optional access.