33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/Casting.h"
35#include "llvm/Support/ErrorHandling.h"
46template <
class... NameTypes>
54 if constexpr (
sizeof...(NameTypes) > 0) {
57 if (
const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.
getParent()))
69 if (RD.
getName() ==
"optional") {
70 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext()))
71 return N->isStdNamespace() ||
77 if (RD.
getName() ==
"Optional") {
79 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
84 if (RD.
getName() ==
"NullableValue") {
85 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
86 return N !=
nullptr &&
120using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
124AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
128auto desugarsToOptionalType() {
129 return hasUnqualifiedDesugaredType(
133auto desugarsToOptionalOrDerivedType() {
134 return hasUnqualifiedDesugaredType(
138auto hasOptionalType() {
return hasType(desugarsToOptionalType()); }
142auto hasOptionalOrDerivedType() {
143 return hasType(desugarsToOptionalOrDerivedType());
146QualType getPublicType(
const Expr *
E) {
147 auto *
Cast = dyn_cast<ImplicitCastExpr>(
E->IgnoreParens());
148 if (Cast ==
nullptr ||
Cast->getCastKind() != CK_UncheckedDerivedToBase) {
149 QualType Ty =
E->getType();
150 if (Ty->isPointerType())
151 return Ty->getPointeeType();
158 bool CastingFromThis = isa<CXXThisExpr>(
Cast->getSubExpr());
162 const CXXBaseSpecifier *PublicBase =
nullptr;
163 for (
const CXXBaseSpecifier *
Base :
Cast->path()) {
164 if (
Base->getAccessSpecifier() !=
AS_public && !CastingFromThis)
167 CastingFromThis =
false;
170 if (PublicBase !=
nullptr)
171 return PublicBase->getType();
176 return getPublicType(
Cast->getSubExpr());
203QualType getPublicReceiverType(
const CXXMemberCallExpr &MCE) {
204 return getPublicType(MCE.getImplicitObjectArgument());
208 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
209 return InnerMatcher.matches(getPublicReceiverType(
Node), Finder, Builder);
212auto isOptionalMemberCallWithNameMatcher(
213 ast_matchers::internal::Matcher<NamedDecl> matcher,
214 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
217 publicReceiverType(desugarsToOptionalType()),
221auto isOptionalOperatorCallWithName(
222 llvm::StringRef operator_name,
223 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
230auto isMakeOptionalCall() {
233 "std::make_optional",
"base::make_optional",
"absl::make_optional",
234 "folly::make_optional",
"bsl::make_optional"))),
238auto nulloptTypeDecl() {
240 "base::nullopt_t",
"folly::None",
244auto hasNulloptType() {
return hasType(nulloptTypeDecl()); }
248 "base::in_place_t",
"folly::in_place_t",
252auto isOptionalNulloptConstructor() {
255 hasParameter(0, hasNulloptType()))),
256 hasOptionalOrDerivedType());
259auto isOptionalInPlaceConstructor() {
261 hasOptionalOrDerivedType());
264auto isOptionalValueOrConversionConstructor() {
268 argumentCountIs(1), hasArgument(0,
unless(hasNulloptType())),
269 hasOptionalOrDerivedType());
272auto isOptionalValueOrConversionAssignment() {
277 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
278 argumentCountIs(2), hasArgument(1,
unless(hasNulloptType())));
281auto isOptionalNulloptAssignment() {
285 argumentCountIs(2), hasArgument(1, hasNulloptType()));
288auto isStdSwapCall() {
291 hasArgument(0, hasOptionalOrDerivedType()),
292 hasArgument(1, hasOptionalOrDerivedType()));
295auto isStdForwardCall() {
298 hasArgument(0, hasOptionalOrDerivedType()));
301constexpr llvm::StringLiteral ValueOrCallID =
"ValueOrCall";
303auto isValueOrStringEmptyCall() {
307 onImplicitObjectArgument(ignoringImplicit(
310 ofClass(optionalClass()))),
312 .bind(ValueOrCallID))));
315auto isValueOrNotEqX() {
316 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
321 ofClass(optionalClass()))),
323 .bind(ValueOrCallID)),
324 ignoringImplicit(Arg));
338auto isZeroParamConstMemberCall() {
343auto isZeroParamConstMemberOperatorCall() {
348auto isNonConstMemberCall() {
352auto isNonConstMemberOperatorCall() {
356auto isCallReturningOptional() {
358 anyOf(desugarsToOptionalOrDerivedType(),
359 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
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));
371const Formula &forceBoolValue(Environment &
Env,
const Expr &Expr) {
373 if (
Value !=
nullptr)
374 return Value->formula();
378 return Value->formula();
381StorageLocation &locForHasValue(
const RecordStorageLocation &OptionalLoc) {
382 return OptionalLoc.getSyntheticField(
"has_value");
385StorageLocation &locForValue(
const RecordStorageLocation &OptionalLoc) {
386 return OptionalLoc.getSyntheticField(
"value");
391void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
393 Env.
setValue(locForHasValue(OptionalLoc), HasValueVal);
398BoolValue *getHasValue(Environment &
Env, RecordStorageLocation *OptionalLoc) {
399 if (OptionalLoc ==
nullptr)
401 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
402 auto *HasValueVal =
Env.
get<BoolValue>(HasValueLoc);
403 if (HasValueVal ==
nullptr) {
410QualType valueTypeFromOptionalDecl(
const CXXRecordDecl &RD) {
411 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
412 return CTSD.getTemplateArgs()[0].getAsType();
419int countOptionalWrappers(
const ASTContext &ASTCtx, QualType Type) {
424 return 1 + countOptionalWrappers(
426 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
429StorageLocation *getLocBehindPossiblePointer(
const Expr &
E,
430 const Environment &
Env) {
432 if (
auto *PointerVal = dyn_cast_or_null<PointerValue>(
Env.
getValue(
E)))
433 return &PointerVal->getPointeeLoc();
439void transferUnwrapCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
440 LatticeTransferState &State) {
441 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
442 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
443 if (State.Env.getStorageLocation(*UnwrapExpr) ==
nullptr)
444 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
448void transferArrowOpCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
449 LatticeTransferState &State) {
450 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
451 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
453 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
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);
463void transferOptionalHasValueCall(
const CXXMemberCallExpr *CallExpr,
464 const MatchFinder::MatchResult &,
465 LatticeTransferState &State) {
466 if (
auto *HasValueVal = getHasValue(
468 State.Env.setValue(*CallExpr, *HasValueVal);
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));
483void transferValueOrImpl(
485 LatticeTransferState &State,
486 const Formula &(*ModelPred)(Environment &
Env,
const Formula &ExprVal,
487 const Formula &HasValueVal)) {
488 auto &
Env = State.Env;
495 if (HasValueVal ==
nullptr)
499 HasValueVal->formula()));
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 & {
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 & {
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);
541 Loc = State.Env.get<RecordStorageLocation>(*E);
542 if (
Loc ==
nullptr) {
543 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*
E));
544 State.Env.setStorageLocation(*
E, *
Loc);
548 if (State.Env.getValue(locForHasValue(*
Loc)) !=
nullptr)
551 setHasValue(*
Loc, State.Env.makeAtomicBoolValue(), State.Env);
554void handleConstMemberCall(
const CallExpr *CE,
555 dataflow::RecordStorageLocation *
RecordLoc,
556 const MatchFinder::MatchResult &
Result,
557 LatticeTransferState &State) {
560 const FunctionDecl *DirectCallee = CE->getDirectCallee();
561 if (DirectCallee ==
nullptr)
563 StorageLocation &
Loc =
564 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
565 *
RecordLoc, DirectCallee, State.Env, [&](StorageLocation &
Loc) {
566 setHasValue(cast<RecordStorageLocation>(Loc),
567 State.Env.makeAtomicBoolValue(), State.Env);
569 if (CE->isGLValue()) {
572 State.Env.setStorageLocation(*CE,
Loc);
577 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
578 copyRecord(cast<RecordStorageLocation>(
Loc), ResultLoc, State.Env);
586 (CE->getType()->isBooleanType() || CE->getType()->isPointerType())) {
587 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*
RecordLoc, CE,
591 State.Env.setValue(*CE, *Val);
598 transferCallReturningOptional(CE,
Result, State);
602void transferValue_ConstMemberCall(
const CXXMemberCallExpr *MCE,
603 const MatchFinder::MatchResult &
Result,
604 LatticeTransferState &State) {
605 handleConstMemberCall(
609void transferValue_ConstMemberOperatorCall(
610 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
611 LatticeTransferState &State) {
612 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
613 State.Env.getStorageLocation(*OCE->getArg(0)));
617void handleNonConstMemberCall(
const CallExpr *CE,
618 dataflow::RecordStorageLocation *
RecordLoc,
619 const MatchFinder::MatchResult &
Result,
620 LatticeTransferState &State) {
625 for (
const auto &[Field, FieldLoc] :
RecordLoc->children()) {
626 QualType FieldType =
Field->getType();
627 if (!FieldType.isConstQualified() &&
629 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
630 if (FieldRecordLoc) {
631 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
636 State.Lattice.clearConstMethodReturnValues(*
RecordLoc);
637 State.Lattice.clearConstMethodReturnStorageLocations(*
RecordLoc);
642 transferCallReturningOptional(CE,
Result, State);
646void transferValue_NonConstMemberCall(
const CXXMemberCallExpr *MCE,
647 const MatchFinder::MatchResult &
Result,
648 LatticeTransferState &State) {
649 handleNonConstMemberCall(
653void transferValue_NonConstMemberOperatorCall(
654 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
655 LatticeTransferState &State) {
656 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
657 State.Env.getStorageLocation(*OCE->getArg(0)));
661void constructOptionalValue(
const Expr &
E, Environment &
Env,
662 BoolValue &HasValueVal) {
664 setHasValue(
Loc, HasValueVal,
Env);
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());
683 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
684 return State.Env.getBoolLiteralValue(
true);
690 auto *
Loc = State.Env.get<RecordStorageLocation>(
E);
691 if (
auto *HasValueVal = getHasValue(State.Env,
Loc))
693 return State.Env.makeAtomicBoolValue();
696void transferValueOrConversionConstructor(
697 const CXXConstructExpr *
E,
const MatchFinder::MatchResult &MatchRes,
698 LatticeTransferState &State) {
699 assert(
E->getNumArgs() > 0);
701 constructOptionalValue(
703 valueOrConversionHasValue(
704 E->getConstructor()->getThisType()->getPointeeType(), *
E->getArg(0),
708void transferAssignment(
const CXXOperatorCallExpr *
E, BoolValue &HasValueVal,
709 LatticeTransferState &State) {
710 assert(
E->getNumArgs() > 0);
712 if (
auto *
Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(0))) {
713 setHasValue(*
Loc, HasValueVal, State.Env);
716 State.Env.setStorageLocation(*
E, *
Loc);
720void transferValueOrConversionAssignment(
721 const CXXOperatorCallExpr *
E,
const MatchFinder::MatchResult &MatchRes,
722 LatticeTransferState &State) {
723 assert(
E->getNumArgs() > 1);
726 valueOrConversionHasValue(
E->getArg(0)->getType().getNonReferenceType(),
727 *
E->getArg(1), MatchRes, State),
731void transferNulloptAssignment(
const CXXOperatorCallExpr *
E,
732 const MatchFinder::MatchResult &,
733 LatticeTransferState &State) {
734 transferAssignment(
E, State.Env.getBoolLiteralValue(
false), State);
737void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
743 if (Loc1 ==
nullptr) {
748 if (Loc2 ==
nullptr) {
759 BoolValue *BoolVal1 = getHasValue(
Env, Loc1);
760 if (BoolVal1 ==
nullptr)
763 BoolValue *BoolVal2 = getHasValue(
Env, Loc2);
764 if (BoolVal2 ==
nullptr)
767 setHasValue(*Loc1, *BoolVal2,
Env);
768 setHasValue(*Loc2, *BoolVal1,
Env);
771void transferSwapCall(
const CXXMemberCallExpr *
E,
772 const MatchFinder::MatchResult &,
773 LatticeTransferState &State) {
774 assert(
E->getNumArgs() == 1);
775 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*
E->getArg(0));
779void transferStdSwapCall(
const CallExpr *
E,
const MatchFinder::MatchResult &,
780 LatticeTransferState &State) {
781 assert(
E->getNumArgs() == 2);
782 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(0));
783 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*
E->getArg(1));
784 transferSwap(Arg0Loc, Arg1Loc, State.Env);
787void transferStdForwardCall(
const CallExpr *
E,
const MatchFinder::MatchResult &,
788 LatticeTransferState &State) {
789 assert(
E->getNumArgs() == 1);
791 if (
auto *
Loc = State.Env.getStorageLocation(*
E->getArg(0)))
792 State.Env.setStorageLocation(*
E, *
Loc);
795const Formula &evaluateEquality(Arena &A,
const Formula &EqVal,
796 const Formula &LHS,
const Formula &RHS) {
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)));
818 const MatchFinder::MatchResult &,
819 LatticeTransferState &State) {
820 Environment &
Env = State.Env;
822 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
823 auto *Arg0Loc =
Env.
get<RecordStorageLocation>(*CmpExpr->
getArg(0));
824 if (
auto *LHasVal = getHasValue(
Env, Arg0Loc)) {
825 auto *Arg1Loc =
Env.
get<RecordStorageLocation>(*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()));
838 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
839 auto *
Loc =
Env.
get<RecordStorageLocation>(*E);
840 if (
auto *HasVal = getHasValue(
Env,
Loc)) {
841 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
842 CmpValue = &A.makeNot(*CmpValue);
844 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(
true)));
851 auto *CmpValue = &forceBoolValue(
Env, *CmpExpr);
852 auto *
Loc =
Env.
get<RecordStorageLocation>(*E);
853 if (
auto *HasVal = getHasValue(
Env,
Loc)) {
854 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
855 CmpValue = &A.makeNot(*CmpValue);
856 Env.
assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
857 A.makeLiteral(
false)));
861std::optional<StatementMatcher>
862ignorableOptional(
const UncheckedOptionalAccessModelOptions &Options) {
863 if (Options.IgnoreSmartPointerDereference) {
866 unless(hasArgument(0,
expr(hasOptionalType()))))));
874valueCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
875 return isOptionalMemberCallWithNameMatcher(
hasName(
"value"),
880valueOperatorCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
881 return expr(
anyOf(isOptionalOperatorCallWithName(
"*", IgnorableOptional),
882 isOptionalOperatorCallWithName(
"->", IgnorableOptional)));
885auto buildTransferMatchSwitch() {
889 return CFGMatchSwitchBuilder<LatticeTransferState>()
891 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
894 .CaseOfCFGStmt<CXXConstructExpr>(
895 isOptionalInPlaceConstructor(),
896 [](
const CXXConstructExpr *
E,
const MatchFinder::MatchResult &,
897 LatticeTransferState &State) {
898 constructOptionalValue(*
E, State.Env,
899 State.Env.getBoolLiteralValue(
true));
902 .CaseOfCFGStmt<CXXConstructExpr>(
903 isOptionalNulloptConstructor(),
904 [](
const CXXConstructExpr *
E,
const MatchFinder::MatchResult &,
905 LatticeTransferState &State) {
906 constructOptionalValue(*
E, State.Env,
907 State.Env.getBoolLiteralValue(
false));
910 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
911 transferValueOrConversionConstructor)
914 .CaseOfCFGStmt<CXXOperatorCallExpr>(
915 isOptionalValueOrConversionAssignment(),
916 transferValueOrConversionAssignment)
917 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
918 transferNulloptAssignment)
921 .CaseOfCFGStmt<CXXMemberCallExpr>(
922 valueCall(std::nullopt),
923 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
924 LatticeTransferState &State) {
925 transferUnwrapCall(
E,
E->getImplicitObjectArgument(), State);
929 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"*"),
930 [](
const CallExpr *
E,
931 const MatchFinder::MatchResult &,
932 LatticeTransferState &State) {
933 transferUnwrapCall(
E,
E->getArg(0), State);
937 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"->"),
938 [](
const CallExpr *
E,
939 const MatchFinder::MatchResult &,
940 LatticeTransferState &State) {
941 transferArrowOpCall(
E,
E->getArg(0), State);
947 .CaseOfCFGStmt<CXXMemberCallExpr>(
948 isOptionalMemberCallWithNameMatcher(
950 transferOptionalHasValueCall)
953 .CaseOfCFGStmt<CXXMemberCallExpr>(
954 isOptionalMemberCallWithNameMatcher(
hasName(
"operator bool")),
955 transferOptionalHasValueCall)
959 .CaseOfCFGStmt<CXXMemberCallExpr>(
960 isOptionalMemberCallWithNameMatcher(
hasName(
"isNull")),
961 transferOptionalIsNullCall)
964 .CaseOfCFGStmt<CXXMemberCallExpr>(
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);
975 .CaseOfCFGStmt<CXXMemberCallExpr>(
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),
987 .CaseOfCFGStmt<CXXMemberCallExpr>(
988 isOptionalMemberCallWithNameMatcher(
hasName(
"swap")),
992 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
995 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
998 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
999 transferValueOrStringEmptyCall)
1002 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1005 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1006 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1007 transferOptionalAndOptionalCmp)
1008 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1009 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1011 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1012 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
1014 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1015 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1017 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1018 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
1020 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1021 isComparisonOperatorCall(
1023 unless(
anyOf(hasOptionalType(), hasNulloptType()))),
1025 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1026 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1028 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1029 isComparisonOperatorCall(
1030 unless(
anyOf(hasOptionalType(), hasNulloptType())),
1033 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1034 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1040 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1042 [](
const CXXOperatorCallExpr *
E,
1043 const MatchFinder::MatchResult &
Result,
1044 LatticeTransferState &State) {
1047 dyn_cast_or_null<RecordStorageLocation>(
1048 getLocBehindPossiblePointer(*
E->getArg(0), State.Env)),
1049 State, [](StorageLocation &
Loc) {});
1051 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1053 [](
const CXXOperatorCallExpr *
E,
1054 const MatchFinder::MatchResult &
Result,
1055 LatticeTransferState &State) {
1058 dyn_cast_or_null<RecordStorageLocation>(
1059 getLocBehindPossiblePointer(*
E->getArg(0), State.Env)),
1060 State, [](StorageLocation &
Loc) {});
1062 .CaseOfCFGStmt<CXXMemberCallExpr>(
1064 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &
Result,
1065 LatticeTransferState &State) {
1068 [](StorageLocation &
Loc) {});
1070 .CaseOfCFGStmt<CXXMemberCallExpr>(
1072 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &
Result,
1073 LatticeTransferState &State) {
1076 [](StorageLocation &
Loc) {});
1080 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1081 transferValue_ConstMemberCall)
1082 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1083 transferValue_ConstMemberOperatorCall)
1085 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1086 transferValue_NonConstMemberCall)
1087 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1088 isNonConstMemberOperatorCall(),
1089 transferValue_NonConstMemberOperatorCall)
1092 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1093 transferCallReturningOptional)
1099 const Environment &
Env) {
1100 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1101 getLocBehindPossiblePointer(*ObjectExpr,
Env))) {
1102 auto *Prop =
Env.
getValue(locForHasValue(*OptionalLoc));
1103 if (
auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1112 return {ObjectExpr->getBeginLoc()};
1115auto buildDiagnoseMatchSwitch(
1116 const UncheckedOptionalAccessModelOptions &Options) {
1120 auto IgnorableOptional = ignorableOptional(Options);
1121 return CFGMatchSwitchBuilder<
const Environment,
1124 .CaseOfCFGStmt<CXXMemberCallExpr>(
1125 valueCall(IgnorableOptional),
1126 [](
const CXXMemberCallExpr *
E,
const MatchFinder::MatchResult &,
1127 const Environment &
Env) {
1128 return diagnoseUnwrapCall(
E->getImplicitObjectArgument(),
Env);
1132 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
1133 [](
const CallExpr *
E,
1134 const MatchFinder::MatchResult &,
1135 const Environment &
Env) {
1136 return diagnoseUnwrapCall(
E->getArg(0),
Env);
1152 TransferMatchSwitch(buildTransferMatchSwitch()) {
1153 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1154 [&Ctx](
QualType Ty) -> llvm::StringMap<QualType> {
1159 return {{
"value", valueTypeFromOptionalDecl(*
Optional)},
1160 {
"has_value", Ctx.
BoolTy}};
1167 LatticeTransferState State(L,
Env);
1173 : 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 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.