|
25 | 25 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
26 | 26 | #include "clang/Analysis/FlowSensitive/MatchSwitch.h" |
27 | 27 | #include "clang/Analysis/FlowSensitive/RecordOps.h" |
| 28 | +#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" |
28 | 29 | #include "clang/Analysis/FlowSensitive/StorageLocation.h" |
29 | 30 | #include "clang/Analysis/FlowSensitive/Value.h" |
30 | 31 | #include "clang/Basic/LLVM.h" |
@@ -237,6 +238,49 @@ static auto isAsStatusCallWithStatusOr() { |
237 | 238 | hasArgument(0, hasType(statusOrType()))); |
238 | 239 | } |
239 | 240 |
|
| 241 | +static auto possiblyReferencedStatusOrType() { |
| 242 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 243 | + return anyOf(statusOrType(), referenceType(pointee(statusOrType()))); |
| 244 | +} |
| 245 | + |
| 246 | +static auto isConstStatusOrAccessorMemberCall() { |
| 247 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 248 | + return cxxMemberCallExpr(callee( |
| 249 | + cxxMethodDecl(parameterCountIs(0), isConst(), |
| 250 | + returns(qualType(possiblyReferencedStatusOrType()))))); |
| 251 | +} |
| 252 | + |
| 253 | +static auto isConstStatusOrAccessorMemberOperatorCall() { |
| 254 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 255 | + return cxxOperatorCallExpr( |
| 256 | + callee(cxxMethodDecl(parameterCountIs(0), isConst(), |
| 257 | + returns(possiblyReferencedStatusOrType())))); |
| 258 | +} |
| 259 | + |
| 260 | +static auto isConstStatusOrPointerAccessorMemberCall() { |
| 261 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 262 | + return cxxMemberCallExpr(callee(cxxMethodDecl( |
| 263 | + parameterCountIs(0), isConst(), |
| 264 | + returns(pointerType(pointee(possiblyReferencedStatusOrType())))))); |
| 265 | +} |
| 266 | + |
| 267 | +static auto isConstStatusOrPointerAccessorMemberOperatorCall() { |
| 268 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 269 | + return cxxOperatorCallExpr(callee(cxxMethodDecl( |
| 270 | + parameterCountIs(0), isConst(), |
| 271 | + returns(pointerType(pointee(possiblyReferencedStatusOrType())))))); |
| 272 | +} |
| 273 | + |
| 274 | +static auto isNonConstMemberCall() { |
| 275 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 276 | + return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst())))); |
| 277 | +} |
| 278 | + |
| 279 | +static auto isNonConstMemberOperatorCall() { |
| 280 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 281 | + return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst())))); |
| 282 | +} |
| 283 | + |
240 | 284 | static auto |
241 | 285 | buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) { |
242 | 286 | return CFGMatchSwitchBuilder<const Environment, |
@@ -697,6 +741,117 @@ static void transferPointerToBoolean(const ImplicitCastExpr *Expr, |
697 | 741 | dyn_cast_or_null<BoolValue>(State.Env.getValue(*Expr->getSubExpr()))) |
698 | 742 | State.Env.setValue(*Expr, *SubExprVal); |
699 | 743 | } |
| 744 | +static void handleConstStatusOrAccessorMemberCall( |
| 745 | + const CallExpr *Expr, RecordStorageLocation *RecordLoc, |
| 746 | + const MatchFinder::MatchResult &Result, LatticeTransferState &State) { |
| 747 | + assert(isStatusOrType(Expr->getType())); |
| 748 | + if (RecordLoc == nullptr) |
| 749 | + return; |
| 750 | + const FunctionDecl *DirectCallee = Expr->getDirectCallee(); |
| 751 | + if (DirectCallee == nullptr) |
| 752 | + return; |
| 753 | + StorageLocation &Loc = |
| 754 | + State.Lattice.getOrCreateConstMethodReturnStorageLocation( |
| 755 | + *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) { |
| 756 | + initializeStatusOr(cast<RecordStorageLocation>(Loc), State.Env); |
| 757 | + }); |
| 758 | + if (Expr->isPRValue()) { |
| 759 | + auto &ResultLoc = State.Env.getResultObjectLocation(*Expr); |
| 760 | + copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env); |
| 761 | + } else { |
| 762 | + State.Env.setStorageLocation(*Expr, Loc); |
| 763 | + } |
| 764 | +} |
| 765 | + |
| 766 | +static void handleConstStatusOrPointerAccessorMemberCall( |
| 767 | + const CallExpr *Expr, RecordStorageLocation *RecordLoc, |
| 768 | + const MatchFinder::MatchResult &Result, LatticeTransferState &State) { |
| 769 | + if (RecordLoc == nullptr) |
| 770 | + return; |
| 771 | + auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, Expr, |
| 772 | + State.Env); |
| 773 | + State.Env.setValue(*Expr, *Val); |
| 774 | +} |
| 775 | + |
| 776 | +static void |
| 777 | +transferConstStatusOrAccessorMemberCall(const CXXMemberCallExpr *Expr, |
| 778 | + const MatchFinder::MatchResult &Result, |
| 779 | + LatticeTransferState &State) { |
| 780 | + handleConstStatusOrAccessorMemberCall( |
| 781 | + Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State); |
| 782 | +} |
| 783 | + |
| 784 | +static void transferConstStatusOrAccessorMemberOperatorCall( |
| 785 | + const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result, |
| 786 | + LatticeTransferState &State) { |
| 787 | + auto *RecordLoc = cast_or_null<RecordStorageLocation>( |
| 788 | + State.Env.getStorageLocation(*Expr->getArg(0))); |
| 789 | + handleConstStatusOrAccessorMemberCall(Expr, RecordLoc, Result, State); |
| 790 | +} |
| 791 | + |
| 792 | +static void transferConstStatusOrPointerAccessorMemberCall( |
| 793 | + const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result, |
| 794 | + LatticeTransferState &State) { |
| 795 | + handleConstStatusOrPointerAccessorMemberCall( |
| 796 | + Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State); |
| 797 | +} |
| 798 | + |
| 799 | +static void transferConstStatusOrPointerAccessorMemberOperatorCall( |
| 800 | + const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result, |
| 801 | + LatticeTransferState &State) { |
| 802 | + auto *RecordLoc = cast_or_null<RecordStorageLocation>( |
| 803 | + State.Env.getStorageLocation(*Expr->getArg(0))); |
| 804 | + handleConstStatusOrPointerAccessorMemberCall(Expr, RecordLoc, Result, State); |
| 805 | +} |
| 806 | + |
| 807 | +static void transferStatusOrReturningCall(const CallExpr *Expr, |
| 808 | + LatticeTransferState &State) { |
| 809 | + RecordStorageLocation *StatusOrLoc = |
| 810 | + Expr->isPRValue() ? &State.Env.getResultObjectLocation(*Expr) |
| 811 | + : State.Env.get<RecordStorageLocation>(*Expr); |
| 812 | + if (StatusOrLoc != nullptr && |
| 813 | + State.Env.getValue(locForOk(locForStatus(*StatusOrLoc))) == nullptr) |
| 814 | + initializeStatusOr(*StatusOrLoc, State.Env); |
| 815 | +} |
| 816 | + |
| 817 | +static void handleNonConstMemberCall(const CallExpr *Expr, |
| 818 | + RecordStorageLocation *RecordLoc, |
| 819 | + const MatchFinder::MatchResult &Result, |
| 820 | + LatticeTransferState &State) { |
| 821 | + if (RecordLoc == nullptr) |
| 822 | + return; |
| 823 | + State.Lattice.clearConstMethodReturnValues(*RecordLoc); |
| 824 | + State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc); |
| 825 | + |
| 826 | + if (isStatusOrType(Expr->getType())) |
| 827 | + transferStatusOrReturningCall(Expr, State); |
| 828 | +} |
| 829 | + |
| 830 | +static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr, |
| 831 | + const MatchFinder::MatchResult &Result, |
| 832 | + LatticeTransferState &State) { |
| 833 | + handleNonConstMemberCall(Expr, getImplicitObjectLocation(*Expr, State.Env), |
| 834 | + Result, State); |
| 835 | +} |
| 836 | + |
| 837 | +static void |
| 838 | +transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr, |
| 839 | + const MatchFinder::MatchResult &Result, |
| 840 | + LatticeTransferState &State) { |
| 841 | + auto *RecordLoc = cast_or_null<RecordStorageLocation>( |
| 842 | + State.Env.getStorageLocation(*Expr->getArg(0))); |
| 843 | + handleNonConstMemberCall(Expr, RecordLoc, Result, State); |
| 844 | +} |
| 845 | + |
| 846 | +static RecordStorageLocation * |
| 847 | +getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) { |
| 848 | + if (!E.isPRValue()) |
| 849 | + return dyn_cast_or_null<RecordStorageLocation>(Env.getStorageLocation(E)); |
| 850 | + if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E))) |
| 851 | + return dyn_cast_or_null<RecordStorageLocation>( |
| 852 | + &PointerVal->getPointeeLoc()); |
| 853 | + return nullptr; |
| 854 | +} |
700 | 855 |
|
701 | 856 | CFGMatchSwitch<LatticeTransferState> |
702 | 857 | buildTransferMatchSwitch(ASTContext &Ctx, |
@@ -755,6 +910,60 @@ buildTransferMatchSwitch(ASTContext &Ctx, |
755 | 910 | transferLoggingGetReferenceableValueCall) |
756 | 911 | .CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(), |
757 | 912 | transferLoggingCheckEqImpl) |
| 913 | + // This needs to go before the const accessor call matcher, because these |
| 914 | + // look like them, but we model `operator`* and `get` to return the same |
| 915 | + // object. Also, we model them for non-const cases. |
| 916 | + .CaseOfCFGStmt<CXXOperatorCallExpr>( |
| 917 | + isPointerLikeOperatorStar(), |
| 918 | + [](const CXXOperatorCallExpr *E, |
| 919 | + const MatchFinder::MatchResult &Result, |
| 920 | + LatticeTransferState &State) { |
| 921 | + transferSmartPointerLikeCachedDeref( |
| 922 | + E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env), |
| 923 | + State, [](StorageLocation &Loc) {}); |
| 924 | + }) |
| 925 | + .CaseOfCFGStmt<CXXOperatorCallExpr>( |
| 926 | + isPointerLikeOperatorArrow(), |
| 927 | + [](const CXXOperatorCallExpr *E, |
| 928 | + const MatchFinder::MatchResult &Result, |
| 929 | + LatticeTransferState &State) { |
| 930 | + transferSmartPointerLikeCachedGet( |
| 931 | + E, getSmartPtrLikeStorageLocation(*E->getArg(0), State.Env), |
| 932 | + State, [](StorageLocation &Loc) {}); |
| 933 | + }) |
| 934 | + .CaseOfCFGStmt<CXXMemberCallExpr>( |
| 935 | + isSmartPointerLikeValueMethodCall(), |
| 936 | + [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, |
| 937 | + LatticeTransferState &State) { |
| 938 | + transferSmartPointerLikeCachedDeref( |
| 939 | + E, getImplicitObjectLocation(*E, State.Env), State, |
| 940 | + [](StorageLocation &Loc) {}); |
| 941 | + }) |
| 942 | + .CaseOfCFGStmt<CXXMemberCallExpr>( |
| 943 | + isSmartPointerLikeGetMethodCall(), |
| 944 | + [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result, |
| 945 | + LatticeTransferState &State) { |
| 946 | + transferSmartPointerLikeCachedGet( |
| 947 | + E, getImplicitObjectLocation(*E, State.Env), State, |
| 948 | + [](StorageLocation &Loc) {}); |
| 949 | + }) |
| 950 | + // const accessor calls |
| 951 | + .CaseOfCFGStmt<CXXMemberCallExpr>(isConstStatusOrAccessorMemberCall(), |
| 952 | + transferConstStatusOrAccessorMemberCall) |
| 953 | + .CaseOfCFGStmt<CXXOperatorCallExpr>( |
| 954 | + isConstStatusOrAccessorMemberOperatorCall(), |
| 955 | + transferConstStatusOrAccessorMemberOperatorCall) |
| 956 | + .CaseOfCFGStmt<CXXMemberCallExpr>( |
| 957 | + isConstStatusOrPointerAccessorMemberCall(), |
| 958 | + transferConstStatusOrPointerAccessorMemberCall) |
| 959 | + .CaseOfCFGStmt<CXXOperatorCallExpr>( |
| 960 | + isConstStatusOrPointerAccessorMemberOperatorCall(), |
| 961 | + transferConstStatusOrPointerAccessorMemberOperatorCall) |
| 962 | + // non-const member calls that may modify the state of an object. |
| 963 | + .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(), |
| 964 | + transferNonConstMemberCall) |
| 965 | + .CaseOfCFGStmt<CXXOperatorCallExpr>(isNonConstMemberOperatorCall(), |
| 966 | + transferNonConstMemberOperatorCall) |
758 | 967 | // N.B. These need to come after all other CXXConstructExpr. |
759 | 968 | // These are there to make sure that every Status and StatusOr object |
760 | 969 | // have their ok boolean initialized when constructed. If we were to |
|
0 commit comments