mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-01 04:16:08 +00:00
137 lines
5.0 KiB
C++
137 lines
5.0 KiB
C++
![]() |
#include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
|
||
|
#include "clang/AST/ASTContext.h"
|
||
|
#include "clang/AST/Expr.h"
|
||
|
#include "clang/AST/ExprCXX.h"
|
||
|
#include "clang/AST/Stmt.h"
|
||
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
||
|
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||
|
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
|
||
|
#include "clang/Analysis/FlowSensitive/SourceLocationsLattice.h"
|
||
|
#include "clang/Analysis/FlowSensitive/Value.h"
|
||
|
#include "llvm/ADT/StringRef.h"
|
||
|
#include "llvm/Support/Casting.h"
|
||
|
#include <cassert>
|
||
|
|
||
|
namespace clang {
|
||
|
namespace dataflow {
|
||
|
namespace {
|
||
|
|
||
|
using namespace ::clang::ast_matchers;
|
||
|
|
||
|
using LatticeTransferState = TransferState<SourceLocationsLattice>;
|
||
|
|
||
|
static auto optionalClass() {
|
||
|
return classTemplateSpecializationDecl(
|
||
|
anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
|
||
|
hasName("__optional_destruct_base"), hasName("absl::optional"),
|
||
|
hasName("base::Optional")),
|
||
|
hasTemplateArgument(0, refersToType(type().bind("T"))));
|
||
|
}
|
||
|
|
||
|
static auto hasOptionalType() { return hasType(optionalClass()); }
|
||
|
|
||
|
static auto isOptionalMemberCallWithName(llvm::StringRef MemberName) {
|
||
|
return cxxMemberCallExpr(
|
||
|
on(expr(unless(cxxThisExpr()))),
|
||
|
callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
|
||
|
}
|
||
|
|
||
|
static auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
|
||
|
return cxxOperatorCallExpr(hasOverloadedOperatorName(OperatorName),
|
||
|
callee(cxxMethodDecl(ofClass(optionalClass()))));
|
||
|
}
|
||
|
|
||
|
/// Returns the symbolic value that represents the "has_value" property of the
|
||
|
/// optional value `Val`. Returns null if `Val` is null.
|
||
|
static BoolValue *getHasValue(Value *Val) {
|
||
|
if (auto *OptionalVal = cast_or_null<StructValue>(Val)) {
|
||
|
return cast<BoolValue>(OptionalVal->getProperty("has_value"));
|
||
|
}
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
static void initializeOptionalReference(const Expr *OptionalExpr,
|
||
|
LatticeTransferState &State) {
|
||
|
if (auto *OptionalVal = cast_or_null<StructValue>(
|
||
|
State.Env.getValue(*OptionalExpr, SkipPast::Reference))) {
|
||
|
if (OptionalVal->getProperty("has_value") == nullptr) {
|
||
|
OptionalVal->setProperty("has_value", State.Env.makeAtomicBoolValue());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
|
||
|
LatticeTransferState &State) {
|
||
|
if (auto *OptionalVal = cast_or_null<StructValue>(
|
||
|
State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer))) {
|
||
|
auto *HasValueVal = getHasValue(OptionalVal);
|
||
|
assert(HasValueVal != nullptr);
|
||
|
|
||
|
if (State.Env.flowConditionImplies(*HasValueVal))
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Record that this unwrap is *not* provably safe.
|
||
|
State.Lattice.getSourceLocations().insert(ObjectExpr->getBeginLoc());
|
||
|
}
|
||
|
|
||
|
static void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
|
||
|
LatticeTransferState &State) {
|
||
|
if (auto *OptionalVal = cast_or_null<StructValue>(
|
||
|
State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
|
||
|
SkipPast::ReferenceThenPointer))) {
|
||
|
auto *HasValueVal = getHasValue(OptionalVal);
|
||
|
assert(HasValueVal != nullptr);
|
||
|
|
||
|
auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
|
||
|
State.Env.setValue(CallExprLoc, *HasValueVal);
|
||
|
State.Env.setStorageLocation(*CallExpr, CallExprLoc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static auto buildTransferMatchSwitch() {
|
||
|
return MatchSwitchBuilder<LatticeTransferState>()
|
||
|
// Attach a symbolic "has_value" state to optional values that we see for
|
||
|
// the first time.
|
||
|
.CaseOf(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
|
||
|
initializeOptionalReference)
|
||
|
|
||
|
// optional::value
|
||
|
.CaseOf(
|
||
|
isOptionalMemberCallWithName("value"),
|
||
|
+[](const CXXMemberCallExpr *E, LatticeTransferState &State) {
|
||
|
transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
|
||
|
})
|
||
|
|
||
|
// optional::operator*, optional::operator->
|
||
|
.CaseOf(
|
||
|
expr(anyOf(isOptionalOperatorCallWithName("*"),
|
||
|
isOptionalOperatorCallWithName("->"))),
|
||
|
+[](const CallExpr *E, LatticeTransferState &State) {
|
||
|
transferUnwrapCall(E, E->getArg(0), State);
|
||
|
})
|
||
|
|
||
|
// optional::has_value
|
||
|
.CaseOf(isOptionalMemberCallWithName("has_value"),
|
||
|
transferOptionalHasValueCall)
|
||
|
|
||
|
.Build();
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx)
|
||
|
: DataflowAnalysis<UncheckedOptionalAccessModel, SourceLocationsLattice>(
|
||
|
Ctx),
|
||
|
TransferMatchSwitch(buildTransferMatchSwitch()) {}
|
||
|
|
||
|
void UncheckedOptionalAccessModel::transfer(const Stmt *S,
|
||
|
SourceLocationsLattice &L,
|
||
|
Environment &Env) {
|
||
|
LatticeTransferState State(L, Env);
|
||
|
TransferMatchSwitch(*S, getASTContext(), State);
|
||
|
}
|
||
|
|
||
|
} // namespace dataflow
|
||
|
} // namespace clang
|