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/Casting.h"

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

36#include

37#include

38#include

39#include

40

42namespace dataflow {

43

44

45

46template <class... NameTypes>

48 llvm::StringRef Name,

49 NameTypes... Names) {

52 return false;

53

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

56 return false;

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

59 return false;

60 } else {

62 }

63}

64

67 return false;

68

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

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

71 return N->isStdNamespace() ||

74 return false;

75 }

76

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

78

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

82 }

83

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

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

86 return N != nullptr &&

88 }

89

90 return false;

91}

92

94 if (RD == nullptr)

95 return nullptr;

97 return RD;

98

100 return nullptr;

101

105 return BaseClass;

106

107 return nullptr;

108}

109

114}

115

116namespace {

117

119

120using LatticeTransferState = TransferState;

121

123

124AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {

126}

127

128auto desugarsToOptionalType() {

129 return hasUnqualifiedDesugaredType(

131}

132

133auto desugarsToOptionalOrDerivedType() {

134 return hasUnqualifiedDesugaredType(

136}

137

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

139

140

141

142auto hasOptionalOrDerivedType() {

143 return hasType(desugarsToOptionalOrDerivedType());

144}

145

146QualType getPublicType(const Expr *E) {

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

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

149 QualType Ty = E->getType();

150 if (Ty->isPointerType())

151 return Ty->getPointeeType();

152 return Ty;

153 }

154

155

156

157

158 bool CastingFromThis = isa(Cast->getSubExpr());

159

160

161

162 const CXXBaseSpecifier *PublicBase = nullptr;

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

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

165 break;

166 PublicBase = Base;

167 CastingFromThis = false;

168 }

169

170 if (PublicBase != nullptr)

171 return PublicBase->getType();

172

173

174

175

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

177}

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {

204 return getPublicType(MCE.getImplicitObjectArgument());

205}

206

207AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,

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

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

210}

211

212auto isOptionalMemberCallWithNameMatcher(

213 ast_matchers::internal::Matcher matcher,

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

217 publicReceiverType(desugarsToOptionalType()),

219}

220

221auto isOptionalOperatorCallWithName(

222 llvm::StringRef operator_name,

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

228}

229

230auto isMakeOptionalCall() {

233 "std::make_optional", "base::make_optional", "absl::make_optional",

234 "folly::make_optional", "bsl::make_optional"))),

235 hasOptionalType());

236}

237

238auto nulloptTypeDecl() {

240 "base::nullopt_t", "folly::None",

241 "bsl::nullopt_t"));

242}

243

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

245

246auto inPlaceClass() {

248 "base::in_place_t", "folly::in_place_t",

249 "bsl::in_place_t"));

250}

251

252auto isOptionalNulloptConstructor() {

255 hasParameter(0, hasNulloptType()))),

256 hasOptionalOrDerivedType());

257}

258

259auto isOptionalInPlaceConstructor() {

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

261 hasOptionalOrDerivedType());

262}

263

264auto isOptionalValueOrConversionConstructor() {

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

269 hasOptionalOrDerivedType());

270}

271

272auto isOptionalValueOrConversionAssignment() {

275 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),

277 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),

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

279}

280

281auto isOptionalNulloptAssignment() {

284 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),

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

286}

287

288auto isStdSwapCall() {

290 argumentCountIs(2),

291 hasArgument(0, hasOptionalOrDerivedType()),

292 hasArgument(1, hasOptionalOrDerivedType()));

293}

294

295auto isStdForwardCall() {

297 argumentCountIs(1),

298 hasArgument(0, hasOptionalOrDerivedType()));

299}

300

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

302

303auto isValueOrStringEmptyCall() {

304

307 onImplicitObjectArgument(ignoringImplicit(

310 ofClass(optionalClass()))),

312 .bind(ValueOrCallID))));

313}

314

315auto isValueOrNotEqX() {

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

317 return hasOperands(

318 ignoringImplicit(

321 ofClass(optionalClass()))),

322 hasArgument(0, Arg))

323 .bind(ValueOrCallID)),

