1 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
2 #include "clang/AST/ASTContext.h"
3 #include "clang/AST/Expr.h"
4 #include "clang/AST/ExprCXX.h"
5 #include "clang/AST/Stmt.h"
6 #include "clang/ASTMatchers/ASTMatchers.h"
7 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
8 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
9 #include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
10 #include "clang/Analysis/FlowSensitive/Value.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/Support/Casting.h"
13 #include <cassert>
14 
15 namespace clang {
16 namespace dataflow {
17 namespace {
18 
19 using namespace ::clang::ast_matchers;
20 
21 using LatticeTransferState = TransferState<SourceLocationsLattice>;
22 
23 static auto optionalClass() {
24   return classTemplateSpecializationDecl(
25       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
26             hasName("__optional_destruct_base"), hasName("absl::optional"),
27             hasName("base::Optional")),
28       hasTemplateArgument(0, refersToType(type().bind("T"))));
29 }
30 
31 static auto hasOptionalType() { return hasType(optionalClass()); }
32 
33 static auto isOptionalMemberCallWithName(llvm::StringRef MemberName) {
34   return cxxMemberCallExpr(
35       on(expr(unless(cxxThisExpr()))),
36       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
37 }
38 
39 static auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
40   return cxxOperatorCallExpr(hasOverloadedOperatorName(OperatorName),
41                              callee(cxxMethodDecl(ofClass(optionalClass()))));
42 }
43 
44 /// Returns the symbolic value that represents the "has_value" property of the
45 /// optional value `Val`. Returns null if `Val` is null.
46 static BoolValue *getHasValue(Value *Val) {
47   if (auto *OptionalVal = cast_or_null<StructValue>(Val)) {
48     return cast<BoolValue>(OptionalVal->getProperty("has_value"));
49   }
50   return nullptr;
51 }
52 
53 static void initializeOptionalReference(const Expr *OptionalExpr,
54                                         LatticeTransferState &State) {
55   if (auto *OptionalVal = cast_or_null<StructValue>(
56           State.Env.getValue(*OptionalExpr, SkipPast::Reference))) {
57     if (OptionalVal->getProperty("has_value") == nullptr) {
58       OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue());
59     }
60   }
61 }
62 
63 static void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
64                                LatticeTransferState &State) {
65   if (auto *OptionalVal = cast_or_null<StructValue>(
66           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer))) {
67     auto *HasValueVal = getHasValue(OptionalVal);
68     assert(HasValueVal != nullptr);
69 
70     if (State.Env.flowConditionImplies(*HasValueVal))
71       return;
72   }
73 
74   // Record that this unwrap is *not* provably safe.
75   State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
76 }
77 
78 static void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
79                                          LatticeTransferState &State) {
80   if (auto *OptionalVal = cast_or_null<StructValue>(
81           State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
82                              SkipPast::ReferenceThenPointer))) {
83     auto *HasValueVal = getHasValue(OptionalVal);
84     assert(HasValueVal != nullptr);
85 
86     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
87     State.Env.setValue(CallExprLoc, *HasValueVal);
88     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
89   }
90 }
91 
92 static auto buildTransferMatchSwitch() {
93   return MatchSwitchBuilder<LatticeTransferState>()
94       // Attach a symbolic "has_value" state to optional values that we see for
95       // the first time.
96       .CaseOf(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
97               initializeOptionalReference)
98 
99       // optional::value
100       .CaseOf(
101           isOptionalMemberCallWithName("value"),
102           +[](const CXXMemberCallExpr *E, LatticeTransferState &State) {
103             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
104           })
105 
106       // optional::operator*, optional::operator->
107       .CaseOf(
108           expr(anyOf(isOptionalOperatorCallWithName("*"),
109                      isOptionalOperatorCallWithName("->"))),
110           +[](const CallExpr *E, LatticeTransferState &State) {
111             transferUnwrapCall(E, E->getArg(0), State);
112           })
113 
114       // optional::has_value
115       .CaseOf(isOptionalMemberCallWithName("has_value"),
116               transferOptionalHasValueCall)
117 
118       .Build();
119 }
120 
121 } // namespace
122 
123 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
124     : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
125           Ctx),
126       TransferMatchSwitch(buildTransferMatchSwitch()) {}
127 
128 void UncheckedOptionalAccessModel::transfer(const Stmt *S,
129                                             SourceLocationsLattice &L,
130                                             Environment &Env) {
131   LatticeTransferState State(L, Env);
132   TransferMatchSwitch(*S, getASTContext(), State);
133 }
134 
135 } // namespace dataflow
136 } // namespace clang
137