31#include "llvm/ADT/StringRef.h"
32#include "llvm/Support/Casting.h"
33#include "llvm/Support/ErrorHandling.h"
44template <
class... NameTypes>
52 if constexpr (
sizeof...(NameTypes) > 0) {
55 if (
const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.
getParent()))
67 if (RD.
getName() ==
"optional") {
68 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext()))
69 return N->isStdNamespace() ||
75 if (RD.
getName() ==
"Optional") {
77 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
82 if (RD.
getName() ==
"NullableValue") {
83 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
84 return N !=
nullptr &&
118using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
122AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
126auto desugarsToOptionalType() {
127 return hasUnqualifiedDesugaredType(
131auto desugarsToOptionalOrDerivedType() {
132 return hasUnqualifiedDesugaredType(
136auto hasOptionalType() {
return hasType(desugarsToOptionalType()); }
140auto hasOptionalOrDerivedType() {
141 return hasType(desugarsToOptionalOrDerivedType());
144QualType getPublicType(
const Expr *
E) {
145 auto *
Cast = dyn_cast<ImplicitCastExpr>(
E->IgnoreParens());
146 if (Cast ==
nullptr ||
Cast->getCastKind() != CK_UncheckedDerivedToBase) {
147 QualType Ty =
E->getType();
148 if (Ty->isPointerType())
149 return Ty->getPointeeType();
156 bool CastingFromThis = isa<CXXThisExpr>(
Cast->getSubExpr());
160 const CXXBaseSpecifier *PublicBase =
nullptr;
161 for (
const CXXBaseSpecifier *
Base :
Cast->path()) {
162 if (
Base->getAccessSpecifier() !=
AS_public && !CastingFromThis)
165 CastingFromThis =
false;
168 if (PublicBase !=
nullptr)
169 return PublicBase->getType();
174 return getPublicType(
Cast->getSubExpr());
201QualType getPublicReceiverType(
const CXXMemberCallExpr &MCE) {
202 return getPublicType(MCE.getImplicitObjectArgument());
206 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
207 return InnerMatcher.matches(getPublicReceiverType(
Node), Finder, Builder);
210auto isOptionalMemberCallWithNameMatcher(
211 ast_matchers::internal::Matcher<NamedDecl> matcher,
212 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
215 publicReceiverType(desugarsToOptionalType()),
219auto isOptionalOperatorCallWithName(
220 llvm::StringRef operator_name,
221 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
228auto isMakeOptionalCall() {
231 "std::make_optional",
"base::make_optional",
"absl::make_optional",
232 "folly::make_optional",
"bsl::make_optional"))),
236auto nulloptTypeDecl() {
238 "base::nullopt_t",
"folly::None",
242auto hasNulloptType() {
return hasType(nulloptTypeDecl()); }
246 "base::in_place_t",
"folly::in_place_t",
250auto isOptionalNulloptConstructor() {
253 hasParameter(0, hasNulloptType()))),
254 hasOptionalOrDerivedType());
257auto isOptionalInPlaceConstructor() {
259 hasOptionalOrDerivedType());
262auto isOptionalValueOrConversionConstructor() {
266 argumentCountIs(1), hasArgument(0,
unless(hasNulloptType())),
267 hasOptionalOrDerivedType());
270auto isOptionalValueOrConversionAssignment() {
275 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
276 argumentCountIs(2), hasArgument(1,
unless(hasNulloptType())));
279auto isOptionalNulloptAssignment() {
283 argumentCountIs(2), hasArgument(1, hasNulloptType()));
286auto isStdSwapCall() {
289 hasArgument(0, hasOptionalOrDerivedType()),
290 hasArgument(1, hasOptionalOrDerivedType()));
293auto isStdForwardCall() {
296 hasArgument(0, hasOptionalOrDerivedType()));
299constexpr llvm::StringLiteral ValueOrCallID =
"ValueOrCall";
301auto isValueOrStringEmptyCall() {
305 onImplicitObjectArgument(ignoringImplicit(
308 ofClass(optionalClass()))),
310 .bind(ValueOrCallID))));
313auto isValueOrNotEqX() {
314 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
319 ofClass(optionalClass()))),
321 .bind(ValueOrCallID)),
322 ignoringImplicit(Arg));
336auto isZeroParamConstMemberCall() {
341auto isZeroParamConstMemberOperatorCall() {
346auto isNonConstMemberCall() {
350auto isNonConstMemberOperatorCall() {
354auto isCallReturningOptional() {
356 anyOf(desugarsToOptionalOrDerivedType(),
357 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
360template <
typename L,
typename R>
361auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
364 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
365 hasArgument(1, rhs_arg_matcher));
369const Formula &forceBoolValue(Environment &
Env,
const Expr &Expr) {
371 if (
Value !=
nullptr)
372 return Value->formula();
376 return Value->formula();
379StorageLocation &locForHasValue(
const RecordStorageLocation &OptionalLoc) {
380 return OptionalLoc.getSyntheticField(
"has_value");
383StorageLocation &locForValue(
const RecordStorageLocation &OptionalLoc) {
384 return OptionalLoc.getSyntheticField(
"value");
389void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
391 Env.
setValue(locForHasValue(OptionalLoc), HasValueVal);
396BoolValue *getHasValue(Environment &
Env, RecordStorageLocation *OptionalLoc) {
397 if (OptionalLoc ==
nullptr)
399 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
400 auto *HasValueVal =
Env.
get<BoolValue>(HasValueLoc);
401 if (HasValueVal ==
nullptr) {
408QualType valueTypeFromOptionalDecl(
const CXXRecordDecl &RD) {
409 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
410 return CTSD.getTemplateArgs()[0].getAsType();
417int countOptionalWrappers(
const ASTContext &ASTCtx, QualType Type) {
422 return 1 + countOptionalWrappers(
424 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
427StorageLocation *getLocBehindPossiblePointer(
const Expr &
E,
428 const Environment &
Env) {
430 if (
auto *PointerVal = dyn_cast_or_null<PointerValue>(
Env.
getValue(
E)))
431 return &PointerVal->getPointeeLoc();
437void transferUnwrapCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
438 LatticeTransferState &State) {
439 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
440 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
441 if (State.Env.getStorageLocation(*UnwrapExpr) ==
nullptr)
442 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
446void transferArrowOpCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
447 LatticeTransferState &State) {
448 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
449 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
451 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
454void transferMakeOptionalCall(
const CallExpr *
E,
455 const MatchFinder::MatchResult &,
456 LatticeTransferState &State) {
457 setHasValue(State.Env.getResultObjectLocation(*
E),
458 State.Env.getBoolLiteralValue(
true), State.Env);
461void transferOptionalHasValueCall(
const CXXMemberCallExpr *CallExpr,
462 const MatchFinder::MatchResult &,
463 LatticeTransferState &State) {
464 if (
auto *HasValueVal = getHasValue(
466 State.Env.setValue(*CallExpr, *HasValueVal);
470void transferOptionalIsNullCall(
const CXXMemberCallExpr *CallExpr,
471 const MatchFinder::MatchResult &,
472 LatticeTransferState &State) {
473 if (
auto *HasValueVal = getHasValue(
475 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
481void transferValueOrImpl(
483 LatticeTransferState &State,
484 const Formula &(*ModelPred)(Environment &
Env,
const Formula &ExprVal,
485 const Formula &HasValueVal)) {
486 auto &
Env = State.Env;
493 if (HasValueVal ==
nullptr)
497 HasValueVal->formula()));
500void transferValueOrStringEmptyCall(
const clang::Expr *ComparisonExpr,
501 const MatchFinder::MatchResult &
Result,
502 LatticeTransferState &State) {
503 return transferValueOrImpl(ComparisonExpr,
Result, State,
504 [](Environment &
Env,
const Formula &ExprVal,
505 const Formula &HasValueVal) ->
const Formula & {
518void transferValueOrNotEqX(
const Expr *ComparisonExpr,
519 const MatchFinder::MatchResult &
Result,
520 LatticeTransferState &State) {
521 transferValueOrImpl(ComparisonExpr,
Result, State,
522 [](Environment &
Env,
const Formula &ExprVal,
523 const Formula &HasValueVal) ->
const Formula & {
532void transferCallReturningOptional(
const CallExpr *
E,
533 const MatchFinder::MatchResult &
Result,
534 LatticeTransferState &State) {
535 RecordStorageLocation *
Loc =
nullptr;
536 if (
E->isPRValue()) {
537 Loc = &State.Env.getResultObjectLocation(*
E);
539 Loc = State.Env.get<RecordStorageLocation>(*E);
540 if (
Loc ==
nullptr) {
541 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*
E));
542 State.Env.setStorageLocation(*
E, *
Loc);
546 if (State.Env.getValue(locForHasValue(*
Loc)) !=
nullptr)
549 setHasValue(*
Loc, State.Env.makeAtomicBoolValue(), State.Env);
552void handleConstMemberCall(
const CallExpr *CE,
553 dataflow::RecordStorageLocation *
RecordLoc,
554 const MatchFinder::MatchResult &
Result,
555 LatticeTransferState &State) {
558 StorageLocation *
Loc =
559 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
561 setHasValue(cast<RecordStorageLocation>(Loc),
562 State.Env.makeAtomicBoolValue(), State.Env);
566 if (CE->isGLValue()) {
569 State.Env.setStorageLocation(*CE, *
Loc);
574 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
575 copyRecord(*cast<RecordStorageLocation>(
Loc), ResultLoc, State.Env);
583 (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) {
584 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*
RecordLoc, CE,
588 State.Env.setValue(*CE, *Val);
595 transferCallReturningOptional(CE,
Result, State);
599void transferValue_ConstMemberCall(
const CXXMemberCallExpr *MCE,
600 const MatchFinder::MatchResult &
Result,
601 LatticeTransferState &State) {
602 handleConstMemberCall(
606void transferValue_ConstMemberOperatorCall(
607 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
608 LatticeTransferState &State) {
609 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
610 State.Env.getStorageLocation(*OCE->getArg(0)));
614void handleNonConstMemberCall(
const CallExpr *CE,
615 dataflow::RecordStorageLocation *
RecordLoc,
616 const MatchFinder::MatchResult &
Result,
617 LatticeTransferState &State) {
622 for (
const auto &[Field, FieldLoc] :
RecordLoc->children()) {
623 QualType FieldType =
Field->getType();
624 if (!FieldType.isConstQualified() &&
626 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
627 if (FieldRecordLoc) {
628 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
633 State.Lattice.clearConstMethodReturnValues(*
RecordLoc);
634 State.Lattice.clearConstMethodReturnStorageLocations(*
RecordLoc);
639 transferCallReturningOptional(CE,
Result, State);
643void transferValue_NonConstMemberCall(
const CXXMemberCallExpr *MCE,
644 const MatchFinder::MatchResult &
Result,
645 LatticeTransferState &State) {
646 handleNonConstMemberCall(
650void transferValue_NonConstMemberOperatorCall(
651 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
652 LatticeTransferState &State) {
653 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
654 State.Env.getStorageLocation(*OCE->getArg(0)));
658void constructOptionalValue(
const Expr &
E, Environment &
Env,
659 BoolValue &HasValueVal) {
661 setHasValue(
Loc, HasValueVal,
Env);
667BoolValue &valueOrConversionHasValue(QualType DestType,
const Expr &
E,
668 const MatchFinder::MatchResult &MatchRes,
669 LatticeTransferState &State) {
670 const int DestTypeOptionalWrappersCount =
671 countOptionalWrappers(*MatchRes.Context, DestType);
672 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
673 *MatchRes.Context,
E.getType().getNonReferenceType());
680 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
681 return State.Env.getBoolLiteralValue(
true);
687 auto *
Loc = State.Env.get<RecordStorageLocation>(
E);
688 if (
auto *HasValueVal = getHasValue(State.Env,
Loc))
690 return State.Env.makeAtomicBoolValue();
693void transferValueOrConversionConstructor(
694 const CXXConstructExpr *
E,
const MatchFinder::MatchResult &MatchRes,
695 LatticeTransferState &State) {
696 assert(
E->getNumArgs() > 0);
698 constructOptionalValue(
700 valueOrConversionHasValue(
701 E->getConstructor()->getThisType()->getPointeeType(), *
E->getArg(0),
705void transferAssignment(
const CXXOperatorCallExpr *
E, BoolValue &HasValueVal,
706 LatticeTransferState &State) {
707 assert(
E->getNumArgs() > 0);
709 if (
auto *
Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(0))) {
710 setHasValue(*
Loc, HasValueVal, State.Env);
713 State.Env.setStorageLocation(*
E, *
Loc);
717void transferValueOrConversionAssignment(
718 const CXXOperatorCallExpr *
E,
const MatchFinder::MatchResult &MatchRes,
719 LatticeTransferState &State) {
720 assert(
E->getNumArgs() > 1);
723 valueOrConversionHasValue(
E->getArg(0)->getType().getNonReferenceType(),
724 *
E->getArg(1), MatchRes, State),
728void transferNulloptAssignment(
const CXXOperatorCallExpr *
E,
729 const MatchFinder::MatchResult &,
730 LatticeTransferState &State) {
731 transferAssignment(
E, State.Env.getBoolLiteralValue(
false), State);
734void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
740 if (Loc1 ==
nullptr) {
745 if (Loc2 ==
nullptr) {
756 BoolValue *BoolVal1 = getHasValue(
Env, Loc1);
757 if (BoolVal1 ==
nullptr)
760 BoolValue *BoolVal2 = getHasValue(
Env, Loc2);
761 if (BoolVal2 ==
nullptr)
764 setHasValue(*Loc1, *BoolVal2,
Env);
765 setHasValue(*Loc2, *BoolVal1,
Env);
768void transferSwapCall(
const CXXMemberCallExpr *
E,
769 const MatchFinder::MatchResult &,
770 LatticeTransferState &State) {
771 assert(
E->getNumArgs() == 1);
772 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*
E->getArg(0));
776void transferStdSwapCall(
const CallExpr *
E,
const MatchFinder::MatchResult &,
777 LatticeTransferState &State) {
778 assert(
E->getNumArgs() == 2);
779 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(0));
780 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(1));
781 transferSwap(Arg0Loc, Arg1Loc, State.Env);
784void transferStdForwardCall(
const CallExpr *
E,
const MatchFinder::MatchResult &,
785 LatticeTransferState &State) {
786 assert(
E->getNumArgs() == 1);
788 if (
auto *
Loc = State.Env.getStorageLocation(*
E->getArg(0)))
789 State.Env.setStorageLocation(*
E, *
Loc);
792const Formula &evaluateEquality(Arena &A,
const Formula &EqVal,
793 const Formula &LHS,
const Formula &RHS) {
809 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
810 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
811 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
815 const MatchFinder::MatchResult &,
816 LatticeTransferState &State) {
817 Environment &
Env = State.Env;
819 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
820 auto *Arg0Loc =
Env.
get<RecordStorageLocation>(*CmpExpr->
getArg(0));
821 if (
auto *LHasVal = getHasValue(
Env, Arg0Loc)) {
822 auto *Arg1Loc =
Env.
get<RecordStorageLocation>(*CmpExpr->
getArg(1));
823 if (
auto *RHasVal = getHasValue(
Env, Arg1Loc)) {
824 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
825 CmpValue = &A.makeNot(*CmpValue);
826 Env.
assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
827 RHasVal->formula()));
835 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
836 auto *
Loc =
Env.
get<RecordStorageLocation>(*E);
837 if (
auto *HasVal = getHasValue(
Env,
Loc)) {
838 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
839 CmpValue = &A.makeNot(*CmpValue);
841 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(
true)));
848 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
849 auto *
Loc =
Env.
get<RecordStorageLocation>(*E);
850 if (
auto *HasVal = getHasValue(
Env,
Loc)) {
851 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
852 CmpValue = &A.makeNot(*CmpValue);
853 Env.
assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
854 A.makeLiteral(
false)));
858std::optional<StatementMatcher>
859ignorableOptional(
const UncheckedOptionalAccessModelOptions &Options) {
860 if (Options.IgnoreSmartPointerDereference) {
863 unless(hasArgument(0,
expr(hasOptionalType()))))));
871valueCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
872 return isOptionalMemberCallWithNameMatcher(
hasName(
"value"),
877valueOperatorCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
878 return expr(
anyOf(isOptionalOperatorCallWithName(
"*", IgnorableOptional),
879 isOptionalOperatorCallWithName(
"->", IgnorableOptional)));
882auto buildTransferMatchSwitch() {
886 return CFGMatchSwitchBuilder<LatticeTransferState>()
888 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
891 .CaseOfCFGStmt<CXXConstructExpr>(
892 isOptionalInPlaceConstructor(),
893 [](
const CXXConstructExpr *
E,
const MatchFinder::MatchResult &,
894 LatticeTransferState &State) {
895 constructOptionalValue(*
E, State.Env,
896 State.Env.getBoolLiteralValue(
true));
899 .CaseOfCFGStmt<CXXConstructExpr>(
900 isOptionalNulloptConstructor(),
901 [](
const CXXConstructExpr *
E,
const MatchFinder::MatchResult &,
902 LatticeTransferState &State) {
903 constructOptionalValue(*
E, State.Env,
904 State.Env.getBoolLiteralValue(
false));
907 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
908 transferValueOrConversionConstructor)
911 .CaseOfCFGStmt<CXXOperatorCallExpr>(
912 isOptionalValueOrConversionAssignment(),
913 transferValueOrConversionAssignment)
914 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
915 transferNulloptAssignment)
918 .CaseOfCFGStmt<CXXMemberCallExpr>(
919 valueCall(std::nullopt),
920 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
921 LatticeTransferState &State) {
922 transferUnwrapCall(
E,
E->getImplicitObjectArgument(), State);
926 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"*"),
927 [](
const CallExpr *
E,
928 const MatchFinder::MatchResult &,
929 LatticeTransferState &State) {
930 transferUnwrapCall(
E,
E->getArg(0), State);
934 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"->"),
935 [](
const CallExpr *
E,
936 const MatchFinder::MatchResult &,
937 LatticeTransferState &State) {
938 transferArrowOpCall(
E,
E->getArg(0), State);
944 .CaseOfCFGStmt<CXXMemberCallExpr>(
945 isOptionalMemberCallWithNameMatcher(
947 transferOptionalHasValueCall)
950 .CaseOfCFGStmt<CXXMemberCallExpr>(
951 isOptionalMemberCallWithNameMatcher(
hasName(
"operator bool")),
952 transferOptionalHasValueCall)
956 .CaseOfCFGStmt<CXXMemberCallExpr>(
957 isOptionalMemberCallWithNameMatcher(
hasName(
"isNull")),
958 transferOptionalIsNullCall)
961 .CaseOfCFGStmt<CXXMemberCallExpr>(
962 isOptionalMemberCallWithNameMatcher(
hasName(
"emplace")),
963 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
964 LatticeTransferState &State) {
965 if (RecordStorageLocation *
Loc =
967 setHasValue(*
Loc, State.Env.getBoolLiteralValue(
true), State.Env);
972 .CaseOfCFGStmt<CXXMemberCallExpr>(
973 isOptionalMemberCallWithNameMatcher(
hasName(
"reset")),
974 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
975 LatticeTransferState &State) {
976 if (RecordStorageLocation *
Loc =
978 setHasValue(*
Loc, State.Env.getBoolLiteralValue(
false),
984 .CaseOfCFGStmt<CXXMemberCallExpr>(
985 isOptionalMemberCallWithNameMatcher(
hasName(
"swap")),
989 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
992 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
995 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
996 transferValueOrStringEmptyCall)
999 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1002 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1003 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1004 transferOptionalAndOptionalCmp)
1005 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1006 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1008 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1009 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
1011 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1012 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1014 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1015 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
1017 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1018 isComparisonOperatorCall(
1020 unless(
anyOf(hasOptionalType(), hasNulloptType()))),
1022 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1023 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1025 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1026 isComparisonOperatorCall(
1027 unless(
anyOf(hasOptionalType(), hasNulloptType())),
1030 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1031 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1035 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1036 transferValue_ConstMemberCall)
1037 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1038 transferValue_ConstMemberOperatorCall)
1040 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1041 transferValue_NonConstMemberCall)
1042 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1043 isNonConstMemberOperatorCall(),
1044 transferValue_NonConstMemberOperatorCall)
1047 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1048 transferCallReturningOptional)
1054 const Environment &
Env) {
1055 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1056 getLocBehindPossiblePointer(*ObjectExpr,
Env))) {
1057 auto *Prop =
Env.
getValue(locForHasValue(*OptionalLoc));
1058 if (
auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1067 return {ObjectExpr->getBeginLoc()};
1070auto buildDiagnoseMatchSwitch(
1071 const UncheckedOptionalAccessModelOptions &Options) {
1075 auto IgnorableOptional = ignorableOptional(Options);
1076 return CFGMatchSwitchBuilder<
const Environment,
1079 .CaseOfCFGStmt<CXXMemberCallExpr>(
1080 valueCall(IgnorableOptional),
1081 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
1082 const Environment &
Env) {
1083 return diagnoseUnwrapCall(
E->getImplicitObjectArgument(),
Env);
1087 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
1088 [](
const CallExpr *
E,
1089 const MatchFinder::MatchResult &,
1090 const Environment &
Env) {
1091 return diagnoseUnwrapCall(
E->getArg(0),
Env);
1107 TransferMatchSwitch(buildTransferMatchSwitch()) {
1108 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1109 [&Ctx](
QualType Ty) -> llvm::StringMap<QualType> {
1114 return {{
"value", valueTypeFromOptionalDecl(*
Optional)},
1115 {
"has_value", Ctx.
BoolTy}};
1122 LatticeTransferState State(L,
Env);
1128 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
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 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)
static bool hasOptionalClassName(const CXXRecordDecl &RD)
void copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env)
Copies a record (struct, class, or union) from Src to Dst.
static bool isSupportedOptionalType(QualType Ty)
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)
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.