324 ignoringImplicit(Arg));

325 };

326

327

328

329

330

331

336}

337

338auto isZeroParamConstMemberCall() {

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

341}

342

343auto isZeroParamConstMemberOperatorCall() {

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

346}

347

348auto isNonConstMemberCall() {

350}

351

352auto isNonConstMemberOperatorCall() {

354}

355

356auto isCallReturningOptional() {

358 anyOf(desugarsToOptionalOrDerivedType(),

359 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));

360}

361

362template <typename L, typename R>

363auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {

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

367 hasArgument(1, rhs_arg_matcher));

368}

369

370

371const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {

373 if (Value != nullptr)

374 return Value->formula();

375

378 return Value->formula();

379}

380

381StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {

382 return OptionalLoc.getSyntheticField("has_value");

383}

384

385StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {

386 return OptionalLoc.getSyntheticField("value");

387}

388

389

390

391void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,

392 Environment &Env) {

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

394}

395

396

397

398BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {

399 if (OptionalLoc == nullptr)

400 return nullptr;

401 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);

402 auto *HasValueVal = Env.get(HasValueLoc);

403 if (HasValueVal == nullptr) {

406 }

407 return HasValueVal;

408}

409

410QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {

411 auto &CTSD = cast(RD);

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

413}

414

415

416

417

418

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

420 const CXXRecordDecl *Optional =

423 return 0;

424 return 1 + countOptionalWrappers(

425 ASTCtx,

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

427}

428

429StorageLocation *getLocBehindPossiblePointer(const Expr &E,

430 const Environment &Env) {

431 if (E.isPRValue()) {

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

433 return &PointerVal->getPointeeLoc();

434 return nullptr;

435 }

437}

438

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

440 LatticeTransferState &State) {

441 if (auto *OptionalLoc = cast_or_null(

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

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

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

445 }

446}

447

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

449 LatticeTransferState &State) {

450 if (auto *OptionalLoc = cast_or_null(

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

452 State.Env.setValue(

453 *UnwrapExpr, State.Env.create(locForValue(*OptionalLoc)));

454}

455

456void transferMakeOptionalCall(const CallExpr *E,

457 const MatchFinder::MatchResult &,

458 LatticeTransferState &State) {

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

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

461}

462

463void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,

464 const MatchFinder::MatchResult &,

465 LatticeTransferState &State) {

466 if (auto *HasValueVal = getHasValue(

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

469 }

470}

471

472void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,

473 const MatchFinder::MatchResult &,

474 LatticeTransferState &State) {

475 if (auto *HasValueVal = getHasValue(

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

478 }

479}

480

481

482

483void transferValueOrImpl(

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

485 LatticeTransferState &State,

486 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,

487 const Formula &HasValueVal)) {

488 auto &Env = State.Env;

489

490 const auto *MCE =

492

493 auto *HasValueVal =

495 if (HasValueVal == nullptr)

496 return;

497

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

499 HasValueVal->formula()));

500}

501

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

503 const MatchFinder::MatchResult &Result,

504 LatticeTransferState &State) {

505 return transferValueOrImpl(ComparisonExpr, Result, State,

506 [](Environment &Env, const Formula &ExprVal,

507 const Formula &HasValueVal) -> const Formula & {

509

510

511

512

513

514

516 HasValueVal);

517 });

518}

519

520void transferValueOrNotEqX(const Expr *ComparisonExpr,

521 const MatchFinder::MatchResult &Result,

522 LatticeTransferState &State) {

523 transferValueOrImpl(ComparisonExpr, Result, State,

524 [](Environment &Env, const Formula &ExprVal,

525 const Formula &HasValueVal) -> const Formula & {

527

528

529

530 return A.makeImplies(ExprVal, HasValueVal);

531 });

532}

533

