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.