Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,76 @@ static auto isNonConstMemberOperatorCall() {
return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
}

static auto isMakePredicateFormatterFromIsOkMatcherCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(
callee(functionDecl(
hasName("::testing::internal::MakePredicateFormatterFromMatcher"))),
hasArgument(
0, hasType(cxxRecordDecl(hasAnyName(
"::testing::status::internal_status::IsOkMatcher",
"::absl_testing::status_internal::IsOkMatcher",
"::testing::status::internal_status::IsOkAndHoldsMatcher",
"::absl_testing::status_internal::IsOkAndHoldsMatcher")))));
}

static auto isStatusIsOkMatcherCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(callee(functionDecl(hasAnyName(
"::testing::status::StatusIs", "absl_testing::StatusIs",
"::testing::status::CanonicalStatusIs",
"::absl_testing::CanonicalStatusIs"))),
hasArgument(0, declRefExpr(to(enumConstantDecl(hasAnyName(
"::absl::StatusCode::kOk", "OK"))))));
}

static auto isMakePredicateFormatterFromStatusIsMatcherCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return callExpr(
callee(functionDecl(
hasName("::testing::internal::MakePredicateFormatterFromMatcher"))),
hasArgument(0, hasType(cxxRecordDecl(hasAnyName(
"::testing::status::internal_status::StatusIsMatcher",
"::testing::status::internal_status::"
"CanonicalStatusIsMatcher",
"::absl_testing::status_internal::StatusIsMatcher",
"::absl_testing::status_internal::"
"CanonicalStatusIsMatcher")))));
}

static auto isPredicateFormatterFromStatusMatcherCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxOperatorCallExpr(
hasOverloadedOperatorName("()"),
callee(cxxMethodDecl(ofClass(
hasName("testing::internal::PredicateFormatterFromMatcher")))),
hasArgument(2, hasType(cxxRecordDecl(hasName("absl::Status")))));
}

static auto isPredicateFormatterFromStatusOrMatcherCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxOperatorCallExpr(
hasOverloadedOperatorName("()"),
callee(cxxMethodDecl(ofClass(
hasName("testing::internal::PredicateFormatterFromMatcher")))),
hasArgument(2, hasType(statusOrType())));
}

static auto isAssertionResultOperatorBoolCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxMemberCallExpr(
on(expr(unless(cxxThisExpr()))),
callee(cxxMethodDecl(hasName("operator bool"),
ofClass(hasName("testing::AssertionResult")))));
}

static auto isAssertionResultConstructFromBoolCall() {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return cxxConstructExpr(
hasType(recordDecl(hasName("testing::AssertionResult"))),
hasArgument(0, hasType(booleanType())));
}

static auto
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
return CFGMatchSwitchBuilder<const Environment,
Expand Down Expand Up @@ -365,13 +435,39 @@ bool isStatusType(QualType Type) {
return isTypeNamed(Type, {"absl"}, "Status");
}

static bool isPredicateFormatterFromMatcherType(QualType Type) {
return isTypeNamed(Type, {"testing", "internal"},
"PredicateFormatterFromMatcher");
}

static bool isAssertionResultType(QualType Type) {
return isTypeNamed(Type, {"testing"}, "AssertionResult");
}

static bool isStatusIsMatcherType(QualType Type) {
return isTypeNamed(Type, {"testing", "status", "internal_status"},
"StatusIsMatcher") ||
isTypeNamed(Type, {"testing", "status", "internal_status"},
"CanonicalStatusIsMatcher") ||
isTypeNamed(Type, {"absl_testing", "status_internal"},
"StatusIsMatcher") ||
isTypeNamed(Type, {"absl_testing", "status_internal"},
"CanonicalStatusIsMatcher");
}

llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
const CXXRecordDecl &RD) {
if (auto *TRD = getStatusOrBaseClass(Ty))
return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
if (isStatusType(Ty) || (RD.hasDefinition() &&
RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))
return {{"ok", RD.getASTContext().BoolTy}};
if (isAssertionResultType(Ty))
return {{"ok", RD.getASTContext().BoolTy}};
if (isPredicateFormatterFromMatcherType(Ty))
return {{"ok_predicate", RD.getASTContext().BoolTy}};
if (isStatusIsMatcherType(Ty))
return {{"ok_matcher", RD.getASTContext().BoolTy}};
return {};
}

Expand All @@ -388,6 +484,13 @@ BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {
return *Val;
return initializeStatus(StatusLoc, Env);
}
static StorageLocation &locForOkPredicate(RecordStorageLocation &StatusLoc) {
return StatusLoc.getSyntheticField("ok_predicate");
}

static StorageLocation &locForOkMatcher(RecordStorageLocation &StatusLoc) {
return StatusLoc.getSyntheticField("ok_matcher");
}

static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,
const MatchFinder::MatchResult &,
Expand Down Expand Up @@ -843,6 +946,97 @@ transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
handleNonConstMemberCall(Expr, RecordLoc, Result, State);
}