534void transferCallReturningOptional(const CallExpr *E,

535 const MatchFinder::MatchResult &Result,

536 LatticeTransferState &State) {

537 RecordStorageLocation *Loc = nullptr;

538 if (E->isPRValue()) {

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

540 } else {

541 Loc = State.Env.get(*E);

542 if (Loc == nullptr) {

543 Loc = &cast(State.Env.createStorageLocation(*E));

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

545 }

546 }

547

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

549 return;

550

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

552}

553

554void handleConstMemberCall(const CallExpr *CE,

555 dataflow::RecordStorageLocation *RecordLoc,

556 const MatchFinder::MatchResult &Result,

557 LatticeTransferState &State) {

558

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

561 if (DirectCallee == nullptr)

562 return;

563 StorageLocation &Loc =

564 State.Lattice.getOrCreateConstMethodReturnStorageLocation(

565 *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {

566 setHasValue(cast(Loc),

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

568 });

569 if (CE->isGLValue()) {

570

571

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

573 } else {

574

575

576

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

578 copyRecord(cast(Loc), ResultLoc, State.Env);

579 }

580 return;

581 }

582

583

584

586 (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) {

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

588 State.Env);

589 if (Val == nullptr)

590 return;

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

592 return;

593 }

594

595

596

598 transferCallReturningOptional(CE, Result, State);

599 }

600}

601

602void transferValue_ConstMemberCall(const CXXMemberCallExpr *MCE,

603 const MatchFinder::MatchResult &Result,

604 LatticeTransferState &State) {

605 handleConstMemberCall(

607}

608

609void transferValue_ConstMemberOperatorCall(

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

611 LatticeTransferState &State) {

612 auto *RecordLoc = cast_or_nulldataflow::RecordStorageLocation(

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

615}

616

617void handleNonConstMemberCall(const CallExpr *CE,

618 dataflow::RecordStorageLocation *RecordLoc,

619 const MatchFinder::MatchResult &Result,

620 LatticeTransferState &State) {

622

623

624

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

626 QualType FieldType = Field->getType();

627 if (!FieldType.isConstQualified() &&

629 auto *FieldRecordLoc = cast_or_null(FieldLoc);

630 if (FieldRecordLoc) {

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

632 State.Env);

633 }

634 }

635 }

636 State.Lattice.clearConstMethodReturnValues(*RecordLoc);

637 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);

638 }

639

640

642 transferCallReturningOptional(CE, Result, State);

643 }

644}

645

646void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,

647 const MatchFinder::MatchResult &Result,

648 LatticeTransferState &State) {

649 handleNonConstMemberCall(

651}

652

653void transferValue_NonConstMemberOperatorCall(

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

655 LatticeTransferState &State) {

656 auto *RecordLoc = cast_or_nulldataflow::RecordStorageLocation(

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

659}

660

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

662 BoolValue &HasValueVal) {

664 setHasValue(Loc, HasValueVal, Env);

665}

666

667

668

669

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

671 const MatchFinder::MatchResult &MatchRes,

672 LatticeTransferState &State) {

673 const int DestTypeOptionalWrappersCount =

674 countOptionalWrappers(*MatchRes.Context, DestType);

675 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(

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

677

678

679

680

681

682

683 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)

684 return State.Env.getBoolLiteralValue(true);

685

686

687

688

689

690 auto *Loc = State.Env.get(E);

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

692 return *HasValueVal;

693 return State.Env.makeAtomicBoolValue();

694}

695

696void transferValueOrConversionConstructor(

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

698 LatticeTransferState &State) {

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

700

701 constructOptionalValue(

702 *E, State.Env,

703 valueOrConversionHasValue(

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

705 MatchRes, State));

706}

707

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

709 LatticeTransferState &State) {

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

711

712 if (auto *Loc = State.Env.get(*E->getArg(0))) {

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

714

715

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

717 }

718}

719

720void transferValueOrConversionAssignment(

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

722 LatticeTransferState &State) {

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

724 transferAssignment(

725 E,

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

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

728 State);

729}

730

731void transferNulloptAssignment(const CXXOperatorCallExpr *E,

732 const MatchFinder::MatchResult &,

733 LatticeTransferState &State) {

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

735}

736

737void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,

