33#include "llvm/ADT/StringRef.h"
34#include "llvm/Support/ErrorHandling.h"
43template <
class... NameTypes>
51 if constexpr (
sizeof...(NameTypes) > 0) {
54 if (
const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.
getParent()))
66 if (RD.
getName() ==
"optional") {
67 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext()))
68 return N->isStdNamespace() ||
73 if (RD.
getName() ==
"Optional") {
75 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
80 if (RD.
getName() ==
"Optional_Base") {
81 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
82 return N !=
nullptr &&
86 if (RD.
getName() ==
"NullableValue") {
87 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.
getDeclContext());
88 return N !=
nullptr &&
123 if (RD->getName() ==
"AssertionResult")
124 if (
const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))
134using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
138AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
142auto desugarsToOptionalType() {
143 return hasUnqualifiedDesugaredType(
147auto desugarsToOptionalOrDerivedType() {
148 return hasUnqualifiedDesugaredType(
152auto hasOptionalType() {
return hasType(desugarsToOptionalType()); }
156auto hasOptionalOrDerivedType() {
157 return hasType(desugarsToOptionalOrDerivedType());
160bool isDesugaredTypeOptional(QualType Ty) {
161 const Type &DesugaredTy = *Ty->getUnqualifiedDesugaredType();
162 return DesugaredTy.isRecordType() &&
166bool isDesugaredTypeOptionalOrPointerToOptional(QualType Ty) {
167 if (Ty->isPointerType())
168 Ty = Ty->getPointeeType();
169 return isDesugaredTypeOptional(Ty);
197bool hasReceiverTypeDesugaringToOptional(
const Expr *E) {
198 auto *
Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
199 if (Cast ==
nullptr ||
Cast->getCastKind() != CK_UncheckedDerivedToBase)
200 return isDesugaredTypeOptionalOrPointerToOptional(E->getType());
205 if (isDesugaredTypeOptionalOrPointerToOptional(
Cast->getSubExpr()->getType()))
210 for (
const CXXBaseSpecifier *Base :
Cast->path()) {
211 if (isDesugaredTypeOptional(
Base->getType()))
218 return hasReceiverTypeDesugaringToOptional(
Cast->getSubExpr());
221AST_MATCHER(CXXMemberCallExpr, hasOptionalReceiverType) {
222 return hasReceiverTypeDesugaringToOptional(Node.getImplicitObjectArgument());
225AST_MATCHER(CXXOperatorCallExpr, hasOptionalOperatorObjectType) {
226 return hasReceiverTypeDesugaringToOptional(Node.getArg(0));
229auto isOptionalMemberCallWithNameMatcher(
230 ast_matchers::internal::Matcher<NamedDecl> matcher,
231 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
237auto isOptionalOperatorCallWithName(
238 llvm::StringRef operator_name,
239 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
245auto isMakeOptionalCall() {
248 "std::make_optional",
"base::make_optional",
"absl::make_optional",
249 "folly::make_optional",
"bsl::make_optional"))),
253auto nulloptTypeDecl() {
255 "base::nullopt_t",
"folly::None",
259auto hasNulloptType() {
return hasType(nulloptTypeDecl()); }
263 "base::in_place_t",
"folly::in_place_t",
267auto isOptionalNulloptConstructor() {
270 hasParameter(0, hasNulloptType()))),
271 hasOptionalOrDerivedType());
274auto isOptionalInPlaceConstructor() {
276 hasOptionalOrDerivedType());
279auto isOptionalValueOrConversionConstructor() {
283 argumentCountIs(1), hasArgument(0,
unless(hasNulloptType())),
284 hasOptionalOrDerivedType());
287auto isOptionalValueOrConversionAssignment() {
292 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
293 argumentCountIs(2), hasArgument(1,
unless(hasNulloptType())));
296auto isOptionalNulloptAssignment() {
300 argumentCountIs(2), hasArgument(1, hasNulloptType()));
303auto isStdSwapCall() {
306 hasArgument(0, hasOptionalOrDerivedType()),
307 hasArgument(1, hasOptionalOrDerivedType()));
310auto isStdForwardCall() {
313 hasArgument(0, hasOptionalOrDerivedType()));
316auto isAssertionResultOperatorBoolCall() {
320 ofClass(
hasName(
"testing::AssertionResult")))));
323auto isAssertionResultConstructFromBoolCall() {
326 hasArgument(0, hasType(booleanType())));
329auto isAssertionResultConstructFromOptionalCall() {
332 hasArgument(0, hasOptionalOrDerivedType()));
335constexpr llvm::StringLiteral ValueOrCallID =
"ValueOrCall";
337auto isValueOrStringEmptyCall() {
341 onImplicitObjectArgument(ignoringImplicit(
344 ofClass(optionalClass()))),
346 .bind(ValueOrCallID))));
349auto isValueOrNotEqX() {
350 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
355 ofClass(optionalClass()))),
357 .bind(ValueOrCallID)),
358 ignoringImplicit(Arg));
372auto isZeroParamConstMemberCall() {
377auto isZeroParamConstMemberOperatorCall() {
382auto isNonConstMemberCall() {
386auto isNonConstMemberOperatorCall() {
390auto isCallReturningOptional() {
392 anyOf(desugarsToOptionalOrDerivedType(),
393 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
396template <
typename L,
typename R>
397auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
400 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
401 hasArgument(1, rhs_arg_matcher));
407 if (
Value !=
nullptr)
408 return Value->formula();
410 Value = &Env.makeAtomicBoolValue();
411 Env.setValue(Expr, *
Value);
412 return Value->formula();
416 return OptionalLoc.getSyntheticField(
"has_value");
420 return OptionalLoc.getSyntheticField(
"value");
425 return AssertResultLoc.getSyntheticField(
"success");
432 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
438 if (OptionalLoc ==
nullptr)
441 auto *HasValueVal = Env.get<
BoolValue>(HasValueLoc);
442 if (HasValueVal ==
nullptr) {
443 HasValueVal = &Env.makeAtomicBoolValue();
444 Env.setValue(HasValueLoc, *HasValueVal);
449QualType valueTypeFromOptionalDecl(
const CXXRecordDecl &RD) {
451 return CTSD.getTemplateArgs()[0].getAsType();
458int countOptionalWrappers(
const ASTContext &ASTCtx, QualType
Type) {
463 return 1 + countOptionalWrappers(
465 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
471 if (
auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
472 return &PointerVal->getPointeeLoc();
475 return Env.getStorageLocation(E);
478void transferUnwrapCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
479 LatticeTransferState &State) {
480 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
481 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
482 if (State.Env.getStorageLocation(*UnwrapExpr) ==
nullptr)
483 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
487void transferArrowOpCall(
const Expr *UnwrapExpr,
const Expr *ObjectExpr,
488 LatticeTransferState &State) {
489 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
490 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
492 *UnwrapExpr, State.Env.create<
PointerValue>(locForValue(*OptionalLoc)));
495void transferMakeOptionalCall(
const CallExpr *E,
496 const MatchFinder::MatchResult &,
497 LatticeTransferState &State) {
498 setHasValue(State.Env.getResultObjectLocation(*E),
499 State.Env.getBoolLiteralValue(
true), State.Env);
502void transferOptionalHasValueCall(
const CXXMemberCallExpr *CallExpr,
503 const MatchFinder::MatchResult &,
504 LatticeTransferState &State) {
505 if (
auto *HasValueVal = getHasValue(
507 State.Env.setValue(*CallExpr, *HasValueVal);
511void transferOptionalIsNullCall(
const CXXMemberCallExpr *CallExpr,
512 const MatchFinder::MatchResult &,
513 LatticeTransferState &State) {
514 if (
auto *HasValueVal = getHasValue(
516 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
522void transferValueOrImpl(
523 const clang::Expr *ValueOrPredExpr,
const MatchFinder::MatchResult &
Result,
524 LatticeTransferState &State,
527 auto &Env = State.Env;
530 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
534 if (HasValueVal ==
nullptr)
537 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
538 HasValueVal->formula()));
541void transferValueOrStringEmptyCall(
const clang::Expr *ComparisonExpr,
542 const MatchFinder::MatchResult &
Result,
543 LatticeTransferState &State) {
544 return transferValueOrImpl(ComparisonExpr,
Result, State,
547 auto &A = Env.arena();
554 return A.makeImplies(A.makeNot(ExprVal),
559void transferValueOrNotEqX(
const Expr *ComparisonExpr,
560 const MatchFinder::MatchResult &
Result,
561 LatticeTransferState &State) {
562 transferValueOrImpl(ComparisonExpr,
Result, State,
565 auto &A = Env.arena();
569 return A.makeImplies(ExprVal, HasValueVal);
573void transferCallReturningOptional(
const CallExpr *E,
574 const MatchFinder::MatchResult &
Result,
575 LatticeTransferState &State) {
577 if (E->isPRValue()) {
578 Loc = &State.Env.getResultObjectLocation(*E);
581 if (Loc ==
nullptr) {
583 State.Env.setStorageLocation(*E, *Loc);
587 if (State.Env.getValue(locForHasValue(*Loc)) !=
nullptr)
590 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
596bool handleConstMemberCall(
const CallExpr *CE,
597 dataflow::RecordStorageLocation *
RecordLoc,
598 const MatchFinder::MatchResult &
Result,
599 LatticeTransferState &State) {
604 if (CE->isGLValue()) {
605 const FunctionDecl *DirectCallee = CE->getDirectCallee();
606 if (DirectCallee ==
nullptr)
615 State.Env.makeAtomicBoolValue(), State.Env);
618 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
621 State.Env.setStorageLocation(*CE, Loc);
625 if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {
627 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*
RecordLoc, CE,
631 State.Env.setValue(*CE, *Val);
636 const FunctionDecl *DirectCallee = CE->getDirectCallee();
637 if (DirectCallee ==
nullptr)
640 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
642 setHasValue(cast<RecordStorageLocation>(Loc),
643 State.Env.makeAtomicBoolValue(), State.Env);
647 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
655void handleConstMemberCallWithFallbacks(
656 const CallExpr *CE, dataflow::RecordStorageLocation *
RecordLoc,
657 const MatchFinder::MatchResult &
Result, LatticeTransferState &State) {
663 transferCallReturningOptional(CE,
Result, State);
666void transferConstMemberCall(
const CXXMemberCallExpr *MCE,
667 const MatchFinder::MatchResult &
Result,
668 LatticeTransferState &State) {
669 handleConstMemberCallWithFallbacks(
673void transferConstMemberOperatorCall(
const CXXOperatorCallExpr *OCE,
674 const MatchFinder::MatchResult &
Result,
675 LatticeTransferState &State) {
676 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
677 State.Env.getStorageLocation(*OCE->getArg(0)));
681void handleNonConstMemberCall(
const CallExpr *CE,
682 dataflow::RecordStorageLocation *
RecordLoc,
683 const MatchFinder::MatchResult &
Result,
684 LatticeTransferState &State) {
689 for (
const auto &[Field, FieldLoc] :
RecordLoc->children()) {
690 QualType FieldType =
Field->getType();
691 if (!FieldType.isConstQualified() &&
693 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
694 if (FieldRecordLoc) {
695 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
700 State.Lattice.clearConstMethodReturnValues(*
RecordLoc);
701 State.Lattice.clearConstMethodReturnStorageLocations(*
RecordLoc);
706 transferCallReturningOptional(CE,
Result, State);
710void transferValue_NonConstMemberCall(
const CXXMemberCallExpr *MCE,
711 const MatchFinder::MatchResult &
Result,
712 LatticeTransferState &State) {
713 handleNonConstMemberCall(
717void transferValue_NonConstMemberOperatorCall(
718 const CXXOperatorCallExpr *OCE,
const MatchFinder::MatchResult &
Result,
719 LatticeTransferState &State) {
720 auto *
RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
721 State.Env.getStorageLocation(*OCE->getArg(0)));
725void constructOptionalValue(
const Expr &E,
Environment &Env,
728 setHasValue(Loc, HasValueVal, Env);
734BoolValue &valueOrConversionHasValue(QualType DestType,
const Expr &E,
735 const MatchFinder::MatchResult &MatchRes,
736 LatticeTransferState &State) {
737 const int DestTypeOptionalWrappersCount =
738 countOptionalWrappers(*MatchRes.Context, DestType);
739 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
740 *MatchRes.Context, E.getType().getNonReferenceType());
747 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
748 return State.Env.getBoolLiteralValue(
true);
755 if (
auto *HasValueVal = getHasValue(State.Env, Loc))
757 return State.Env.makeAtomicBoolValue();
760void transferValueOrConversionConstructor(
761 const CXXConstructExpr *E,
const MatchFinder::MatchResult &MatchRes,
762 LatticeTransferState &State) {
763 assert(E->getNumArgs() > 0);
765 constructOptionalValue(
767 valueOrConversionHasValue(
768 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
772void transferAssignment(
const CXXOperatorCallExpr *E,
BoolValue &HasValueVal,
773 LatticeTransferState &State) {
774 assert(E->getNumArgs() > 0);
777 setHasValue(*Loc, HasValueVal, State.Env);
780 State.Env.setStorageLocation(*E, *Loc);
784void transferValueOrConversionAssignment(
785 const CXXOperatorCallExpr *E,
const MatchFinder::MatchResult &MatchRes,
786 LatticeTransferState &State) {
787 assert(E->getNumArgs() > 1);
790 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
791 *E->getArg(1), MatchRes, State),
795void transferNulloptAssignment(
const CXXOperatorCallExpr *E,
796 const MatchFinder::MatchResult &,
797 LatticeTransferState &State) {
798 transferAssignment(E, State.Env.getBoolLiteralValue(
false), State);
807 if (Loc1 ==
nullptr) {
809 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
812 if (Loc2 ==
nullptr) {
813 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
823 BoolValue *BoolVal1 = getHasValue(Env, Loc1);
824 if (BoolVal1 ==
nullptr)
825 BoolVal1 = &Env.makeAtomicBoolValue();
827 BoolValue *BoolVal2 = getHasValue(Env, Loc2);
828 if (BoolVal2 ==
nullptr)
829 BoolVal2 = &Env.makeAtomicBoolValue();
831 setHasValue(*Loc1, *BoolVal2, Env);
832 setHasValue(*Loc2, *BoolVal1, Env);
835void transferSwapCall(
const CXXMemberCallExpr *E,
836 const MatchFinder::MatchResult &,
837 LatticeTransferState &State) {
838 assert(E->getNumArgs() == 1);
843void transferStdSwapCall(
const CallExpr *E,
const MatchFinder::MatchResult &,
844 LatticeTransferState &State) {
845 assert(E->getNumArgs() == 2);
848 transferSwap(Arg0Loc, Arg1Loc, State.Env);
851void transferStdForwardCall(
const CallExpr *E,
const MatchFinder::MatchResult &,
852 LatticeTransferState &State) {
853 assert(E->getNumArgs() == 1);
855 if (
auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
856 State.Env.setStorageLocation(*E, *Loc);
876 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
877 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
878 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
881void transferOptionalAndOptionalCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
882 const MatchFinder::MatchResult &,
883 LatticeTransferState &State) {
885 auto &A = Env.
arena();
886 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
888 if (
auto *LHasVal = getHasValue(Env, Arg0Loc)) {
890 if (
auto *RHasVal = getHasValue(Env, Arg1Loc)) {
891 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
892 CmpValue = &A.makeNot(*CmpValue);
893 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
894 RHasVal->formula()));
899void transferOptionalAndValueCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
901 auto &A = Env.arena();
902 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
904 if (
auto *HasVal = getHasValue(Env, Loc)) {
905 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
906 CmpValue = &A.makeNot(*CmpValue);
908 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(
true)));
912void transferOptionalAndNulloptCmp(
const clang::CXXOperatorCallExpr *CmpExpr,
914 auto &A = Env.arena();
915 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
917 if (
auto *HasVal = getHasValue(Env, Loc)) {
918 if (CmpExpr->
getOperator() == clang::OO_ExclaimEqual)
919 CmpValue = &A.makeNot(*CmpValue);
920 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
921 A.makeLiteral(
false)));
925void transferAssertionResultOperatorBoolCall(
const CXXMemberCallExpr *Expr,
926 const MatchFinder::MatchResult &,
927 LatticeTransferState &State) {
929 if (AssertResultLoc ==
nullptr)
933 locForAssertResultSuccess(*AssertResultLoc))) {
934 State.Env.setValue(*Expr, *SuccessVal);
938void transferAssertionResultConstructFromBoolCall(
939 const CXXConstructExpr *ConstructExpr,
const MatchFinder::MatchResult &,
940 LatticeTransferState &State) {
941 assert(ConstructExpr->getNumArgs() > 0);
942 const Expr *Arg = ConstructExpr->getArg(0)->IgnoreImplicit();
945 auto &ResultLoc = State.Env.getResultObjectLocation(*ConstructExpr);
946 State.Env.setValue(locForAssertResultSuccess(ResultLoc), *SuccessVal);
950void transferAssertionResultConstructFromOptionalCall(
951 const CXXConstructExpr *ConstructExpr,
const MatchFinder::MatchResult &,
952 LatticeTransferState &State) {
953 assert(ConstructExpr->getNumArgs() > 0);
955 const Expr *Arg = ConstructExpr->getArg(0)->IgnoreImplicit();
957 cast_or_null<RecordStorageLocation>(State.Env.getStorageLocation(*Arg));
958 if (OptionalLoc ==
nullptr)
961 if (
BoolValue *HasVal = getHasValue(State.Env, OptionalLoc)) {
962 auto &ResultLoc = State.Env.getResultObjectLocation(*ConstructExpr);
963 State.Env.setValue(locForAssertResultSuccess(ResultLoc), *HasVal);
967std::optional<StatementMatcher>
969 if (Options.IgnoreSmartPointerDereference) {
972 unless(hasArgument(0,
expr(hasOptionalType()))))));
980valueCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
981 return isOptionalMemberCallWithNameMatcher(
hasName(
"value"),
986valueOperatorCall(
const std::optional<StatementMatcher> &IgnorableOptional) {
987 return expr(
anyOf(isOptionalOperatorCallWithName(
"*", IgnorableOptional),
988 isOptionalOperatorCallWithName(
"->", IgnorableOptional)));
997 .
CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
1000 .CaseOfCFGStmt<CXXConstructExpr>(
1001 isOptionalInPlaceConstructor(),
1002 [](
const CXXConstructExpr *E,
const MatchFinder::MatchResult &,
1003 LatticeTransferState &State) {
1004 constructOptionalValue(*E, State.Env,
1005 State.Env.getBoolLiteralValue(
true));
1008 .CaseOfCFGStmt<CXXConstructExpr>(
1009 isOptionalNulloptConstructor(),
1010 [](
const CXXConstructExpr *E,
const MatchFinder::MatchResult &,
1011 LatticeTransferState &State) {
1012 constructOptionalValue(*E, State.Env,
1013 State.Env.getBoolLiteralValue(
false));
1016 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
1017 transferValueOrConversionConstructor)
1021 isOptionalValueOrConversionAssignment(),
1022 transferValueOrConversionAssignment)
1023 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
1024 transferNulloptAssignment)
1028 valueCall(std::nullopt),
1029 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1031 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
1035 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"*"),
1036 [](
const CallExpr *E,
1037 const MatchFinder::MatchResult &,
1038 LatticeTransferState &State) {
1039 transferUnwrapCall(E, E->getArg(0), State);
1043 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName(
"->"),
1044 [](
const CallExpr *E,
1045 const MatchFinder::MatchResult &,
1046 LatticeTransferState &State) {
1047 transferArrowOpCall(E, E->getArg(0), State);
1053 .CaseOfCFGStmt<CXXMemberCallExpr>(
1054 isOptionalMemberCallWithNameMatcher(
1056 transferOptionalHasValueCall)
1060 isOptionalMemberCallWithNameMatcher(
hasName(
"operator bool")),
1061 transferOptionalHasValueCall)
1065 .CaseOfCFGStmt<CXXMemberCallExpr>(
1066 isOptionalMemberCallWithNameMatcher(
hasName(
"isNull")),
1067 transferOptionalIsNullCall)
1073 isOptionalMemberCallWithNameMatcher(
1074 hasAnyName(
"makeValue",
"makeValueInplace")),
1075 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1079 setHasValue(*Loc, State.Env.getBoolLiteralValue(
true), State.Env);
1084 .CaseOfCFGStmt<CXXMemberCallExpr>(
1085 isOptionalMemberCallWithNameMatcher(
hasName(
"emplace")),
1086 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1087 LatticeTransferState &State) {
1090 setHasValue(*Loc, State.Env.getBoolLiteralValue(
true), State.Env);
1095 .CaseOfCFGStmt<CXXMemberCallExpr>(
1096 isOptionalMemberCallWithNameMatcher(
hasName(
"reset")),
1097 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &,
1098 LatticeTransferState &State) {
1101 setHasValue(*Loc, State.Env.getBoolLiteralValue(
false),
1107 .CaseOfCFGStmt<CXXMemberCallExpr>(
1108 isOptionalMemberCallWithNameMatcher(
hasName(
"swap")),
1112 .
CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
1115 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
1119 transferValueOrStringEmptyCall)
1122 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1126 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1127 transferOptionalAndOptionalCmp)
1128 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1129 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1130 [](
const clang::CXXOperatorCallExpr *
Cmp,
1131 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1132 transferOptionalAndNulloptCmp(
Cmp,
Cmp->getArg(0), State.Env);
1134 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1135 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1136 [](
const clang::CXXOperatorCallExpr *
Cmp,
1137 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1138 transferOptionalAndNulloptCmp(
Cmp,
Cmp->getArg(1), State.Env);
1140 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1141 isComparisonOperatorCall(
1143 unless(
anyOf(hasOptionalType(), hasNulloptType()))),
1144 [](
const clang::CXXOperatorCallExpr *
Cmp,
1145 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1146 transferOptionalAndValueCmp(
Cmp,
Cmp->getArg(0), State.Env);
1148 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1149 isComparisonOperatorCall(
1150 unless(
anyOf(hasOptionalType(), hasNulloptType())),
1152 [](
const clang::CXXOperatorCallExpr *
Cmp,
1153 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1154 transferOptionalAndValueCmp(
Cmp,
Cmp->getArg(1), State.Env);
1160 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1162 [](
const CXXOperatorCallExpr *E,
1163 const MatchFinder::MatchResult &
Result,
1164 LatticeTransferState &State) {
1167 dyn_cast_or_null<RecordStorageLocation>(
1168 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1171 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1173 [](
const CXXOperatorCallExpr *E,
1174 const MatchFinder::MatchResult &
Result,
1175 LatticeTransferState &State) {
1178 dyn_cast_or_null<RecordStorageLocation>(
1179 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1182 .CaseOfCFGStmt<CXXMemberCallExpr>(
1184 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &
Result,
1185 LatticeTransferState &State) {
1190 .CaseOfCFGStmt<CXXMemberCallExpr>(
1192 [](
const CXXMemberCallExpr *E,
const MatchFinder::MatchResult &
Result,
1193 LatticeTransferState &State) {
1200 .CaseOfCFGStmt<CXXMemberCallExpr>(isAssertionResultOperatorBoolCall(),
1201 transferAssertionResultOperatorBoolCall)
1203 isAssertionResultConstructFromBoolCall(),
1204 transferAssertionResultConstructFromBoolCall)
1205 .CaseOfCFGStmt<CXXConstructExpr>(
1206 isAssertionResultConstructFromOptionalCall(),
1207 transferAssertionResultConstructFromOptionalCall)
1209 .
CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1210 transferConstMemberCall)
1211 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1212 transferConstMemberOperatorCall)
1215 transferValue_NonConstMemberCall)
1216 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1217 isNonConstMemberOperatorCall(),
1218 transferValue_NonConstMemberOperatorCall)
1222 transferCallReturningOptional)
1227llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
1228diagnoseUnwrapCall(
const Expr *ObjectExpr,
const Environment &Env) {
1229 if (
auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1230 getLocBehindPossiblePointer(*ObjectExpr, Env))) {
1231 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
1232 if (
auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1233 if (Env.proves(HasValueVal->formula()))
1244auto buildDiagnoseMatchSwitch(
1249 const auto IgnorableOptional = ignorableOptional(Options);
1254 llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()
1256 .CaseOfCFGStmt<CallExpr>(
1257 valueOperatorCall(IgnorableOptional),
1258 [](
const CallExpr *E,
const MatchFinder::MatchResult &,
1260 return diagnoseUnwrapCall(E->getArg(0), Env);
1263 auto Builder = Options.IgnoreValueCalls
1264 ? std::move(DiagBuilder)
1265 : std::move(DiagBuilder)
1267 .CaseOfCFGStmt<CXXMemberCallExpr>(
1268 valueCall(IgnorableOptional),
1269 [](const CXXMemberCallExpr *E,
1272 return diagnoseUnwrapCall(
1273 E->getImplicitObjectArgument(), Env);
1276 return std::move(Builder).Build();
1290 TransferMatchSwitch(buildTransferMatchSwitch()) {
1292 [&Ctx](
QualType Ty) -> llvm::StringMap<QualType> {
1294 return {{
"success", Ctx.
BoolTy}};
1300 return {{
"value", valueTypeFromOptionalDecl(*
Optional)},
1301 {
"has_value", Ctx.
BoolTy}};
1308 LatticeTransferState State(L, Env);
1314 : 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...
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::DeclarationMatcher optionalClassDecl()
Returns a matcher for the optional classes covered 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.
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
const internal::VariadicDynCastAllOfMatcher< Stmt, StringLiteral > stringLiteral
Matches string literals (also matches wide string literals).
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, NamedDecl > namedDecl
Matches a declaration of anything that could have a name.
internal::TrueMatcher anything()
Matches any node.
const internal::VariadicFunction< internal::Matcher< NamedDecl >, StringRef, internal::hasAnyNameFunc > hasAnyName
Matches NamedDecl nodes that have any of the specified names.
const internal::MapAnyOfMatcher< BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator > binaryOperation
Matches nodes which can be used with binary operators.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl
Matches C++ constructor declarations.
internal::Matcher< Stmt > StatementMatcher
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
const AstTypeMatcher< RecordType > recordType
const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr
Matches member expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
const internal::VariadicDynCastAllOfMatcher< 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.
TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState
CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)
Dataflow Directional Tag Classes.
ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(clang::StringRef MethodName="value")
static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS, llvm::StringRef Name, NameTypes... Names)
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)
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
@ Type
The name was classified as a type.
U cast(CodeGen::Address addr)
Diagnostic information for an unchecked optional access.