static void transferMakePredicateFormatterFromIsOkMatcherCall(
const CallExpr *Expr, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
State.Env.setValue(
locForOkPredicate(State.Env.getResultObjectLocation(*Expr)),
State.Env.getBoolLiteralValue(true));
}

static void transferStatusIsOkMatcherCall(const CallExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
BoolValue &OkMatcherVal = State.Env.getBoolLiteralValue(true);
State.Env.setValue(locForOkMatcher(State.Env.getResultObjectLocation(*Expr)),
OkMatcherVal);
}

static void transferMakePredicateFormatterFromStatusIsMatcherCall(
const CallExpr *Expr, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(Expr->isPRValue());
auto &Loc = State.Env.getResultObjectLocation(*Expr->getArg(0));
auto &OkMatcherLoc = locForOkMatcher(Loc);
BoolValue *OkMatcherVal = State.Env.get<BoolValue>(OkMatcherLoc);
if (OkMatcherVal == nullptr)
return;
State.Env.setValue(
locForOkPredicate(State.Env.getResultObjectLocation(*Expr)),
*OkMatcherVal);
}

static void
transferPredicateFormatterMatcherCall(const CXXOperatorCallExpr *Expr,
LatticeTransferState &State,
bool IsStatusOr) {
auto *Loc = State.Env.get<RecordStorageLocation>(*Expr->getArg(0));
if (Loc == nullptr)
return;

auto *ObjectLoc = State.Env.get<RecordStorageLocation>(*Expr->getArg(2));
if (ObjectLoc == nullptr)
return;

auto &OkPredicateLoc = locForOkPredicate(*Loc);
BoolValue *OkPredicateVal = State.Env.get<BoolValue>(OkPredicateLoc);
if (OkPredicateVal == nullptr)
return;

if (IsStatusOr)
ObjectLoc = &locForStatus(*ObjectLoc);
auto &StatusOk = valForOk(*ObjectLoc, State.Env);

auto &A = State.Env.arena();
auto &Res = State.Env.makeAtomicBoolValue();
State.Env.assume(
A.makeImplies(OkPredicateVal->formula(),
A.makeEquals(StatusOk.formula(), Res.formula())));
State.Env.setValue(locForOk(State.Env.getResultObjectLocation(*Expr)), Res);
}

static void
transferAssertionResultConstructFromBoolCall(const CXXConstructExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
assert(Expr->getNumArgs() > 0);

auto *StatusAdaptorLoc = State.Env.get<StorageLocation>(*Expr->getArg(0));
if (StatusAdaptorLoc == nullptr)
return;
BoolValue *OkVal = State.Env.get<BoolValue>(*StatusAdaptorLoc);
if (OkVal == nullptr)
return;
State.Env.setValue(locForOk(State.Env.getResultObjectLocation(*Expr)),
*OkVal);
}

static void
transferAssertionResultOperatorBoolCall(const CXXMemberCallExpr *Expr,
const MatchFinder::MatchResult &,
LatticeTransferState &State) {
auto *RecordLoc = getImplicitObjectLocation(*Expr, State.Env);
if (RecordLoc == nullptr)
return;
BoolValue *OkVal = State.Env.get<BoolValue>(locForOk(*RecordLoc));
if (OkVal == nullptr)
return;
auto &A = State.Env.arena();
auto &Res = State.Env.makeAtomicBoolValue();
State.Env.assume(A.makeEquals(OkVal->formula(), Res.formula()));
State.Env.setValue(*Expr, Res);
}

static RecordStorageLocation *
getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
if (!E.isPRValue())
Expand All @@ -858,6 +1052,33 @@ buildTransferMatchSwitch(ASTContext &Ctx,
CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
using namespace ::clang::ast_matchers; // NOLINT: Too many names
return std::move(Builder)
.CaseOfCFGStmt<CallExpr>(
isMakePredicateFormatterFromIsOkMatcherCall(),
transferMakePredicateFormatterFromIsOkMatcherCall)
.CaseOfCFGStmt<CallExpr>(isStatusIsOkMatcherCall(),
transferStatusIsOkMatcherCall)
.CaseOfCFGStmt<CallExpr>(
isMakePredicateFormatterFromStatusIsMatcherCall(),
transferMakePredicateFormatterFromStatusIsMatcherCall)
.CaseOfCFGStmt<CXXOperatorCallExpr>(
isPredicateFormatterFromStatusOrMatcherCall(),
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
transferPredicateFormatterMatcherCall(Expr, State,
/*IsStatusOr=*/true);
})
.CaseOfCFGStmt<CXXOperatorCallExpr>(
isPredicateFormatterFromStatusMatcherCall(),
[](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
LatticeTransferState &State) {
transferPredicateFormatterMatcherCall(Expr, State,
/*IsStatusOr=*/false);
})
.CaseOfCFGStmt<CXXConstructExpr>(
isAssertionResultConstructFromBoolCall(),
transferAssertionResultConstructFromBoolCall)
.CaseOfCFGStmt<CXXMemberCallExpr>(isAssertionResultOperatorBoolCall(),
transferAssertionResultOperatorBoolCall)
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("ok"),
transferStatusOrOkCall)
.CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"),
Expand Down
Loading
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.