738 Environment &Env) {

739

740

741

742

743 if (Loc1 == nullptr) {

744 if (Loc2 != nullptr)

746 return;

747 }

748 if (Loc2 == nullptr) {

750 return;

751 }

752

753

754

755

756

757

758

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

760 if (BoolVal1 == nullptr)

762

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

764 if (BoolVal2 == nullptr)

766

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

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

769}

770

771void transferSwapCall(const CXXMemberCallExpr *E,

772 const MatchFinder::MatchResult &,

773 LatticeTransferState &State) {

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

775 auto *OtherLoc = State.Env.get(*E->getArg(0));

777}

778

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

780 LatticeTransferState &State) {

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

782 auto *Arg0Loc = State.Env.get(*E->getArg(0));

783 auto *Arg1Loc = State.Env.get(*E->getArg(1));

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

785}

786

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

788 LatticeTransferState &State) {

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

790

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

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

793}

794

795const Formula &evaluateEquality(Arena &A, const Formula &EqVal,

796 const Formula &LHS, const Formula &RHS) {

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811 return A.makeAnd(

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

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

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

815}

816

818 const MatchFinder::MatchResult &,

819 LatticeTransferState &State) {

820 Environment &Env = State.Env;

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

823 auto *Arg0Loc = Env.get(*CmpExpr->getArg(0));

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

825 auto *Arg1Loc = Env.get(*CmpExpr->getArg(1));

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

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

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

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

830 RHasVal->formula()));

831 }

832 }

833}

834

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

839 auto *Loc = Env.get(*E);

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

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

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

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

845 }

846}

847

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

852 auto *Loc = Env.get(*E);

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

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

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

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

857 A.makeLiteral(false)));

858 }

859}

860

861std::optional

862ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {

863 if (Options.IgnoreSmartPointerDereference) {

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

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

869 }

870 return std::nullopt;

871}

872

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

875 return isOptionalMemberCallWithNameMatcher(hasName("value"),

876 IgnorableOptional);

877}

878

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

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

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

883}

884

885auto buildTransferMatchSwitch() {

886

887

888

889 return CFGMatchSwitchBuilder()

890

891 .CaseOfCFGStmt(isMakeOptionalCall(), transferMakeOptionalCall)

892

893

894 .CaseOfCFGStmt(

895 isOptionalInPlaceConstructor(),

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

897 LatticeTransferState &State) {

898 constructOptionalValue(*E, State.Env,

899 State.Env.getBoolLiteralValue(true));

900 })

901

902 .CaseOfCFGStmt(

903 isOptionalNulloptConstructor(),

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

905 LatticeTransferState &State) {

906 constructOptionalValue(*E, State.Env,

907 State.Env.getBoolLiteralValue(false));

908 })

909

910 .CaseOfCFGStmt(isOptionalValueOrConversionConstructor(),

911 transferValueOrConversionConstructor)

912

913

914 .CaseOfCFGStmt(

915 isOptionalValueOrConversionAssignment(),

916 transferValueOrConversionAssignment)

917 .CaseOfCFGStmt(isOptionalNulloptAssignment(),

918 transferNulloptAssignment)

919

920

921 .CaseOfCFGStmt(

922 valueCall(std::nullopt),

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

924 LatticeTransferState &State) {

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

926 })

927

928

929 .CaseOfCFGStmt(isOptionalOperatorCallWithName("*"),

930 [](const CallExpr *E,

931 const MatchFinder::MatchResult &,

932 LatticeTransferState &State) {

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

934 })

935

936

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

938 [](const CallExpr *E,

939 const MatchFinder::MatchResult &,

940 LatticeTransferState &State) {

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

942 })

943

944

945

946

947 .CaseOfCFGStmt(

948 isOptionalMemberCallWithNameMatcher(

949 hasAnyName("has_value", "hasValue")),

950 transferOptionalHasValueCall)

951

952

953 .CaseOfCFGStmt(

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

955 transferOptionalHasValueCall)

956

957

958

