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