mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-27 05:50:12 +00:00
[clang][dataflow] Support (in)equality operators in optional
model.
This patch adds interpretation of the overloaded equality and inequality operators available for the optional types. Fixes issue #57253. Differential Revision: https://reviews.llvm.org/D139360
This commit is contained in:
parent
72d76a2403
commit
390029be89
@ -80,11 +80,20 @@ auto isMakeOptionalCall() {
|
||||
hasOptionalType());
|
||||
}
|
||||
|
||||
auto hasNulloptType() {
|
||||
return hasType(namedDecl(
|
||||
hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
|
||||
auto nulloptTypeDecl() {
|
||||
return namedDecl(
|
||||
hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"));
|
||||
}
|
||||
|
||||
auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
|
||||
|
||||
// `optional` or `nullopt_t`
|
||||
auto hasAnyOptionalType() {
|
||||
return hasType(hasUnqualifiedDesugaredType(
|
||||
recordType(hasDeclaration(anyOf(nulloptTypeDecl(), optionalClass())))));
|
||||
}
|
||||
|
||||
|
||||
auto inPlaceClass() {
|
||||
return recordDecl(
|
||||
hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
|
||||
@ -117,6 +126,11 @@ auto isOptionalValueOrConversionAssignment() {
|
||||
argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
|
||||
}
|
||||
|
||||
auto isNulloptConstructor() {
|
||||
return cxxConstructExpr(hasNulloptType(), argumentCountIs(1),
|
||||
hasArgument(0, hasNulloptType()));
|
||||
}
|
||||
|
||||
auto isOptionalNulloptAssignment() {
|
||||
return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
|
||||
callee(cxxMethodDecl(ofClass(optionalClass()))),
|
||||
@ -172,6 +186,27 @@ auto isCallReturningOptional() {
|
||||
optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
|
||||
}
|
||||
|
||||
template <typename L, typename R>
|
||||
auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
|
||||
return cxxOperatorCallExpr(
|
||||
anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
|
||||
argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
|
||||
hasArgument(1, rhs_arg_matcher));
|
||||
}
|
||||
|
||||
// Ensures that `Expr` is mapped to a `BoolValue` and returns it.
|
||||
BoolValue &forceBoolValue(Environment &Env, const Expr &Expr) {
|
||||
auto *Value = cast_or_null<BoolValue>(Env.getValue(Expr, SkipPast::None));
|
||||
if (Value != nullptr)
|
||||
return *Value;
|
||||
|
||||
auto &Loc = Env.createStorageLocation(Expr);
|
||||
Value = &Env.makeAtomicBoolValue();
|
||||
Env.setValue(Loc, *Value);
|
||||
Env.setStorageLocation(Expr, Loc);
|
||||
return *Value;
|
||||
}
|
||||
|
||||
/// Sets `HasValueVal` as the symbolic value that represents the "has_value"
|
||||
/// property of the optional value `OptionalVal`.
|
||||
void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
|
||||
@ -357,16 +392,8 @@ void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
|
||||
if (HasValueVal == nullptr)
|
||||
return;
|
||||
|
||||
auto *ExprValue = cast_or_null<BoolValue>(
|
||||
State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
|
||||
if (ExprValue == nullptr) {
|
||||
auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
|
||||
ExprValue = &State.Env.makeAtomicBoolValue();
|
||||
State.Env.setValue(ExprLoc, *ExprValue);
|
||||
State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
|
||||
}
|
||||
|
||||
Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
|
||||
Env.addToFlowCondition(
|
||||
ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr), *HasValueVal));
|
||||
}
|
||||
|
||||
void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
|
||||
@ -423,9 +450,9 @@ void assignOptionalValue(const Expr &E, LatticeTransferState &State,
|
||||
/// 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 &value_orConversionHasValue(const FunctionDecl &F, const Expr &E,
|
||||
const MatchFinder::MatchResult &MatchRes,
|
||||
LatticeTransferState &State) {
|
||||
BoolValue &valueOrConversionHasValue(const FunctionDecl &F, const Expr &E,
|
||||
const MatchFinder::MatchResult &MatchRes,
|
||||
LatticeTransferState &State) {
|
||||
assert(F.getTemplateSpecializationArgs()->size() > 0);
|
||||
|
||||
const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
|
||||
@ -453,9 +480,9 @@ void transferValueOrConversionConstructor(
|
||||
assert(E->getNumArgs() > 0);
|
||||
|
||||
assignOptionalValue(*E, State,
|
||||
value_orConversionHasValue(*E->getConstructor(),
|
||||
*E->getArg(0), MatchRes,
|
||||
State));
|
||||
valueOrConversionHasValue(*E->getConstructor(),
|
||||
*E->getArg(0), MatchRes,
|
||||
State));
|
||||
}
|
||||
|
||||
void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
|
||||
@ -478,8 +505,8 @@ void transferValueOrConversionAssignment(
|
||||
LatticeTransferState &State) {
|
||||
assert(E->getNumArgs() > 1);
|
||||
transferAssignment(E,
|
||||
value_orConversionHasValue(*E->getDirectCallee(),
|
||||
*E->getArg(1), MatchRes, State),
|
||||
valueOrConversionHasValue(*E->getDirectCallee(),
|
||||
*E->getArg(1), MatchRes, State),
|
||||
State);
|
||||
}
|
||||
|
||||
@ -533,6 +560,56 @@ void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
|
||||
transferSwap(*OptionalLoc1, *OptionalLoc2, State);
|
||||
}
|
||||
|
||||
BoolValue &evaluateEquality(Environment &Env, BoolValue &EqVal, BoolValue &LHS,
|
||||
BoolValue &RHS) {
|
||||
// Logically, an optional<T> object is composed of two values - a `has_value`
|
||||
// bit and a value of type T. Equality of optional objects compares both
|
||||
// values. Therefore, merely comparing the `has_value` bits isn't sufficient:
|
||||
// when two optional objects are engaged, the equality of their respective
|
||||
// values of type T matters. Since we only track the `has_value` bits, we
|
||||
// can't make any conclusions about equality when we know that two optional
|
||||
// objects are engaged.
|
||||
//
|
||||
// We express this as two facts about the equality:
|
||||
// a) EqVal => (LHS & RHS) v (!RHS & !LHS)
|
||||
// If they are equal, then either both are set or both are unset.
|
||||
// b) (!LHS & !RHS) => EqVal
|
||||
// If neither is set, then they are equal.
|
||||
// We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
|
||||
return Env.makeAnd(
|
||||
Env.makeImplication(
|
||||
EqVal, Env.makeOr(Env.makeAnd(LHS, RHS),
|
||||
Env.makeAnd(Env.makeNot(LHS), Env.makeNot(RHS)))),
|
||||
Env.makeImplication(Env.makeNot(EqVal), Env.makeOr(LHS, RHS)));
|
||||
}
|
||||
|
||||
void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
|
||||
const MatchFinder::MatchResult &,
|
||||
LatticeTransferState &State) {
|
||||
Environment &Env = State.Env;
|
||||
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
|
||||
if (auto *LHasVal = getHasValue(
|
||||
Env, Env.getValue(*CmpExpr->getArg(0), SkipPast::Reference)))
|
||||
if (auto *RHasVal = getHasValue(
|
||||
Env, Env.getValue(*CmpExpr->getArg(1), SkipPast::Reference))) {
|
||||
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
|
||||
CmpValue = &State.Env.makeNot(*CmpValue);
|
||||
Env.addToFlowCondition(
|
||||
evaluateEquality(Env, *CmpValue, *LHasVal, *RHasVal));
|
||||
}
|
||||
}
|
||||
|
||||
void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
|
||||
const clang::Expr *E, Environment &Env) {
|
||||
auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
|
||||
if (auto *HasVal = getHasValue(Env, Env.getValue(*E, SkipPast::Reference))) {
|
||||
if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
|
||||
CmpValue = &Env.makeNot(*CmpValue);
|
||||
Env.addToFlowCondition(evaluateEquality(Env, *CmpValue, *HasVal,
|
||||
Env.getBoolLiteralValue(true)));
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Optional<StatementMatcher>
|
||||
ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
|
||||
if (Options.IgnoreSmartPointerDereference)
|
||||
@ -578,12 +655,24 @@ auto buildTransferMatchSwitch(
|
||||
assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
|
||||
})
|
||||
.CaseOfCFGStmt<CXXConstructExpr>(
|
||||
isOptionalNulloptConstructor(),
|
||||
isNulloptConstructor(),
|
||||
[](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
|
||||
LatticeTransferState &State) {
|
||||
assignOptionalValue(*E, State,
|
||||
State.Env.getBoolLiteralValue(false));
|
||||
})
|
||||
.CaseOfCFGStmt<CXXConstructExpr>(
|
||||
isOptionalNulloptConstructor(),
|
||||
[](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
|
||||
LatticeTransferState &State) {
|
||||
// Shares a temporary with the underlying `nullopt_t` instance.
|
||||
if (auto *OptionalLoc =
|
||||
State.Env.getStorageLocation(*E, SkipPast::None)) {
|
||||
State.Env.setValue(
|
||||
*OptionalLoc,
|
||||
*State.Env.getValue(*E->getArg(0), SkipPast::None));
|
||||
}
|
||||
})
|
||||
.CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
|
||||
transferValueOrConversionConstructor)
|
||||
|
||||
@ -652,6 +741,25 @@ auto buildTransferMatchSwitch(
|
||||
// opt.value_or(X) != X
|
||||
.CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
|
||||
|
||||
// Comparisons (==, !=):
|
||||
.CaseOfCFGStmt<CXXOperatorCallExpr>(
|
||||
isComparisonOperatorCall(hasAnyOptionalType(), hasAnyOptionalType()),
|
||||
transferOptionalAndOptionalCmp)
|
||||
.CaseOfCFGStmt<CXXOperatorCallExpr>(
|
||||
isComparisonOperatorCall(hasOptionalType(),
|
||||
unless(hasAnyOptionalType())),
|
||||
[](const clang::CXXOperatorCallExpr *Cmp,
|
||||
const MatchFinder::MatchResult &, LatticeTransferState &State) {
|
||||
transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
|
||||
})
|
||||
.CaseOfCFGStmt<CXXOperatorCallExpr>(
|
||||
isComparisonOperatorCall(unless(hasAnyOptionalType()),
|
||||
hasOptionalType()),
|
||||
[](const clang::CXXOperatorCallExpr *Cmp,
|
||||
const MatchFinder::MatchResult &, LatticeTransferState &State) {
|
||||
transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
|
||||
})
|
||||
|
||||
// returns optional
|
||||
.CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
|
||||
transferCallReturningOptional)
|
||||
|
@ -762,6 +762,29 @@ template <typename T, typename U, typename... Args>
|
||||
constexpr optional<T> make_optional(std::initializer_list<U> il,
|
||||
Args&&... args);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool operator==(const optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator==(nullopt_t, const optional<T> &opt);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(const optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(nullopt_t, const optional<T> &opt);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const T &value, const optional<U> &opt);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const T &value, const optional<U> &opt);
|
||||
|
||||
} // namespace std
|
||||
)";
|
||||
|
||||
@ -984,6 +1007,29 @@ template <typename T, typename U, typename... Args>
|
||||
constexpr optional<T> make_optional(std::initializer_list<U> il,
|
||||
Args&&... args);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool operator==(const optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator==(nullopt_t, const optional<T> &opt);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(const optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(nullopt_t, const optional<T> &opt);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const T &value, const optional<U> &opt);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const T &value, const optional<U> &opt);
|
||||
|
||||
} // namespace absl
|
||||
)";
|
||||
|
||||
@ -1177,6 +1223,29 @@ template <typename T, typename U, typename... Args>
|
||||
constexpr Optional<T> make_optional(std::initializer_list<U> il,
|
||||
Args&&... args);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const Optional<T> &lhs, const Optional<U> &rhs);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const Optional<T> &lhs, const Optional<U> &rhs);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool operator==(const Optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator==(nullopt_t, const Optional<T> &opt);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(const Optional<T> &opt, nullopt_t);
|
||||
template <typename T>
|
||||
constexpr bool operator!=(nullopt_t, const Optional<T> &opt);
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const Optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator==(const T &value, const Optional<U> &opt);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const Optional<T> &opt, const U &value);
|
||||
template <typename T, typename U>
|
||||
constexpr bool operator!=(const T &value, const Optional<U> &opt);
|
||||
|
||||
} // namespace base
|
||||
)";
|
||||
|
||||
@ -2117,6 +2186,325 @@ TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) {
|
||||
)");
|
||||
}
|
||||
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftSet) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = 3;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 == opt2) {
|
||||
opt2.value();
|
||||
} else {
|
||||
opt2.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightSet) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = 3;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt2 == opt1) {
|
||||
opt2.value();
|
||||
} else {
|
||||
opt2.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckVerifySetAfterEq) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 == opt2) {
|
||||
if (opt1.has_value())
|
||||
opt2.value();
|
||||
if (opt2.has_value())
|
||||
opt1.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftUnset) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = $ns::nullopt;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 == opt2) {
|
||||
opt2.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt2.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightUnset) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = $ns::nullopt;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt2 == opt1) {
|
||||
opt2.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt2.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightNullopt) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt == $ns::nullopt) {
|
||||
opt.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftNullopt) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if ($ns::nullopt == opt) {
|
||||
opt.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightValue) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt == 3) {
|
||||
opt.value();
|
||||
} else {
|
||||
opt.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftValue) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (3 == opt) {
|
||||
opt.value();
|
||||
} else {
|
||||
opt.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftSet) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = 3;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 != opt2) {
|
||||
opt2.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt2.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightSet) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = 3;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt2 != opt1) {
|
||||
opt2.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt2.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckVerifySetAfterEq) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 != opt2) {
|
||||
if (opt1.has_value())
|
||||
opt2.value(); // [[unsafe]]
|
||||
if (opt2.has_value())
|
||||
opt1.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftUnset) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = $ns::nullopt;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt1 != opt2) {
|
||||
opt2.value();
|
||||
} else {
|
||||
opt2.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightUnset) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt1 = $ns::nullopt;
|
||||
$ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt2 != opt1) {
|
||||
opt2.value();
|
||||
} else {
|
||||
opt2.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightNullopt) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt != $ns::nullopt) {
|
||||
opt.value();
|
||||
} else {
|
||||
opt.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftNullopt) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if ($ns::nullopt != opt) {
|
||||
opt.value();
|
||||
} else {
|
||||
opt.value(); // [[unsafe]]
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightValue) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (opt != 3) {
|
||||
opt.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftValue) {
|
||||
ExpectDiagnosticsFor(
|
||||
R"(
|
||||
#include "unchecked_optional_access_test.h"
|
||||
|
||||
void target() {
|
||||
$ns::$optional<int> opt = Make<$ns::$optional<int>>();
|
||||
|
||||
if (3 != opt) {
|
||||
opt.value(); // [[unsafe]]
|
||||
} else {
|
||||
opt.value();
|
||||
}
|
||||
}
|
||||
)");
|
||||
}
|
||||
|
||||
// Verifies that the model sees through aliases.
|
||||
TEST_P(UncheckedOptionalAccessTest, WithAlias) {
|
||||
ExpectDiagnosticsFor(
|
||||
|
Loading…
x
Reference in New Issue
Block a user