959 .CaseOfCFGStmt(

960 isOptionalMemberCallWithNameMatcher(hasName("isNull")),

961 transferOptionalIsNullCall)

962

963

964 .CaseOfCFGStmt(

965 isOptionalMemberCallWithNameMatcher(hasName("emplace")),

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

967 LatticeTransferState &State) {

968 if (RecordStorageLocation *Loc =

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

971 }

972 })

973

974

975 .CaseOfCFGStmt(

976 isOptionalMemberCallWithNameMatcher(hasName("reset")),

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

978 LatticeTransferState &State) {

979 if (RecordStorageLocation *Loc =

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

982 State.Env);

983 }

984 })

985

986

987 .CaseOfCFGStmt(

988 isOptionalMemberCallWithNameMatcher(hasName("swap")),

989 transferSwapCall)

990

991

992 .CaseOfCFGStmt(isStdSwapCall(), transferStdSwapCall)

993

994

995 .CaseOfCFGStmt(isStdForwardCall(), transferStdForwardCall)

996

997

998 .CaseOfCFGStmt(isValueOrStringEmptyCall(),

999 transferValueOrStringEmptyCall)

1000

1001

1002 .CaseOfCFGStmt(isValueOrNotEqX(), transferValueOrNotEqX)

1003

1004

1005 .CaseOfCFGStmt(

1006 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),

1007 transferOptionalAndOptionalCmp)

1008 .CaseOfCFGStmt(

1009 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),

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

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

1013 })

1014 .CaseOfCFGStmt(

1015 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),

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

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

1019 })

1020 .CaseOfCFGStmt(

1021 isComparisonOperatorCall(

1022 hasOptionalType(),

1023 unless(anyOf(hasOptionalType(), hasNulloptType()))),

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

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

1027 })

1028 .CaseOfCFGStmt(

1029 isComparisonOperatorCall(

1030 unless(anyOf(hasOptionalType(), hasNulloptType())),

1031 hasOptionalType()),

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

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

1035 })

1036

1037

1038

1039

1040 .CaseOfCFGStmt(

1042 [](const CXXOperatorCallExpr *E,

1043 const MatchFinder::MatchResult &Result,

1044 LatticeTransferState &State) {

1046 E,

1047 dyn_cast_or_null(

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

1049 State, [](StorageLocation &Loc) {});

1050 })

1051 .CaseOfCFGStmt(

1053 [](const CXXOperatorCallExpr *E,

1054 const MatchFinder::MatchResult &Result,

1055 LatticeTransferState &State) {

1057 E,

1058 dyn_cast_or_null(

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

1060 State, [](StorageLocation &Loc) {});

1061 })

1062 .CaseOfCFGStmt(

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

1065 LatticeTransferState &State) {

1068 [](StorageLocation &Loc) {});

1069 })

1070 .CaseOfCFGStmt(

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

1073 LatticeTransferState &State) {

1076 [](StorageLocation &Loc) {});

1077 })

1078

1079

1080 .CaseOfCFGStmt(isZeroParamConstMemberCall(),

1081 transferValue_ConstMemberCall)

1082 .CaseOfCFGStmt(isZeroParamConstMemberOperatorCall(),

1083 transferValue_ConstMemberOperatorCall)

1084

1085 .CaseOfCFGStmt(isNonConstMemberCall(),

1086 transferValue_NonConstMemberCall)

1087 .CaseOfCFGStmt(

1088 isNonConstMemberOperatorCall(),

1089 transferValue_NonConstMemberOperatorCall)

1090

1091

1092 .CaseOfCFGStmt(isCallReturningOptional(),

1093 transferCallReturningOptional)

1094

1095 .Build();

1096}

1097

1099 const Environment &Env) {

1100 if (auto *OptionalLoc = cast_or_null(

1101 getLocBehindPossiblePointer(*ObjectExpr, Env))) {

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

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

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

1105 return {};

1106 }

1107 }

1108

1109

1110

1111

1112 return {ObjectExpr->getBeginLoc()};

1113}

1114

