33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/ErrorHandling.h"
45template <
class... NameTypes>
53 if constexpr (
sizeof...(NameTypes) > 0) {
56 if (
const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.
getParent()))
68 if (RD.
getName() ==
"optional") {
69 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext()))
70 return N->isStdNamespace() ||
75 if (RD.
getName() ==
"Optional") {
77 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
82 if (RD.
getName() ==
"Optional_Base") {
83 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
84 return N !=
nullptr &&
88 if (RD.
getName() ==
"NullableValue") {
89 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
90 return N !=
nullptr &&
125 if (RD->getName() ==
"AssertionResult")
126 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))
136using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
140AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
144auto desugarsToOptionalType() {
145 return hasUnqualifiedDesugaredType(
149auto desugarsToOptionalOrDerivedType() {
150 return hasUnqualifiedDesugaredType(
154auto hasOptionalType() {
return hasType(desugarsToOptionalType()); }
158auto hasOptionalOrDerivedType() {
159 return hasType(desugarsToOptionalOrDerivedType());
162bool isDesugaredTypeOptional(QualType Ty) {
163 const Type &DesugaredTy = *Ty->getUnqualifiedDesugaredType();
164 return DesugaredTy.isRecordType() &&
168bool isDesugaredTypeOptionalOrPointerToOptional(QualType Ty) {
169 if (Ty->isPointerType())
170 Ty = Ty->getPointeeType();
171 return isDesugaredTypeOptional(Ty);
199bool hasReceiverTypeDesugaringToOptional(
const Expr *E) {
200 auto *
Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
201 if (Cast ==
nullptr ||
Cast->getCastKind() != CK_UncheckedDerivedToBase)
202 return isDesugaredTypeOptionalOrPointerToOptional(E->getType());
207 if (isDesugaredTypeOptionalOrPointerToOptional(
Cast->getSubExpr()->getType()))
212 for (
const CXXBaseSpecifier *Base :
Cast->path()) {
213 if (isDesugaredTypeOptional(
Base->getType()))
220 return hasReceiverTypeDesugaringToOptional(
Cast->getSubExpr());
223AST_MATCHER(CXXMemberCallExpr, hasOptionalReceiverType) {
224 return hasReceiverTypeDesugaringToOptional(Node.getImplicitObjectArgument());
227AST_MATCHER(CXXOperatorCallExpr, hasOptionalOperatorObjectType) {
228 return hasReceiverTypeDesugaringToOptional(Node.getArg(0));
231auto isOptionalMemberCallWithNameMatcher(
232 ast_matchers::internal::Matcher<NamedDecl> matcher,
233 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
239auto isOptionalOperatorCallWithName(
240 llvm::StringRef operator_name,
241 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
247auto isMakeOptionalCall() {
250 "std::make_optional",
"base::make_optional",
"absl::make_optional",
251 "folly::make_optional",
"bsl::make_optional"))),
252 hasOptionalOrDerivedType());
255auto nulloptTypeDecl() {
257 "base::nullopt_t",
"folly::None",
261auto hasNulloptType() {
return hasType(nulloptTypeDecl()); }
265 "base::in_place_t",
"folly::in_place_t",
269auto allocatorArgClass() {
273auto hasAllocatorArgType() {
return hasType(allocatorArgClass()); }
275auto isOptionalNulloptConstructor() {
278 hasParameter(0, hasNulloptType()))),
279 hasOptionalOrDerivedType());
282auto isOptionalInPlaceConstructor() {
284 hasOptionalOrDerivedType());
289auto isOptionalValueOrConversionConstructor() {
293 argumentCountAtLeast(1),
294 hasArgument(0,
unless(
anyOf(hasNulloptType(), hasType(inPlaceClass()),
295 hasAllocatorArgType()))),
296 hasOptionalOrDerivedType());
302auto isOptionalAllocatorExtendedValueOrConversionConstructor() {
306 hasArgument(0, hasAllocatorArgType()), argumentCountAtLeast(3),
307 hasArgument(2,
unless(
anyOf(hasNulloptType(), hasType(inPlaceClass())))),
308 hasOptionalOrDerivedType());
311auto isOptionalValueOrConversionAssignment() {
316 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
317 argumentCountIs(2), hasArgument(1,
unless(hasNulloptType())));
320auto isOptionalNulloptAssignment() {
324 argumentCountIs(2), hasArgument(1, hasNulloptType()));
327auto isStdSwapCall() {
330 hasArgument(0, hasOptionalOrDerivedType()),
331 hasArgument(1, hasOptionalOrDerivedType()));
334auto isStdForwardCall() {
337 hasArgument(0, hasOptionalOrDerivedType()));
340auto isAssertionResultOperatorBoolCall() {
344 ofClass(
hasName(
"testing::AssertionResult")))));
347auto isAssertionResultConstructFromBoolCall() {
350 hasArgument(0, hasType(booleanType())));
353auto isAssertionResultConstructFromOptionalCall() {
356 hasArgument(0, hasOptionalOrDerivedType()));
359constexpr llvm::StringLiteral ValueOrCallID =
"ValueOrCall";
361auto isValueOrStringEmptyCall() {
365 onImplicitObjectArgument(ignoringImplicit(
368 ofClass(optionalClass()))),
370 .bind(ValueOrCallID))));
373auto isValueOrNotEqX() {
374 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
379 ofClass(optionalClass()))),
381 .bind(ValueOrCallID)),
382 ignoringImplicit(Arg));
396auto isZeroParamConstMemberCall() {
401auto isZeroParamConstMemberOperatorCall() {
406auto isNonConstMemberCall() {
410auto isNonConstMemberOperatorCall() {
414auto isCallReturningOptional() {
416 anyOf(desugarsToOptionalOrDerivedType(),
417 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
420template <
typename L,
typename R>
421auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
424 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
425 hasArgument(1, rhs_arg_matcher));
431 if (
Value !=
nullptr)
432 return Value->formula();
434 Value = &Env.makeAtomicBoolValue();
435 Env.setValue(Expr, *
Value);
436 return Value->formula();
440 return OptionalLoc.getSyntheticField(
"has_value");
444 return OptionalLoc.getSyntheticField(
"value");
449 return AssertResultLoc.getSyntheticField(
"success");
456 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
462 if (OptionalLoc ==
nullptr)
465 auto *HasValueVal = Env.get<
BoolValue>(HasValueLoc);
466 if (HasValueVal ==
nullptr) {
467 HasValueVal = &Env.makeAtomicBoolValue();
468 Env.setValue(HasValueLoc, *HasValueVal);
473QualType valueTypeFromOptionalDecl(
const CXXRecordDecl &RD) {
475 return CTSD.getTemplateArgs()[0].getAsType();
482int countOptionalWrappers(
const ASTContext &ASTCtx, QualType
Type) {
487 return 1 + countOptionalWrappers(
489 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
495 if (
auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
496 return &PointerVal->getPointeeLoc();
499 return Env.getStorageLocation(E);
502void transferUnwrapCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
503 LatticeTransferState &State) {
504 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
505 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
506 if (State.Env.getStorageLocation(*UnwrapExpr) ==
nullptr)
507 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
511void transferArrowOpCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
512 LatticeTransferState &State) {
513 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
514 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
516 *UnwrapExpr, State.Env.create<
PointerValue>(locForValue(*OptionalLoc)));
519void transferMakeOptionalCall(
const CallExpr *E,
520 const MatchFinder::MatchResult &,
521 LatticeTransferState &State) {
522 setHasValue(State.Env.getResultObjectLocation(*E),
523 State.Env.getBoolLiteralValue(
true), State.Env);
526void transferOptionalHasValueCall(
const CXXMemberCallExpr *CallExpr,
527 const MatchFinder::MatchResult &,
528 LatticeTransferState &State) {
529 if (
auto *HasValueVal = getHasValue(
531 State.Env.setValue(*CallExpr, *HasValueVal);
535void transferOptionalIsNullCall(
const CXXMemberCallExpr *CallExpr,
536 const MatchFinder::MatchResult &,
537 LatticeTransferState &State) {
538 if (
auto *HasValueVal = getHasValue(
540 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
546void transferValueOrImpl(
547 const clang::Expr *ValueOrPredExpr,
const MatchFinder::MatchResult &
Result,
548 LatticeTransferState &State,
551 auto &Env = State.Env;
554 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
558 if (HasValueVal ==
nullptr)
561 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
562 HasValueVal->formula()));
565void transferValueOrStringEmptyCall(
const clang::Expr *ComparisonExpr,
566 const MatchFinder::MatchResult &
Result,
567 LatticeTransferState &State) {
568 return transferValueOrImpl(ComparisonExpr,
Result, State,
571 auto &A = Env.arena();
578 return A.makeImplies(A.makeNot(ExprVal),
583void transferValueOrNotEqX(
const Expr *ComparisonExpr,
584 const MatchFinder::MatchResult &
Result,
585 LatticeTransferState &State) {
586 transferValueOrImpl(ComparisonExpr,
Result, State,
589 auto &A = Env.arena();
593 return A.makeImplies(ExprVal, HasValueVal);
597void transferCallReturningOptional(
const CallExpr *E,
598 const MatchFinder::MatchResult &
Result,
599 LatticeTransferState &State) {
601 if (E->isPRValue()) {
602 Loc = &State.Env.getResultObjectLocation(*E);
605 if (Loc ==
nullptr) {
607 State.Env.setStorageLocation(*E, *Loc);
611 if (State.Env.getValue(locForHasValue(*Loc)) !=
nullptr)
614 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
620bool handleConstMemberCall(
const CallExpr *CE,
621 dataflow::RecordStorageLocation *
RecordLoc,
622 const MatchFinder::MatchResult &
Result,
623 LatticeTransferState &State) {
628 if (CE->isGLValue()) {
629 const FunctionDecl *DirectCallee = CE->getDirectCallee();
630 if (DirectCallee ==
nullptr)
639 State.Env.makeAtomicBoolValue(), State.Env);
642 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
645 State.Env.setStorageLocation(*CE, Loc);
649 if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {
651 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*
RecordLoc, CE,
655 State.Env.setValue(*CE, *Val);
660 const FunctionDecl *DirectCallee = CE->getDirectCallee();
661 if (DirectCallee ==
nullptr)
664 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
666 setHasValue(cast<RecordStorageLocation>(Loc),
667 State.Env.makeAtomicBoolValue(), State.Env);
671 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
679void handleConstMemberCallWithFallbacks(
680 const CallExpr *CE, dataflow::RecordStorageLocation *
RecordLoc,
681 const MatchFinder::MatchResult &
Result, LatticeTransferState &State) {
687 transferCallReturningOptional(CE,
Result, State);
690void transferConstMemberCall(
const CXXMemberCallExpr *MCE,
691 const MatchFinder::MatchResult &
Result,
692 LatticeTransferState &State) {
693 handleConstMemberCallWithFallbacks(
697void transferConstMemberOperatorCall(
const CXXOperatorCallExpr *OCE,
698 const MatchFinder::MatchResult &
Result,
699 LatticeTransferState &State) {
700 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
701 State.Env.getStorageLocation(*OCE->getArg(0)));
705void handleNonConstMemberCall(
const CallExpr *CE,
706 dataflow::RecordStorageLocation *
RecordLoc,
707 const MatchFinder::MatchResult &
Result,
708 LatticeTransferState &State) {
713 for (
const auto &[Field, FieldLoc] :
RecordLoc->children()) {
714 QualType FieldType =
Field->getType();
715 if (!FieldType.isConstQualified() &&
717 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
718 if (FieldRecordLoc) {
719 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
724 State.Lattice.clearConstMethodReturnValues(*
RecordLoc);
725 State.Lattice.clearConstMethodReturnStorageLocations(*
RecordLoc);
730 transferCallReturningOptional(CE,
Result, State);
734void transferValue_NonConstMemberCall(
const CXXMemberCallExpr *MCE,
735 const MatchFinder::MatchResult &
Result,
736 LatticeTransferState &State) {
737 handleNonConstMemberCall(
741void transferValue_NonConstMemberOperatorCall(
742 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
743 LatticeTransferState &State) {
744 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
745 State.Env.getStorageLocation(*OCE->getArg(0)));
749void constructOptionalValue(
const Expr &E,
Environment &Env,
752 setHasValue(Loc, HasValueVal, Env);
758BoolValue &valueOrConversionHasValue(QualType DestType,
const Expr &E,
759 const MatchFinder::MatchResult &MatchRes,
760 LatticeTransferState &State) {
761 const int DestTypeOptionalWrappersCount =
762 countOptionalWrappers(*MatchRes.Context, DestType);
763 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
764 *MatchRes.Context, E.getType().getNonReferenceType());
771 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
772 return State.Env.getBoolLiteralValue(
true);
779 if (
auto *HasValueVal = getHasValue(State.Env, Loc))
781 return State.Env.makeAtomicBoolValue();
784void transferValueOrConversionConstructorImpl(
785 const CXXConstructExpr *E,
unsigned ValueArgIdx,
786 const MatchFinder::MatchResult &MatchRes, LatticeTransferState &State) {
787 assert(E->getNumArgs() > ValueArgIdx);
789 constructOptionalValue(
791 valueOrConversionHasValue(
792 E->getConstructor()->getThisType()->getPointeeType(),
793 *E->getArg(ValueArgIdx), MatchRes, State));
796void transferValueOrConversionConstructor(
797 const CXXConstructExpr *E,
const MatchFinder::MatchResult &MatchRes,
798 LatticeTransferState &State) {
799 transferValueOrConversionConstructorImpl(E, 0, MatchRes,
803void transferAllocatorExtendedValueOrConversionConstructor(
804 const CXXConstructExpr *E,
const MatchFinder::MatchResult &MatchRes,
805 LatticeTransferState &State) {
806 transferValueOrConversionConstructorImpl(E, 2, MatchRes,
810void transferAssignment(
const CXXOperatorCallExpr *E,
BoolValue &HasValueVal,
811 LatticeTransferState &State) {
812 assert(E->getNumArgs() > 0);
815 setHasValue(*Loc, HasValueVal, State.Env);
818 State.Env.setStorageLocation(*E, *Loc);
822void transferValueOrConversionAssignment(
823 const CXXOperatorCallExpr *E,
const MatchFinder::MatchResult &MatchRes,
824 LatticeTransferState &State) {
825 assert(E->getNumArgs() > 1);
828 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
829 *E->getArg(1), MatchRes, State),
833void transferNulloptAssignment(
const CXXOperatorCallExpr *E,
834 const MatchFinder::MatchResult &,
835 LatticeTransferState &State) {
836 transferAssignment(E, State.Env.getBoolLiteralValue(
false), State);
845 if (Loc1 ==
nullptr) {
847 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
850 if (Loc2 ==
nullptr) {
851 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
861 BoolValue *BoolVal1 = getHasValue(Env, Loc1);
862 if (BoolVal1 ==
nullptr)
863 BoolVal1 = &Env.makeAtomicBoolValue();
865 BoolValue *BoolVal2 = getHasValue(Env, Loc2);
866 if (BoolVal2 ==
nullptr)
867 BoolVal2 = &Env.makeAtomicBoolValue();
869 setHasValue(*Loc1, *BoolVal2, Env);
870 setHasValue(*Loc2, *BoolVal1, Env);
873void transferSwapCall(
const CXXMemberCallExpr *E,
874 const MatchFinder::MatchResult &,
875 LatticeTransferState &State) {
876 assert(E->getNumArgs() == 1);
881void transferStdSwapCall(
const CallExpr *E,
const MatchFinder::MatchResult &,
882 LatticeTransferState &State) {
883 assert(E->getNumArgs() == 2);
886 transferSwap(Arg0Loc, Arg1Loc, State.Env);
889void transferStdForwardCall(
const CallExpr *E,
const MatchFinder::MatchResult &,
890 LatticeTransferState &State) {
891 assert(E->getNumArgs() == 1);
893 if (
auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
894 State.Env.setStorageLocation(*E, *Loc);
912 return A.makeAnd(A.makeImplies(EqVal, A.makeEquals(LHS, RHS)),
913 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
916void transferOptionalAndOptionalCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
917 const MatchFinder::MatchResult &,
918 LatticeTransferState &State) {
920 auto &A = Env.
arena();
921 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
923 if (
auto *LHasVal = getHasValue(Env, Arg0Loc)) {
925 if (
auto *RHasVal = getHasValue(Env, Arg1Loc)) {
926 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
927 CmpValue = &A.makeNot(*CmpValue);
928 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
929 RHasVal->formula()));
934void transferOptionalAndValueCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
936 auto &A = Env.arena();
937 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
939 if (
auto *HasVal = getHasValue(Env, Loc)) {
940 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
941 CmpValue = &A.makeNot(*CmpValue);
943 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(
true)));
947void transferOptionalAndNulloptCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
949 auto &A = Env.arena();
950 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
952 if (
auto *HasVal = getHasValue(Env, Loc)) {
953 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
954 CmpValue = &A.makeNot(*CmpValue);
955 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
956 A.makeLiteral(
false)));
960void transferAssertionResultOperatorBoolCall(
const CXXMemberCallExpr *Expr,
961 const MatchFinder::MatchResult &,
962 LatticeTransferState &State) {
964 if (AssertResultLoc ==
nullptr)
968 locForAssertResultSuccess(*AssertResultLoc))) {
969 State.Env.setValue(*Expr, *SuccessVal);
973void transferAssertionResultConstructFromBoolCall(
974 const CXXConstructExpr *ConstructExpr,
const MatchFinder::MatchResult &,
975 LatticeTransferState &State) {
976 assert(ConstructExpr->getNumArgs() > 0);
977 const Expr *Arg = ConstructExpr->getArg(0)->IgnoreImplicit();
980 auto &ResultLoc = State.Env.getResultObjectLocation(*ConstructExpr);
981 State.Env.setValue(locForAssertResultSuccess(ResultLoc), *SuccessVal);
985void transferAssertionResultConstructFromOptionalCall(
986 const CXXConstructExpr *ConstructExpr,
const MatchFinder::MatchResult &,
987 LatticeTransferState &State) {
988 assert(ConstructExpr->getNumArgs() > 0);
990 const Expr *Arg = ConstructExpr->getArg(0)->IgnoreImplicit();
992 cast_or_null<RecordStorageLocation>(State.Env.getStorageLocation(*Arg));
993 if (OptionalLoc ==
nullptr)
996 if (
BoolValue *HasVal = getHasValue(State.Env, OptionalLoc)) {
997 auto &ResultLoc = State.Env.getResultObjectLocation(*ConstructExpr);
998 State.Env.setValue(locForAssertResultSuccess(ResultLoc), *HasVal);
1002std::optional<StatementMatcher>
1004 if (Options.IgnoreSmartPointerDereference) {
1007 unless(hasArgument(0,
expr(hasOptionalType()))))));
1011 return std::nullopt;
1015valueCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
1016 return isOptionalMemberCallWithNameMatcher(
hasName(
"value"),
1021valueOperatorCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
1022 return expr(
anyOf(isOptionalOperatorCallWithName(
"*", IgnorableOptional),
1023 isOptionalOperatorCallWithName(
"->", IgnorableOptional)));
1032 .
CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
1035 .CaseOfCFGStmt<CXXConstructExpr>(
1036 isOptionalInPlaceConstructor(),
1037 [](
const CXXConstructExpr *E,
const MatchFinder::MatchResult &,
1038 LatticeTransferState &State) {
1039 constructOptionalValue(*E, State.Env,
1040 State.Env.getBoolLiteralValue(
true));
1043 .CaseOfCFGStmt<CXXConstructExpr>(
1044 isOptionalNulloptConstructor(),
1045 [](
const CXXConstructExpr *E,
const MatchFinder::MatchResult &,
1046 LatticeTransferState &State) {
1047 constructOptionalValue(*E, State.Env,
1048 State.Env.getBoolLiteralValue(
false));
1051 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
1052 transferValueOrConversionConstructor)
1055 isOptionalAllocatorExtendedValueOrConversionConstructor(),
1056 transferAllocatorExtendedValueOrConversionConstructor)
1059 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1060 isOptionalValueOrConversionAssignment(),
1061 transferValueOrConversionAssignment)
1062 .
CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
1063 transferNulloptAssignment)
1066 .CaseOfCFGStmt<CXXMemberCallExpr>(
1067 valueCall(std::nullopt),
1068 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1069 LatticeTransferState &State) {
1070 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
1074 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"*"),
1075 [](
const CallExpr *E,
1076 const MatchFinder::MatchResult &,
1077 LatticeTransferState &State) {
1078 transferUnwrapCall(E, E->getArg(0), State);
1082 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"->"),
1083 [](
const CallExpr *E,
1084 const MatchFinder::MatchResult &,
1085 LatticeTransferState &State) {
1086 transferArrowOpCall(E, E->getArg(0), State);
1092 .CaseOfCFGStmt<CXXMemberCallExpr>(
1093 isOptionalMemberCallWithNameMatcher(
1095 transferOptionalHasValueCall)
1099 isOptionalMemberCallWithNameMatcher(
hasName(
"operator bool")),
1100 transferOptionalHasValueCall)
1104 .CaseOfCFGStmt<CXXMemberCallExpr>(
1105 isOptionalMemberCallWithNameMatcher(
hasName(
"isNull")),
1106 transferOptionalIsNullCall)
1112 isOptionalMemberCallWithNameMatcher(
1113 hasAnyName(
"makeValue",
"makeValueInplace")),
1114 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1118 setHasValue(*Loc, State.Env.getBoolLiteralValue(
true), State.Env);
1123 .CaseOfCFGStmt<CXXMemberCallExpr>(
1124 isOptionalMemberCallWithNameMatcher(
hasName(
"emplace")),
1125 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1126 LatticeTransferState &State) {
1129 setHasValue(*Loc, State.Env.getBoolLiteralValue(
true), State.Env);
1134 .CaseOfCFGStmt<CXXMemberCallExpr>(
1135 isOptionalMemberCallWithNameMatcher(
hasName(
"reset")),
1136 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1137 LatticeTransferState &State) {
1140 setHasValue(*Loc, State.Env.getBoolLiteralValue(
false),
1146 .CaseOfCFGStmt<CXXMemberCallExpr>(
1147 isOptionalMemberCallWithNameMatcher(
hasName(
"swap")),
1151 .
CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
1154 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
1158 transferValueOrStringEmptyCall)
1161 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1165 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1166 transferOptionalAndOptionalCmp)
1167 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1168 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1169 [](
const clang::CXXOperatorCallExpr *
Cmp,
1170 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1171 transferOptionalAndNulloptCmp(
Cmp,
Cmp->getArg(0), State.Env);
1173 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1174 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1175 [](
const clang::CXXOperatorCallExpr *
Cmp,
1176 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1177 transferOptionalAndNulloptCmp(
Cmp,
Cmp->getArg(1), State.Env);
1179 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1180 isComparisonOperatorCall(
1182 unless(
anyOf(hasOptionalType(), hasNulloptType()))),
1183 [](
const clang::CXXOperatorCallExpr *
Cmp,
1184 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1185 transferOptionalAndValueCmp(
Cmp,
Cmp->getArg(0), State.Env);
1187 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1188 isComparisonOperatorCall(
1189 unless(
anyOf(hasOptionalType(), hasNulloptType())),
1191 [](
const clang::CXXOperatorCallExpr *
Cmp,
1192 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1193 transferOptionalAndValueCmp(
Cmp,
Cmp->getArg(1), State.Env);
1199 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1201 [](
const CXXOperatorCallExpr *E,
1202 const MatchFinder::MatchResult &
Result,
1203 LatticeTransferState &State) {
1206 dyn_cast_or_null<RecordStorageLocation>(
1207 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1210 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1212 [](
const CXXOperatorCallExpr *E,
1213 const MatchFinder::MatchResult &
Result,
1214 LatticeTransferState &State) {
1217 dyn_cast_or_null<RecordStorageLocation>(
1218 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1221 .CaseOfCFGStmt<CXXMemberCallExpr>(
1223 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &
Result,
1224 LatticeTransferState &State) {
1229 .CaseOfCFGStmt<CXXMemberCallExpr>(
1231 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &
Result,
1232 LatticeTransferState &State) {
1239 .CaseOfCFGStmt<CXXMemberCallExpr>(isAssertionResultOperatorBoolCall(),
1240 transferAssertionResultOperatorBoolCall)
1243 [](
const CXXMemberCallExpr *Expr,
const MatchFinder::MatchResult &,
1246 Expr, State.Env, locForAssertResultSuccess);
1248 .CaseOfCFGStmt<CXXConstructExpr>(
1249 isAssertionResultConstructFromBoolCall(),
1250 transferAssertionResultConstructFromBoolCall)
1252 isAssertionResultConstructFromOptionalCall(),
1253 transferAssertionResultConstructFromOptionalCall)
1255 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1256 transferConstMemberCall)
1257 .
CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1258 transferConstMemberOperatorCall)
1260 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1261 transferValue_NonConstMemberCall)
1263 isNonConstMemberOperatorCall(),
1264 transferValue_NonConstMemberOperatorCall)
1267 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1268 transferCallReturningOptional)
1273llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
1274diagnoseUnwrapCall(
const Expr *ObjectExpr,
const Environment &Env) {
1275 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1276 getLocBehindPossiblePointer(*ObjectExpr, Env))) {
1277 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
1278 if (
auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1279 if (Env.proves(HasValueVal->formula()))
1290auto buildDiagnoseMatchSwitch(
1295 const auto IgnorableOptional = ignorableOptional(Options);
1300 llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()
1302 .CaseOfCFGStmt<CallExpr>(
1303 valueOperatorCall(IgnorableOptional),
1304 [](
const CallExpr *E,
const MatchFinder::MatchResult &,
1306 return diagnoseUnwrapCall(E->getArg(0), Env);
1309 auto Builder = Options.IgnoreValueCalls
1310 ? std::move(DiagBuilder)
1311 : std::move(DiagBuilder)
1313 .CaseOfCFGStmt<CXXMemberCallExpr>(
1314 valueCall(IgnorableOptional),
1315 [](const CXXMemberCallExpr *E,
1318 return diagnoseUnwrapCall(
1319 E->getImplicitObjectArgument(), Env);
1322 return std::move(Builder).Build();
1341 TransferMatchSwitch(buildTransferMatchSwitch()) {
1343 [&Ctx](
QualType Ty) -> llvm::StringMap<QualType> {
1345 return {{
"success", Ctx.
BoolTy}};
1351 return {{
"value", valueTypeFromOptionalDecl(*
Optional)},
1352 {
"has_value", Ctx.
BoolTy}};
1359 LatticeTransferState State(L, Env);
1365 : 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...
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::MachO::RecordLoc RecordLoc
Defines an enumeration for C++ overloaded operators.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Represents a top-level expression in a basic block.
Represents a base class of a C++ class.
OverloadedOperatorKind getOperator() const
Returns the kind of overloaded operator that this expression refers to.
Represents a C++ struct/union/class.
bool hasDefinition() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
static CharSourceRange getTokenRange(SourceRange R)
DeclContext * getParent()
getParent - Returns the containing DeclContext.
bool isTranslationUnit() const
DeclContext * getDeclContext()
bool isIdentifier() const
Predicate functions for querying what type of name this is.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Represent a C++ namespace.
A (possibly-)qualified type.
The base class of the type hierarchy.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
The Arena owns the objects that model data within an analysis.
Collects cases of a "match switch": a collection of matchers paired with callbacks,...
CFGMatchSwitchBuilder && CaseOfCFGStmt(MatchSwitchMatcher< Stmt > M, MatchSwitchAction< NodeT, State, Result > A) &&
Registers an action A for CFGStmts that will be triggered by the match of the pattern M against the S...
CFGMatchSwitch< State, Result > Build() &&
ASTContext & getASTContext() final
DataflowAnalysis(ASTContext &Context)
Holds the state of the program (store and heap) at a given program point.
DataflowAnalysisContext & getDataflowAnalysisContext() const
Returns the DataflowAnalysisContext used by the environment.
Models a symbolic pointer. Specifically, any value of type T*.
A storage location for a record (struct, class, or union).
Base class for elements of the local variable store and of the heap.
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options={})
UncheckedOptionalAccessModel(ASTContext &Ctx, dataflow::Environment &Env)
void transfer(const CFGElement &Elt, UncheckedOptionalAccessLattice &L, Environment &Env)
static ast_matchers::StatementMatcher operatorCallToOptionalClass()
static ast_matchers::StatementMatcher memberCallToOptionalClass()
Returns a matcher for calls to optional classes diagnosed by this model.
Base class for all values computed by abstract interpretation.
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral
Matches string literals (also matches wide string literals).
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, NamedDecl > namedDecl
Matches a declaration of anything that could have a name.
internal::TrueMatcher anything()
Matches any node.
const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName
Matches NamedDecl nodes that have any of the specified names.
const internal::MapAnyOfMatcher< BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator > binaryOperation
Matches nodes which can be used with binary operators.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl
Matches C++ constructor declarations.
internal::Matcher< Stmt > StatementMatcher
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr
Matches overloaded operator calls.
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const AstTypeMatcher< RecordType > recordType
const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr
Matches member expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
const internal::VariadicDynCastAllOfMatcher< 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::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr
Matches implicit and explicit this expressions.
void transferAssertionResultExpectationOperatorBoolCall(const CXXMemberCallExpr *Expr, Environment &Env, llvm::function_ref< StorageLocation &(RecordStorageLocation &)> GetOk)
clang::ast_matchers::StatementMatcher isAssertionResultExpectationOperatorBoolCall()
TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState
CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(clang::StringRef MethodName="value")
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 copyRecord(RecordStorageLocation &Src, RecordStorageLocation &Dst, Environment &Env, QualType TypeToCopy=QualType())
Copies a record (struct, class, or union) from Src to Dst.
void transferSmartPointerLikeCachedGet(const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc, TransferState< LatticeT > &State, llvm::function_ref< void(StorageLocation &)> InitializeLoc)
A transfer function for operator-> (and get) calls that can be cached.
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
static bool isAssertionResultType(QualType Type)
CachedConstAccessorsLattice< NoopLattice > UncheckedOptionalAccessLattice
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
static bool isSupportedOptionalType(QualType Ty)
ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow()
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr
Matches overloaded operator calls.
internal::PolymorphicMatcher< internal::HasOverloadedOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl), std::vector< std::string > > hasOverloadedOperatorName(StringRef Name)
Matches overloaded operator names.
RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)
Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...
static const CXXRecordDecl * getOptionalBaseClass(const CXXRecordDecl *RD)
ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar()
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall(clang::StringRef MethodName="get")
const AstTypeMatcher< ReferenceType > referenceType
bool Cast(InterpState &S, CodePtr OpPC)
bool equals(const til::SExpr *E1, const til::SExpr *E2)
Top level wrappers for InstallAPI frontend operations.
@ Result
The result type of a method or function.
U cast(CodeGen::Address addr)
Diagnostic information for an unchecked optional access.