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