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 #include <memory>
15 #include <utility>
16 
17 namespace clang {
18 namespace dataflow {
19 namespace {
20 
21 using namespace ::clang::ast_matchers;
22 
23 using LatticeTransferState = TransferState<SourceLocationsLattice>;
24 
25 static auto optionalClass() {
26   return classTemplateSpecializationDecl(
27       anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
28             hasName("__optional_destruct_base"), hasName("absl::optional"),
29             hasName("base::Optional")),
30       hasTemplateArgument(0, refersToType(type().bind("T"))));
31 }
32 
33 static auto hasOptionalType() { return hasType(optionalClass()); }
34 
35 static auto isOptionalMemberCallWithName(llvm::StringRef MemberName) {
36   return cxxMemberCallExpr(
37       on(expr(unless(cxxThisExpr()))),
38       callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
39 }
40 
41 static auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
42   return cxxOperatorCallExpr(hasOverloadedOperatorName(OperatorName),
43                              callee(cxxMethodDecl(ofClass(optionalClass()))));
44 }
45 
46 static auto isMakeOptionalCall() {
47   return callExpr(
48       callee(functionDecl(hasAnyName(
49           "std::make_optional", "base::make_optional", "absl::make_optional"))),
50       hasOptionalType());
51 }
52 
53 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
54 /// symbolic value of its "has_value" property.
55 StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
56   auto OptionalVal = std::make_unique<StructValue>();
57   OptionalVal->setProperty("has_value", HasValueVal);
58   return Env.takeOwnership(std::move(OptionalVal));
59 }
60 
61 /// Returns the symbolic value that represents the "has_value" property of the
62 /// optional value `Val`. Returns null if `Val` is null.
63 static BoolValue *getHasValue(Value *Val) {
64   if (auto *OptionalVal = cast_or_null<StructValue>(Val)) {
65     return cast<BoolValue>(OptionalVal->getProperty("has_value"));
66   }
67   return nullptr;
68 }
69 
70 static void initializeOptionalReference(const Expr *OptionalExpr,
71                                         LatticeTransferState &State) {
72   if (auto *OptionalVal = cast_or_null<StructValue>(
73           State.Env.getValue(*OptionalExpr, SkipPast::Reference))) {
74     if (OptionalVal->getProperty("has_value") == nullptr) {
75       OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue());
76     }
77   }
78 }
79 
80 static void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
81                                LatticeTransferState &State) {
82   if (auto *OptionalVal = cast_or_null<StructValue>(
83           State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer))) {
84     auto *HasValueVal = getHasValue(OptionalVal);
85     assert(HasValueVal != nullptr);
86 
87     if (State.Env.flowConditionImplies(*HasValueVal))
88       return;
89   }
90 
91   // Record that this unwrap is *not* provably safe.
92   State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
93 }
94 
95 void transferMakeOptionalCall(const CallExpr *E, LatticeTransferState &State) {
96   auto &Loc = State.Env.createStorageLocation(*E);
97   State.Env.setStorageLocation(*E, Loc);
98   State.Env.setValue(
99       Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
100 }
101 
102 static void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
103                                          LatticeTransferState &State) {
104   if (auto *OptionalVal = cast_or_null<StructValue>(
105           State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
106                              SkipPast::ReferenceThenPointer))) {
107     auto *HasValueVal = getHasValue(OptionalVal);
108     assert(HasValueVal != nullptr);
109 
110     auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
111     State.Env.setValue(CallExprLoc, *HasValueVal);
112     State.Env.setStorageLocation(*CallExpr, CallExprLoc);
113   }
114 }
115 
116 void transferEmplaceCall(const CXXMemberCallExpr *E,
117                          LatticeTransferState &State) {
118   if (auto *OptionalLoc = State.Env.getStorageLocation(
119           *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer)) {
120     State.Env.setValue(
121         *OptionalLoc,
122         createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
123   }
124 }
125 
126 void transferResetCall(const CXXMemberCallExpr *E,
127                        LatticeTransferState &State) {
128   if (auto *OptionalLoc = State.Env.getStorageLocation(
129           *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer)) {
130     State.Env.setValue(
131         *OptionalLoc,
132         createOptionalValue(State.Env, State.Env.getBoolLiteralValue(false)));
133   }
134 }
135 
136 static auto buildTransferMatchSwitch() {
137   return MatchSwitchBuilder<LatticeTransferState>()
138       // Attach a symbolic "has_value" state to optional values that we see for
139       // the first time.
140       .CaseOf(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
141               initializeOptionalReference)
142 
143       // make_optional
144       .CaseOf(isMakeOptionalCall(), transferMakeOptionalCall)
145 
146       // optional::value
147       .CaseOf(
148           isOptionalMemberCallWithName("value"),
149           +[](const CXXMemberCallExpr *E, LatticeTransferState &State) {
150             transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
151           })
152 
153       // optional::operator*, optional::operator->
154       .CaseOf(
155           expr(anyOf(isOptionalOperatorCallWithName("*"),
156                      isOptionalOperatorCallWithName("->"))),
157           +[](const CallExpr *E, LatticeTransferState &State) {
158             transferUnwrapCall(E, E->getArg(0), State);
159           })
160 
161       // optional::has_value
162       .CaseOf(isOptionalMemberCallWithName("has_value"),
163               transferOptionalHasValueCall)
164 
165       // optional::operator bool
166       .CaseOf(isOptionalMemberCallWithName("operator bool"),
167               transferOptionalHasValueCall)
168 
169       // optional::emplace
170       .CaseOf(isOptionalMemberCallWithName("emplace"), transferEmplaceCall)
171 
172       // optional::reset
173       .CaseOf(isOptionalMemberCallWithName("reset"), transferResetCall)
174 
175       .Build();
176 }
177 
178 } // namespace
179 
180 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
181     : DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
182           Ctx),
183       TransferMatchSwitch(buildTransferMatchSwitch()) {}
184 
185 void UncheckedOptionalAccessModel::transfer(const Stmt *S,
186                                             SourceLocationsLattice &L,
187                                             Environment &Env) {
188   LatticeTransferState State(L, Env);
189   TransferMatchSwitch(*S, getASTContext(), State);
190 }
191 
192 } // namespace dataflow
193 } // namespace clang
194