2022-03-16 22:20:05 +00:00
|
|
|
//===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
|
|
|
|
//
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file defines a dataflow analysis that detects unsafe uses of optional
|
|
|
|
// values.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2022-03-10 08:57:32 +00:00
|
|
|
#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>
|
2022-03-10 15:25:42 +00:00
|
|
|
#include <memory>
|
|
|
|
#include <utility>
|
2022-03-10 08:57:32 +00:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace dataflow {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using namespace ::clang::ast_matchers;
|
|
|
|
|
|
|
|
using LatticeTransferState = TransferState<SourceLocationsLattice>;
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto optionalClass() {
|
2022-03-10 08:57:32 +00:00
|
|
|
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"))));
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto hasOptionalType() { return hasType(optionalClass()); }
|
2022-03-10 08:57:32 +00:00
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto isOptionalMemberCallWithName(llvm::StringRef MemberName) {
|
2022-03-10 08:57:32 +00:00
|
|
|
return cxxMemberCallExpr(
|
|
|
|
on(expr(unless(cxxThisExpr()))),
|
|
|
|
callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto isOptionalOperatorCallWithName(llvm::StringRef OperatorName) {
|
2022-03-10 08:57:32 +00:00
|
|
|
return cxxOperatorCallExpr(hasOverloadedOperatorName(OperatorName),
|
|
|
|
callee(cxxMethodDecl(ofClass(optionalClass()))));
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto isMakeOptionalCall() {
|
2022-03-10 15:25:42 +00:00
|
|
|
return callExpr(
|
|
|
|
callee(functionDecl(hasAnyName(
|
|
|
|
"std::make_optional", "base::make_optional", "absl::make_optional"))),
|
|
|
|
hasOptionalType());
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto hasNulloptType() {
|
|
|
|
return hasType(namedDecl(
|
|
|
|
hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto inPlaceClass() {
|
|
|
|
return recordDecl(
|
|
|
|
hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto isOptionalNulloptConstructor() {
|
|
|
|
return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
|
|
|
|
hasArgument(0, hasNulloptType()));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto isOptionalInPlaceConstructor() {
|
|
|
|
return cxxConstructExpr(hasOptionalType(),
|
|
|
|
hasArgument(0, hasType(inPlaceClass())));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto isOptionalValueOrConversionConstructor() {
|
|
|
|
return cxxConstructExpr(
|
|
|
|
hasOptionalType(),
|
|
|
|
unless(hasDeclaration(
|
|
|
|
cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
|
|
|
|
argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
auto isOptionalValueOrConversionAssignment() {
|
|
|
|
return cxxOperatorCallExpr(
|
|
|
|
hasOverloadedOperatorName("="),
|
|
|
|
callee(cxxMethodDecl(ofClass(optionalClass()))),
|
|
|
|
unless(hasDeclaration(cxxMethodDecl(
|
|
|
|
anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
|
|
|
|
argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
|
|
|
|
}
|
|
|
|
|
|
|
|
auto isOptionalNulloptAssignment() {
|
|
|
|
return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
|
|
|
|
callee(cxxMethodDecl(ofClass(optionalClass()))),
|
|
|
|
argumentCountIs(2),
|
|
|
|
hasArgument(1, hasNulloptType()));
|
|
|
|
}
|
|
|
|
|
2022-03-21 12:06:16 +00:00
|
|
|
auto isStdSwapCall() {
|
|
|
|
return callExpr(callee(functionDecl(hasName("std::swap"))),
|
|
|
|
argumentCountIs(2), hasArgument(0, hasOptionalType()),
|
|
|
|
hasArgument(1, hasOptionalType()));
|
|
|
|
}
|
|
|
|
|
2022-03-10 15:25:42 +00:00
|
|
|
/// Creates a symbolic value for an `optional` value using `HasValueVal` as the
|
|
|
|
/// symbolic value of its "has_value" property.
|
|
|
|
StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
|
|
|
|
auto OptionalVal = std::make_unique<StructValue>();
|
|
|
|
OptionalVal->setProperty("has_value", HasValueVal);
|
|
|
|
return Env.takeOwnership(std::move(OptionalVal));
|
|
|
|
}
|
|
|
|
|
2022-03-10 08:57:32 +00:00
|
|
|
/// Returns the symbolic value that represents the "has_value" property of the
|
|
|
|
/// optional value `Val`. Returns null if `Val` is null.
|
2022-03-14 14:52:35 +00:00
|
|
|
BoolValue *getHasValue(Value *Val) {
|
2022-03-10 08:57:32 +00:00
|
|
|
if (auto *OptionalVal = cast_or_null<StructValue>(Val)) {
|
|
|
|
return cast<BoolValue>(OptionalVal->getProperty("has_value"));
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
/// If `Type` is a reference type, returns the type of its pointee. Otherwise,
|
|
|
|
/// returns `Type` itself.
|
|
|
|
QualType stripReference(QualType Type) {
|
|
|
|
return Type->isReferenceType() ? Type->getPointeeType() : Type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns true if and only if `Type` is an optional type.
|
|
|
|
bool IsOptionalType(QualType Type) {
|
|
|
|
if (!Type->isRecordType())
|
|
|
|
return false;
|
|
|
|
// FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
|
|
|
|
auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
|
|
|
|
return TypeName == "std::optional" || TypeName == "absl::optional" ||
|
|
|
|
TypeName == "base::Optional";
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the number of optional wrappers in `Type`.
|
|
|
|
///
|
|
|
|
/// For example, if `Type` is `optional<optional<int>>`, the result of this
|
|
|
|
/// function will be 2.
|
|
|
|
int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
|
|
|
|
if (!IsOptionalType(Type))
|
|
|
|
return 0;
|
|
|
|
return 1 + countOptionalWrappers(
|
|
|
|
ASTCtx,
|
|
|
|
cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
|
|
|
|
->getTemplateArgs()
|
|
|
|
.get(0)
|
|
|
|
.getAsType()
|
|
|
|
.getDesugaredType(ASTCtx));
|
|
|
|
}
|
|
|
|
|
|
|
|
void initializeOptionalReference(const Expr *OptionalExpr,
|
|
|
|
const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
2022-03-10 08:57:32 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
|
|
|
|
LatticeTransferState &State) {
|
2022-03-10 08:57:32 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
void transferMakeOptionalCall(const CallExpr *E,
|
|
|
|
const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
2022-03-10 15:25:42 +00:00
|
|
|
auto &Loc = State.Env.createStorageLocation(*E);
|
|
|
|
State.Env.setStorageLocation(*E, Loc);
|
|
|
|
State.Env.setValue(
|
|
|
|
Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
|
|
|
|
const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
2022-03-10 08:57:32 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
void assignOptionalValue(const Expr &E, LatticeTransferState &State,
|
|
|
|
BoolValue &HasValueVal) {
|
|
|
|
if (auto *OptionalLoc =
|
|
|
|
State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
|
|
|
|
State.Env.setValue(*OptionalLoc,
|
|
|
|
createOptionalValue(State.Env, HasValueVal));
|
2022-03-10 15:25:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
/// Returns a symbolic value for the "has_value" property of an `optional<T>`
|
|
|
|
/// value that is constructed/assigned from a value of type `U` or `optional<U>`
|
|
|
|
/// where `T` is constructible from `U`.
|
|
|
|
BoolValue &
|
|
|
|
getValueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
|
|
|
|
const MatchFinder::MatchResult &MatchRes,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(F.getTemplateSpecializationArgs()->size() > 0);
|
|
|
|
|
|
|
|
const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
|
|
|
|
*MatchRes.Context,
|
|
|
|
stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
|
|
|
|
const int ArgTypeOptionalWrappersCount =
|
|
|
|
countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
|
|
|
|
|
|
|
|
// Check if this is a constructor/assignment call for `optional<T>` with
|
|
|
|
// argument of type `U` such that `T` is constructible from `U`.
|
|
|
|
if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
|
|
|
|
return State.Env.getBoolLiteralValue(true);
|
|
|
|
|
|
|
|
// This is a constructor/assignment call for `optional<T>` with argument of
|
|
|
|
// type `optional<U>` such that `T` is constructible from `U`.
|
|
|
|
if (BoolValue *Val = getHasValue(State.Env.getValue(E, SkipPast::Reference)))
|
|
|
|
return *Val;
|
|
|
|
return State.Env.makeAtomicBoolValue();
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
void transferValueOrConversionConstructor(
|
|
|
|
const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(E->getNumArgs() > 0);
|
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
assignOptionalValue(*E, State,
|
|
|
|
getValueOrConversionHasValue(*E->getConstructor(),
|
|
|
|
*E->getArg(0), MatchRes,
|
|
|
|
State));
|
|
|
|
}
|
|
|
|
|
|
|
|
void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(E->getNumArgs() > 0);
|
|
|
|
|
|
|
|
auto *OptionalLoc =
|
|
|
|
State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
|
|
|
|
assert(OptionalLoc != nullptr);
|
|
|
|
|
|
|
|
State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
|
|
|
|
|
|
|
|
// Assign a storage location for the whole expression.
|
|
|
|
State.Env.setStorageLocation(*E, *OptionalLoc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void transferValueOrConversionAssignment(
|
|
|
|
const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(E->getNumArgs() > 1);
|
|
|
|
transferAssignment(E,
|
|
|
|
getValueOrConversionHasValue(
|
|
|
|
*E->getDirectCallee(), *E->getArg(1), MatchRes, State),
|
|
|
|
State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void transferNulloptAssignment(const CXXOperatorCallExpr *E,
|
|
|
|
const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
|
2022-03-10 15:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 12:06:16 +00:00
|
|
|
void transferSwap(const StorageLocation &OptionalLoc1,
|
|
|
|
const StorageLocation &OptionalLoc2,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
|
|
|
|
assert(OptionalVal1 != nullptr);
|
|
|
|
|
|
|
|
auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
|
|
|
|
assert(OptionalVal2 != nullptr);
|
|
|
|
|
|
|
|
State.Env.setValue(OptionalLoc1, *OptionalVal2);
|
|
|
|
State.Env.setValue(OptionalLoc2, *OptionalVal1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void transferSwapCall(const CXXMemberCallExpr *E,
|
|
|
|
const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(E->getNumArgs() == 1);
|
|
|
|
|
|
|
|
auto *OptionalLoc1 = State.Env.getStorageLocation(
|
|
|
|
*E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
|
|
|
|
assert(OptionalLoc1 != nullptr);
|
|
|
|
|
|
|
|
auto *OptionalLoc2 =
|
|
|
|
State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
|
|
|
|
assert(OptionalLoc2 != nullptr);
|
|
|
|
|
|
|
|
transferSwap(*OptionalLoc1, *OptionalLoc2, State);
|
|
|
|
}
|
|
|
|
|
|
|
|
void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assert(E->getNumArgs() == 2);
|
|
|
|
|
|
|
|
auto *OptionalLoc1 =
|
|
|
|
State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
|
|
|
|
assert(OptionalLoc1 != nullptr);
|
|
|
|
|
|
|
|
auto *OptionalLoc2 =
|
|
|
|
State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
|
|
|
|
assert(OptionalLoc2 != nullptr);
|
|
|
|
|
|
|
|
transferSwap(*OptionalLoc1, *OptionalLoc2, State);
|
|
|
|
}
|
|
|
|
|
2022-03-14 14:52:35 +00:00
|
|
|
auto buildTransferMatchSwitch() {
|
2022-03-16 22:20:05 +00:00
|
|
|
// FIXME: Evaluate the efficiency of matchers. If using matchers results in a
|
|
|
|
// lot of duplicated work (e.g. string comparisons), consider providing APIs
|
|
|
|
// that avoid it through memoization.
|
2022-03-10 08:57:32 +00:00
|
|
|
return MatchSwitchBuilder<LatticeTransferState>()
|
|
|
|
// Attach a symbolic "has_value" state to optional values that we see for
|
|
|
|
// the first time.
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<Expr>(expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
|
|
|
|
initializeOptionalReference)
|
2022-03-10 08:57:32 +00:00
|
|
|
|
2022-03-10 15:25:42 +00:00
|
|
|
// make_optional
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
|
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
// optional::optional
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXConstructExpr>(
|
|
|
|
isOptionalInPlaceConstructor(),
|
|
|
|
[](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
|
|
|
|
})
|
|
|
|
.CaseOf<CXXConstructExpr>(
|
|
|
|
isOptionalNulloptConstructor(),
|
|
|
|
[](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assignOptionalValue(*E, State,
|
|
|
|
State.Env.getBoolLiteralValue(false));
|
|
|
|
})
|
|
|
|
.CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
|
|
|
|
transferValueOrConversionConstructor)
|
2022-03-10 15:25:42 +00:00
|
|
|
|
2022-03-16 22:20:05 +00:00
|
|
|
// optional::operator=
|
|
|
|
.CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
|
|
|
|
transferValueOrConversionAssignment)
|
|
|
|
.CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
|
|
|
|
transferNulloptAssignment)
|
|
|
|
|
2022-03-10 08:57:32 +00:00
|
|
|
// optional::value
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXMemberCallExpr>(
|
2022-03-10 08:57:32 +00:00
|
|
|
isOptionalMemberCallWithName("value"),
|
2022-03-14 14:52:35 +00:00
|
|
|
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
2022-03-10 08:57:32 +00:00
|
|
|
transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
|
|
|
|
})
|
|
|
|
|
|
|
|
// optional::operator*, optional::operator->
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CallExpr>(expr(anyOf(isOptionalOperatorCallWithName("*"),
|
|
|
|
isOptionalOperatorCallWithName("->"))),
|
|
|
|
[](const CallExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
transferUnwrapCall(E, E->getArg(0), State);
|
|
|
|
})
|
2022-03-10 08:57:32 +00:00
|
|
|
|
|
|
|
// optional::has_value
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
|
|
|
|
transferOptionalHasValueCall)
|
2022-03-10 08:57:32 +00:00
|
|
|
|
2022-03-10 15:25:42 +00:00
|
|
|
// optional::operator bool
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
|
|
|
|
transferOptionalHasValueCall)
|
2022-03-10 15:25:42 +00:00
|
|
|
|
|
|
|
// optional::emplace
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXMemberCallExpr>(
|
|
|
|
isOptionalMemberCallWithName("emplace"),
|
|
|
|
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assignOptionalValue(*E->getImplicitObjectArgument(), State,
|
|
|
|
State.Env.getBoolLiteralValue(true));
|
|
|
|
})
|
2022-03-10 15:25:42 +00:00
|
|
|
|
|
|
|
// optional::reset
|
2022-03-14 14:52:35 +00:00
|
|
|
.CaseOf<CXXMemberCallExpr>(
|
|
|
|
isOptionalMemberCallWithName("reset"),
|
|
|
|
[](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
|
|
|
|
LatticeTransferState &State) {
|
|
|
|
assignOptionalValue(*E->getImplicitObjectArgument(), State,
|
|
|
|
State.Env.getBoolLiteralValue(false));
|
|
|
|
})
|
2022-03-10 15:25:42 +00:00
|
|
|
|
2022-03-21 12:06:16 +00:00
|
|
|
// optional::swap
|
|
|
|
.CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
|
|
|
|
transferSwapCall)
|
|
|
|
|
|
|
|
// std::swap
|
|
|
|
.CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
|
|
|
|
|
2022-03-10 08:57:32 +00:00
|
|
|
.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
|