1115auto buildDiagnoseMatchSwitch(

1116 const UncheckedOptionalAccessModelOptions &Options) {

1117

1118

1119

1120 auto IgnorableOptional = ignorableOptional(Options);

1121 return CFGMatchSwitchBuilder<const Environment,

1123

1124 .CaseOfCFGStmt(

1125 valueCall(IgnorableOptional),

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

1127 const Environment &Env) {

1128 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);

1129 })

1130

1131

1132 .CaseOfCFGStmt(valueOperatorCall(IgnorableOptional),

1133 [](const CallExpr *E,

1134 const MatchFinder::MatchResult &,

1135 const Environment &Env) {

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

1137 })

1138 .Build();

1139}

1140

1141}

1142

1146}

1147

1152 TransferMatchSwitch(buildTransferMatchSwitch()) {

1153 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(

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

1158 return {};

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

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

1161 });

1162}

1163

1167 LatticeTransferState State(L, Env);

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

1169}

1170

1173 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}

1174

1175}

1176}

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.

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.

Represents a call to a member function that may be written either with member call syntax (e....

A call to an overloaded operator written using operator syntax.

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.

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.

This represents one expression.

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...

const Formula & makeImplies(const Formula &LHS, const Formula &RHS)

Returns a formula for LHS => RHS.

A mixin for a lattice that additionally maintains a cache of stable method call return values to mode...

Base class template for dataflow analyses built on a single lattice type.

ASTContext & getASTContext() final

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

StorageLocation * getStorageLocation(const ValueDecl &D) const

Returns the storage location assigned to D in the environment, or null if D isn't assigned a storage ...

BoolValue & makeAtomicBoolValue() const

Returns an atomic boolean value.

bool proves(const Formula &) const

Returns true if the formula is always true when this point is reached.

Value * getValue(const StorageLocation &Loc) const

Returns the value assigned to Loc in the environment or null if Loc isn't assigned a value in the env...

void assume(const Formula &)

Record a fact that must be true if this point in the program is reached.

RecordStorageLocation & getResultObjectLocation(const Expr &RecordPRValue) const

Returns the location of the result object for a record-type prvalue.

void setValue(const StorageLocation &Loc, Value &Val)

Assigns Val as the value of Loc in the environment.

std::enable_if_t< std::is_base_of_v< StorageLocation, T >, T * > get(const ValueDecl &D) const

Returns the result of casting getStorageLocation(...) to a subclass of StorageLocation (using cast_or...

UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options={})

Dataflow analysis that models whether optionals hold values or not.

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

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

static ast_matchers::DeclarationMatcher optionalClassDecl()

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

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).

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

Matches NamedDecl nodes that have the specified name.

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< Stmt, CXXMemberCallExpr > cxxMemberCallExpr

Matches member call expressions.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl

Matches C++ constructor declarations.

internal::PolymorphicMatcher< internal::ValueEqualsMatcher, void(internal::AllNodeBaseTypes), ValueT > equals(const ValueT &Value)

Matches literals that are equal to the given value of type ValueT.

internal::Matcher< Stmt > StatementMatcher

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr

Matches constructor call expressions (including implicit ones).

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.

const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl

Matches function declarations.

const AstTypeMatcher< RecordType > recordType

Matches record types (e.g.

const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr

Matches member expressions.

const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl

Matches C++ class declarations.

const AstTypeMatcher< ReferenceType > referenceType

Matches both lvalue and rvalue reference types.

const internal::VariadicDynCastAllOfMatcher< Decl, RecordDecl > recordDecl

Matches class, struct, and union 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::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl

Matches method declarations.

const internal::VariadicAllOfMatcher< QualType > qualType

Matches QualTypes in the clang AST.

const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr

Matches implicit and explicit this expressions.

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

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)

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.

ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall()

ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall()

void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env)

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

static bool isSupportedOptionalType(QualType Ty)

ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow()

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)

ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar()

Matchers: For now, these match on any class with an operator* or operator-> where the return types ha...

bool Cast(InterpState &S, CodePtr OpPC)

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

@ Result

The result type of a method or function.