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