diff --git a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h b/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h deleted file mode 100644 index cb619fb0cb5bb..0000000000000 --- a/clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h +++ /dev/null @@ -1,112 +0,0 @@ -//===- UncheckedStatusOrAccessModel.h -------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H -#define CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H - -#include "clang/AST/Type.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" -#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" -#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "clang/Analysis/FlowSensitive/NoopLattice.h" -#include "clang/Analysis/FlowSensitive/StorageLocation.h" -#include "clang/Analysis/FlowSensitive/Value.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/StringRef.h" - -namespace clang::dataflow::statusor_model { - -// The helper functions exported here are for use of downstream vendor -// extensions of this model. - -// Match declaration of `absl::StatusOr` and bind `T` to "T". -clang::ast_matchers::DeclarationMatcher statusOrClass(); -// Match declaration of `absl::Status`. -clang::ast_matchers::DeclarationMatcher statusClass(); -// Match declaration of `absl::internal_statusor::OperatorBase`. -clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass(); -clang::ast_matchers::TypeMatcher statusOrType(); - -// Get RecordStorageLocation for the `Status` contained in the `StatusOr` -RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc); -// Get the StorageLocation for the OK boolean in the `Status` -StorageLocation &locForOk(RecordStorageLocation &StatusLoc); -// Get the OK boolean in the `Status`, and initialize it if necessary. -BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env); -// Get synthetic fields for the types modelled by -// `UncheckedStatusOrAccessModel`. -llvm::StringMap getSyntheticFields(QualType Ty, QualType StatusType, - const CXXRecordDecl &RD); - -// Initialize the synthetic fields of the `StatusOr`. -// N.B. if it is already initialized, the value gets reset. -BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc, - Environment &Env); -// Initialize the synthetic fields of the `Status`. -// N.B. if it is already initialized, the value gets reset. -BoolValue &initializeStatus(RecordStorageLocation &StatusLoc, Environment &Env); - -bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName); -// Return true if `Type` is instantiation of `absl::StatusOr` -bool isStatusOrType(QualType Type); -// Return true if `Type` is `absl::Status` -bool isStatusType(QualType Type); - -// Get `QualType` for `absl::Status`, or default-constructed -// QualType if it does not exist. -QualType findStatusType(const ASTContext &Ctx); - -struct UncheckedStatusOrAccessModelOptions {}; - -// Dataflow analysis that discovers unsafe uses of StatusOr values. -class UncheckedStatusOrAccessModel - : public DataflowAnalysis { -public: - explicit UncheckedStatusOrAccessModel(ASTContext &Ctx, Environment &Env); - - static Lattice initialElement() { return {}; } - - void transfer(const CFGElement &Elt, Lattice &L, Environment &Env); - -private: - CFGMatchSwitch> TransferMatchSwitch; -}; - -using LatticeTransferState = - TransferState; - -// Extend the Builder with the transfer functions for -// `UncheckedStatusOrAccessModel`. This is useful to write downstream models -// that extend the model. -CFGMatchSwitch -buildTransferMatchSwitch(ASTContext &Ctx, - CFGMatchSwitchBuilder Builder); - -class UncheckedStatusOrAccessDiagnoser { -public: - explicit UncheckedStatusOrAccessDiagnoser( - UncheckedStatusOrAccessModelOptions Options = {}); - - llvm::SmallVector operator()( - const CFGElement &Elt, ASTContext &Ctx, - const TransferStateForDiagnostics - &State); - -private: - CFGMatchSwitch> - DiagnoseMatchSwitch; -}; - -} // namespace clang::dataflow::statusor_model - -#endif // CLANG_ANALYSIS_FLOWSENSITIVE_MODELS_UNCHECKEDSTATUSORACCESSMODEL_H diff --git a/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt b/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt index d1236f5714881..89bbe8791eb2c 100644 --- a/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt +++ b/clang/lib/Analysis/FlowSensitive/Models/CMakeLists.txt @@ -1,7 +1,6 @@ add_clang_library(clangAnalysisFlowSensitiveModels ChromiumCheckModel.cpp UncheckedOptionalAccessModel.cpp - UncheckedStatusOrAccessModel.cpp LINK_LIBS clangAnalysis diff --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp deleted file mode 100644 index 75b0959491c22..0000000000000 --- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp +++ /dev/null @@ -1,295 +0,0 @@ -//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h" - -#include -#include - -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/TypeBase.h" -#include "clang/ASTMatchers/ASTMatchFinder.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/ASTMatchers/ASTMatchersInternal.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h" -#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" -#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "clang/Analysis/FlowSensitive/StorageLocation.h" -#include "clang/Analysis/FlowSensitive/Value.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/StringMap.h" - -namespace clang::dataflow::statusor_model { -namespace { - -using ::clang::ast_matchers::MatchFinder; -using ::clang::ast_matchers::StatementMatcher; - -} // namespace - -static bool namespaceEquals(const NamespaceDecl *NS, - clang::ArrayRef NamespaceNames) { - while (!NamespaceNames.empty() && NS) { - if (NS->getName() != NamespaceNames.consume_back()) - return false; - NS = dyn_cast_or_null(NS->getParent()); - } - return NamespaceNames.empty() && !NS; -} - -// TODO: move this to a proper place to share with the rest of clang -static bool isTypeNamed(QualType Type, clang::ArrayRef NS, - StringRef Name) { - if (Type.isNull()) - return false; - if (auto *RD = Type->getAsRecordDecl()) - if (RD->getName() == Name) - if (const auto *N = dyn_cast_or_null(RD->getDeclContext())) - return namespaceEquals(N, NS); - return false; -} - -static bool isStatusOrOperatorBaseType(QualType Type) { - return isTypeNamed(Type, {"absl", "internal_statusor"}, "OperatorBase"); -} - -static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc, - const Environment &Env) { - if (!StatusOrLoc) - return false; - auto &StatusLoc = locForStatus(*StatusOrLoc); - auto *OkVal = Env.get(locForOk(StatusLoc)); - return OkVal != nullptr && Env.proves(OkVal->formula()); -} - -static ClassTemplateSpecializationDecl * -getStatusOrBaseClass(const QualType &Ty) { - auto *RD = Ty->getAsCXXRecordDecl(); - if (RD == nullptr) - return nullptr; - if (isStatusOrType(Ty) || - // In case we are analyzing code under OperatorBase itself that uses - // operator* (e.g. to implement operator->). - isStatusOrOperatorBaseType(Ty)) - return cast(RD); - if (!RD->hasDefinition()) - return nullptr; - for (const auto &Base : RD->bases()) - if (auto *QT = getStatusOrBaseClass(Base.getType())) - return QT; - return nullptr; -} - -static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) { - return TRD->getTemplateArgs().get(0).getAsType(); -} - -static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return cxxMemberCallExpr( - on(expr(unless(cxxThisExpr()))), - callee(cxxMethodDecl( - hasName(member_name), - ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass()))))); -} - -static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return cxxOperatorCallExpr( - hasOverloadedOperatorName(operator_name), - callee(cxxMethodDecl( - ofClass(anyOf(statusOrClass(), statusOrOperatorBaseClass()))))); -} - -static auto valueCall() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return anyOf(isStatusOrMemberCallWithName("value"), - isStatusOrMemberCallWithName("ValueOrDie")); -} - -static auto valueOperatorCall() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return expr(anyOf(isStatusOrOperatorCallWithName("*"), - isStatusOrOperatorCallWithName("->"))); -} - -static auto -buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) { - return CFGMatchSwitchBuilder>() - // StatusOr::value, StatusOr::ValueOrDie - .CaseOfCFGStmt( - valueCall(), - [](const CXXMemberCallExpr *E, - const ast_matchers::MatchFinder::MatchResult &, - const Environment &Env) { - if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env)) - return llvm::SmallVector({E->getExprLoc()}); - return llvm::SmallVector(); - }) - - // StatusOr::operator*, StatusOr::operator-> - .CaseOfCFGStmt( - valueOperatorCall(), - [](const CXXOperatorCallExpr *E, - const ast_matchers::MatchFinder::MatchResult &, - const Environment &Env) { - RecordStorageLocation *StatusOrLoc = - Env.get(*E->getArg(0)); - if (!isSafeUnwrap(StatusOrLoc, Env)) - return llvm::SmallVector({E->getOperatorLoc()}); - return llvm::SmallVector(); - }) - .Build(); -} - -UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser( - UncheckedStatusOrAccessModelOptions Options) - : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} - -llvm::SmallVector UncheckedStatusOrAccessDiagnoser::operator()( - const CFGElement &Elt, ASTContext &Ctx, - const TransferStateForDiagnostics - &State) { - return DiagnoseMatchSwitch(Elt, Ctx, State.Env); -} - -BoolValue &initializeStatus(RecordStorageLocation &StatusLoc, - Environment &Env) { - auto &OkVal = Env.makeAtomicBoolValue(); - Env.setValue(locForOk(StatusLoc), OkVal); - return OkVal; -} - -BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc, - Environment &Env) { - return initializeStatus(locForStatus(StatusOrLoc), Env); -} - -clang::ast_matchers::DeclarationMatcher statusOrClass() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return classTemplateSpecializationDecl( - hasName("absl::StatusOr"), - hasTemplateArgument(0, refersToType(type().bind("T")))); -} - -clang::ast_matchers::DeclarationMatcher statusClass() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return cxxRecordDecl(hasName("absl::Status")); -} - -clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return classTemplateSpecializationDecl( - hasName("absl::internal_statusor::OperatorBase")); -} - -clang::ast_matchers::TypeMatcher statusOrType() { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return hasCanonicalType(qualType(hasDeclaration(statusOrClass()))); -} - -bool isRecordTypeWithName(QualType Type, llvm::StringRef TypeName) { - return Type->isRecordType() && - Type->getAsCXXRecordDecl()->getQualifiedNameAsString() == TypeName; -} - -bool isStatusOrType(QualType Type) { - return isTypeNamed(Type, {"absl"}, "StatusOr"); -} - -bool isStatusType(QualType Type) { - return isTypeNamed(Type, {"absl"}, "Status"); -} - -llvm::StringMap 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}}; - return {}; -} - -RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc) { - return cast(StatusOrLoc.getSyntheticField("status")); -} - -StorageLocation &locForOk(RecordStorageLocation &StatusLoc) { - return StatusLoc.getSyntheticField("ok"); -} - -BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) { - if (auto *Val = Env.get(locForOk(StatusLoc))) - return *Val; - return initializeStatus(StatusLoc, Env); -} - -static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr, - const MatchFinder::MatchResult &, - LatticeTransferState &State) { - RecordStorageLocation *StatusOrLoc = - getImplicitObjectLocation(*Expr, State.Env); - if (StatusOrLoc == nullptr) - return; - - auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env); - State.Env.setValue(*Expr, OkVal); -} - -CFGMatchSwitch -buildTransferMatchSwitch(ASTContext &Ctx, - CFGMatchSwitchBuilder Builder) { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - return std::move(Builder) - .CaseOfCFGStmt(isStatusOrMemberCallWithName("ok"), - transferStatusOrOkCall) - .Build(); -} - -QualType findStatusType(const ASTContext &Ctx) { - for (Type *Ty : Ctx.getTypes()) - if (isStatusType(QualType(Ty, 0))) - return QualType(Ty, 0); - - return QualType(); -} - -UncheckedStatusOrAccessModel::UncheckedStatusOrAccessModel(ASTContext &Ctx, - Environment &Env) - : DataflowAnalysis(Ctx), - TransferMatchSwitch(buildTransferMatchSwitch(Ctx, {})) { - QualType StatusType = findStatusType(Ctx); - Env.getDataflowAnalysisContext().setSyntheticFieldCallback( - [StatusType](QualType Ty) -> llvm::StringMap { - CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); - if (RD == nullptr) - return {}; - - if (auto Fields = getSyntheticFields(Ty, StatusType, *RD); - !Fields.empty()) - return Fields; - return {}; - }); -} - -void UncheckedStatusOrAccessModel::transfer(const CFGElement &Elt, Lattice &L, - Environment &Env) { - LatticeTransferState State(L, Env); - TransferMatchSwitch(Elt, getASTContext(), State); -} - -} // namespace clang::dataflow::statusor_model diff --git a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt index 1d932ec6e8a96..35082387b46e9 100644 --- a/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt +++ b/clang/unittests/Analysis/FlowSensitive/CMakeLists.txt @@ -25,8 +25,6 @@ add_clang_unittest(ClangAnalysisFlowSensitiveTests TransferTest.cpp TypeErasedDataflowAnalysisTest.cpp UncheckedOptionalAccessModelTest.cpp - UncheckedStatusOrAccessModelTest.cpp - UncheckedStatusOrAccessModelTestFixture.cpp ValueTest.cpp WatchedLiteralsSolverTest.cpp CLANG_LIBS diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp deleted file mode 100644 index 07d3f2412e842..0000000000000 --- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTest.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//===- UncheckedStatusOrAccessModelTest.cpp -------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include - -#include "UncheckedStatusOrAccessModelTestFixture.h" -#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h" -#include "gtest/gtest.h" - -namespace clang::dataflow::statusor_model { -namespace { - -INSTANTIATE_TEST_SUITE_P( - UncheckedStatusOrAccessModelTest, UncheckedStatusOrAccessModelTest, - testing::Values( - std::make_pair(new UncheckedStatusOrAccessModelTestExecutor< - UncheckedStatusOrAccessModel>(), - UncheckedStatusOrAccessModelTestAliasKind::kUnaliased), - std::make_pair( - new UncheckedStatusOrAccessModelTestExecutor< - UncheckedStatusOrAccessModel>(), - UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased), - std::make_pair( - new UncheckedStatusOrAccessModelTestExecutor< - UncheckedStatusOrAccessModel>(), - UncheckedStatusOrAccessModelTestAliasKind::kFullyAliased))); -} // namespace - -} // namespace clang::dataflow::statusor_model diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp deleted file mode 100644 index 4827cc1d0a7e9..0000000000000 --- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.cpp +++ /dev/null @@ -1,2518 +0,0 @@ -//===- UncheckedStatusOrAccessModelTestFixture.cpp ------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#include "UncheckedStatusOrAccessModelTestFixture.h" -#include "MockHeaders.h" -#include "llvm/Support/ErrorHandling.h" - -#include -#include -#include - -#include "gtest/gtest.h" - -namespace clang::dataflow::statusor_model { -namespace { - -TEST_P(UncheckedStatusOrAccessModelTest, NoStatusOrMention) { - ExpectDiagnosticsFor(R"cc( - void target() { "nop"; } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToValue) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, NonExplicitInitialization) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - STATUSOR_INT target() { - STATUSOR_INT x = Make(); - return x.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToValue_NewLine) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - sor. // force newline - value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Rvalue_CallToValue) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - std::move(sor).value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToValueOrDie) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - sor.ValueOrDie(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Rvalue_CallToValueOrDie) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - std::move(sor).ValueOrDie(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToOperatorStar) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - *sor; // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToOperatorStarSeparateLine) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - * // [[unsafe]] - sor; - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Rvalue_CallToOperatorStar) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - *std::move(sor); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Lvalue_CallToOperatorArrow) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - void foo(); - }; - - void target(absl::StatusOr sor) { - sor->foo(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, - UnwrapWithoutCheck_Rvalue_CallToOperatorArrow) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - void foo(); - }; - - void target(absl::StatusOr sor) { - std::move(sor)->foo(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, UnwrapRvalueWithCheck) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (sor.ok()) std::move(sor).value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ParensInDeclInitExpr) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - auto sor = (Make()); - if (sor.ok()) sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ReferenceInDeclInitExpr) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - const STATUSOR_INT& GetStatusOrInt() const; - }; - - void target(Foo foo) { - auto sor = foo.GetStatusOrInt(); - if (sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT& GetStatusOrInt(); - }; - - void target(Foo foo) { - auto sor = foo.GetStatusOrInt(); - if (sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT&& GetStatusOrInt() &&; - }; - - void target(Foo foo) { - auto sor = std::move(foo).GetStatusOrInt(); - if (sor.ok()) sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - if (auto sor = Make(); sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, JoinSafeSafe) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor, bool b) { - if (sor.ok()) { - if (b) - sor.value(); - else - sor.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, JoinUnsafeUnsafe) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor, bool b) { - if (b) - sor.value(); // [[unsafe]] - else - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, InversedIfThenElse) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!sor.ok()) - sor.value(); // [[unsafe]] - else - sor.value(); - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, DoubleInversedIfThenElse) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!!sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TripleInversedIfThenElse) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!!!sor.ok()) - sor.value(); // [[unsafe]] - else - sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (x.ok() && y.ok()) { - x.value(); - - y.value(); - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!x.ok() && y.ok()) { - y.value(); - - x.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsAndNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (x.ok() && !y.ok()) { - x.value(); - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!x.ok() && !y.ok()) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(x.ok() && y.ok())) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); - - y.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(!x.ok() && y.ok())) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - y.value(); - - x.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsAndNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(x.ok() && !y.ok())) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(!x.ok() && !y.ok())) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (x.ok() || y.ok()) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!x.ok() || y.ok()) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_LhsOrNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (x.ok() || !y.ok()) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - y.value(); - - x.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_NotLhsOrNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!x.ok() || !y.ok()) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); - - y.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(x.ok() || y.ok())) { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(!x.ok() || y.ok())) { - x.value(); - - y.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_LhsOrNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(x.ok() || !y.ok())) { - y.value(); - - x.value(); // [[unsafe]] - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, IfThenElse_Not_NotLhsOrNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!(!x.ok() || !y.ok())) { - x.value(); - - y.value(); - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranch) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!sor.ok()) return; - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (sor.ok()) return; - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!x.ok() || !y.ok()) return; - - x.value(); - - y.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfElseBranch) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (sor.ok()) { - } else { - return; - } - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!sor.ok()) { - } else { - return; - } - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TerminatingIfThenBranchInLoop) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (Make()) { - if (!sor.ok()) continue; - - sor.value(); - } - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (Make()) { - if (!sor.ok()) break; - - sor.value(); - } - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TernaryConditionalOperator) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - sor.ok() ? sor.value() : 21; - - sor.ok() ? 21 : sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - !sor.ok() ? 21 : sor.value(); - - !sor.ok() ? sor.value() : 21; // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor1, STATUSOR_INT sor2) { - !((__builtin_expect(false || (!(sor1.ok() && sor2.ok())), false))) - ? (void)0 - : (void)1; - do { - sor1.value(); // [[unsafe]] - sor2.value(); // [[unsafe]] - } while (true); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (Make()) sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (!sor.ok()) sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (!!sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (!!!sor.ok()) sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() && y.ok()) { - x.value(); - - y.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() && y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() && y.ok()) y.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_LhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() && !y.ok()) x.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() && !y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() && !y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() && !y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() && y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() && y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() && y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() && y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() && !y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() && !y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsAndNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() && !y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() && !y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() || y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() || y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() || y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() || y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_LhsOrNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() || !y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (x.ok() || !y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_NotLhsOrNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() || !y.ok()) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!x.ok() || !y.ok()) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() || y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() || y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() || y.ok())) x.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() || y.ok())) y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_LhsOrNotRhs) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() || !y.ok())) x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(x.ok() || !y.ok())) y.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_Not_NotLhsOrNotRhs) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (!(!x.ok() || !y.ok())) { - x.value(); - - y.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_AccessAfterStmt) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (sor.ok()) { - } - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (!sor.ok()) { - } - - sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_TerminatingBranch_Return) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (!sor.ok()) return; - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (sor.ok()) return; - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, While_NestedIfWithBinaryCondition) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (Make()) { - if (x.ok() && y.ok()) { - x.value(); - - y.value(); - } - } - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - while (Make()) { - if (!(!x.ok() || !y.ok())) { - x.value(); - - y.value(); - } - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, BuiltinExpect) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT x, STATUSOR_INT y) { - if (!__builtin_expect(!x.ok() || __builtin_expect(!y.ok(), true), false)) { - x.value(); - - y.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, CopyAssignment) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT sor = Make(); - if (sor.ok()) { - sor = Make(); - sor.value(); // [[unsafe]] - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT sor = Make(); - if (!sor.ok()) return; - - sor = Make(); - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - if (x.ok()) { - STATUSOR_INT y = x; - x = Make(); - - y.value(); - - x.value(); // [[unsafe]] - } - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - STATUSOR_INT y = x; - if (!y.ok()) return; - - x.value(); - - y = Make(); - x.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT bar; - }; - - void target(Foo foo) { - foo.bar = Make(); - if (foo.bar.ok()) { - foo.bar.value(); - - foo.bar = Make(); - foo.bar.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ShortCircuitingBinaryOperators) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_BOOL sor) { - bool b = sor.ok() & sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_BOOL sor) { - bool b = sor.ok() && sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_BOOL sor) { - bool b = !sor.ok() && sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_BOOL sor) { - bool b = sor.ok() || sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_BOOL sor) { - bool b = !sor.ok() || sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (b || sor.ok()) { - do { - sor.value(); // [[unsafe]] - } while (true); - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (__builtin_expect(b || sor.ok(), false)) { - do { - sor.value(); // [[unsafe]] - } while (false); - } - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor1, STATUSOR_INT sor2) { - while (sor1.ok() && sor2.ok()) sor1.value(); - while (sor1.ok() && sor2.ok()) sor2.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, References) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - STATUSOR_INT& y = x; - if (x.ok()) { - x.value(); - - y.value(); - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - STATUSOR_INT& y = x; - if (y.ok()) { - x.value(); - - y.value(); - } else { - x.value(); // [[unsafe]] - - y.value(); // [[unsafe]] - } - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - STATUSOR_INT& y = x; - if (!y.ok()) return; - - x.value(); - - y = Make(); - x.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT x = Make(); - const STATUSOR_INT& y = x; - if (!y.ok()) return; - - y.value(); - - x = Make(); - y.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, NoReturnAttribute) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - __attribute__((noreturn)) void f(); - - void target(STATUSOR_INT sor) { - if (!sor.ok()) f(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void f(); - - void target(STATUSOR_INT sor) { - if (!sor.ok()) f(); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - __attribute__((noreturn)) ~Foo(); - void Bar(); - }; - - void target(STATUSOR_INT sor) { - if (!sor.ok()) Foo().Bar(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - ~Foo(); - void Bar(); - }; - - void target(STATUSOR_INT sor) { - if (!sor.ok()) Foo().Bar(); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void f(); - __attribute__((noreturn)) void g(); - - void target(STATUSOR_INT sor) { - sor.ok() ? f() : g(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - __attribute__((noreturn)) void f(); - void g(); - - void target(STATUSOR_INT sor) { - !sor.ok() ? f() : g(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void f(); - void g(); - - void target(STATUSOR_INT sor) { - sor.ok() ? f() : g(); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void terminate() __attribute__((noreturn)); - - void target(STATUSOR_INT sor) { - sor.value(); // [[unsafe]] - terminate(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void terminate() __attribute__((noreturn)); - - void target(STATUSOR_INT sor) { - if (sor.ok()) sor.value(); - terminate(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void terminate() __attribute__((noreturn)); - - struct Foo { - ~Foo() __attribute__((noreturn)); - }; - - void target() { - auto sor = Make>(); - !(false || !(sor.ok())) ? (void)0 : terminate(); - sor.value(); - terminate(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, DeclInLoop) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - while (auto ok = sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - using BoolAlias = bool; - - void target(STATUSOR_INT sor) { - while (BoolAlias ok = sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - while (Make()) { - STATUSOR_INT sor = Make(); - sor.value(); // [[unsafe]] - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - using StatusOrInt = STATUSOR_INT; - - void target() { - while (Make()) { - StatusOrInt sor = Make(); - sor.value(); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, NonEvaluatedExprInCondition) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - bool unknown(); - - void target(STATUSOR_INT sor) { - if (unknown() && sor.ok()) sor.value(); - if (sor.ok() && unknown()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - bool unknown(); - - void target(STATUSOR_INT sor) { - if (!(!unknown() || !sor.ok())) sor.value(); - if (!(!sor.ok() || !unknown())) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - bool unknown(); - - void target(STATUSOR_INT sor) { - if (unknown() || sor.ok()) sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - bool unknown(); - - void target(STATUSOR_INT sor) { - if (sor.ok() || unknown()) sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, CorrelatedBranches) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (b || sor.ok()) { - if (!b) sor.value(); - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (b && !sor.ok()) return; - if (b) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (sor.ok()) b = true; - if (b) sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - if (b) return; - if (sor.ok()) b = true; - if (b) sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ConditionWithInitStmt) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - if (STATUSOR_INT sor = Make(); sor.ok()) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - if (STATUSOR_INT sor = Make(); !sor.ok()) - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, DeadCode) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - bool b = false; - if (b) sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - bool b; - b = false; - if (b) sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, TemporaryDestructors) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - sor.ok() ? sor.value() : Fatal().value(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - !sor.ok() ? Fatal().value() : sor.value(); - - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - b ? 0 : sor.ok() ? sor.value() : Fatal().value(); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - for (int i = 0; i < 10; i++) { - (b && sor.ok()) ? sor.value() : Fatal().value(); - - if (b) sor.value(); - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - for (int i = 0; i < 10; i++) { - (b || !sor.ok()) ? Fatal().value() : 0; - - if (!b) sor.value(); - } - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b, STATUSOR_INT sor) { - for (int i = 0; i < 10; i++) { - (false || !(b && sor.ok())) ? Fatal().value() : 0; - - do { - sor.value(); - } while (b); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, CheckMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - CHECK(sor.ok()); - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - CHECK(!sor.ok()); - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, QcheckMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - QCHECK(sor.ok()); - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - QCHECK(!sor.ok()); - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, CheckNeMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - CHECK_NE(sor.status(), absl::OkStatus()); - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, QcheckNeMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - QCHECK_NE(sor.status(), absl::OkStatus()); - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, GlobalVars) { - // The following examples are not sound as there could be opaque calls between - // the ok() and the value() calls that change the StatusOr value. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - static STATUSOR_INT sor; - - void target() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - static STATUSOR_INT sor; - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - static STATUSOR_INT sor; - }; - - void target(Foo foo) { - if (foo.sor.ok()) - foo.sor.value(); - else - foo.sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - static STATUSOR_INT sor; - }; - - void target() { - if (Foo::sor.ok()) - Foo::sor.value(); - else - Foo::sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - static STATUSOR_INT sor; - - static void target() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - }; - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - static STATUSOR_INT sor; - - void target() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - }; - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct S { - static const int x = -1; - }; - - int target(S s) { return s.x; } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ReferenceReceivers) { - // The following examples are not sound as there could be opaque calls between - // the ok() and the value() calls that change the StatusOr value. However, - // this is the behavior that users expect so it is here to stay. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT& sor) { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT& sor; - }; - - void target(Foo foo) { - if (foo.sor.ok()) - foo.sor.value(); - else - foo.sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Bar { - STATUSOR_INT sor; - }; - - struct Foo { - Bar& bar; - }; - - void target(Foo foo) { - if (foo.bar.sor.ok()) - foo.bar.sor.value(); - else - foo.bar.sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT& sor; - }; - - void target(Foo& foo) { - if (foo.sor.ok()) - foo.sor.value(); - else - foo.sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, Lambdas) { - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - [](STATUSOR_INT sor) { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(Make()); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - [sor]() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - [&sor]() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - [sor2 = sor]() { - if (sor2.ok()) - sor2.value(); - else - sor2.value(); // [[unsafe]] - }(); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - [&]() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - [=]() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(); - } - )cc"); - ExpectDiagnosticsForLambda(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct Foo { - STATUSOR_INT sor; - - void target() { - [this]() { - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - }(); - } - }; - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, GoodLambda) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - int target() { - STATUSOR_INT sor = Make(); - if (sor.ok()) return [&s = sor.value()] { return s; }(); - return 0; - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, Status) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void foo(); - - void target(STATUS s) { - if (s.ok()) foo(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void foo(); - - void target() { - STATUS s = Make().status(); - if (s.ok()) foo(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ExpectThatMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - EXPECT_THAT(sor, testing::status::IsOk()); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - EXPECT_THAT(sor.status(), testing::status::IsOk()); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT sor = Make(); - EXPECT_THAT(sor, testing::status::IsOk()); - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ExpectOkMacro) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - EXPECT_OK(sor); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - EXPECT_OK(sor.status()); - - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT sor = Make(); - EXPECT_OK(sor); - - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, BreadthFirstBlockTraversalLoop) { - // Evaluating the CFG blocks of the code below in breadth-first order results - // in an infinite loop. Each iteration of the while loop below results in a - // new value being assigned to the storage location of sor1. However, - // following a bread-first order of evaluation, downstream blocks will join - // environments of different generations of predecessor blocks having distinct - // values assigned to the sotrage location of sor1, resulting in not assigning - // a value to the storage location of sor1 in successors. As iterations of the - // analysis go, the state of the environment flips between having a value - // assigned to the storage location of sor1 and not having a value assigned to - // it. Since the evaluation of the copy constructor expression in bar(sor1) - // depends on a value being assigned to sor1, the state of the environment - // also flips between having a storage location assigned to the bar(sor1) - // expression and not having a storage location assigned to it. This leads to - // an infinite loop as the environment can't stabilize. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void foo(int, int); - STATUSOR_INT bar(STATUSOR_INT); - void baz(int); - - void target() { - while (true) { - STATUSOR_INT sor1 = Make(); - if (sor1.ok()) { - STATUSOR_INT sor2 = Make(); - if (sor2.ok()) foo(sor1.value(), sor2.value()); - } - - STATUSOR_INT sor3 = bar(sor1); - for (int i = 0; i < 5; i++) sor3 = bar(sor1); - - baz(sor3.value()); // [[unsafe]] - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ReturnValue) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - STATUSOR_INT sor = Make(); - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, Goto) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - label: - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - goto label; - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - label: - if (!sor.ok()) goto label; - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - if (!sor.ok()) return; - goto label; - label: - sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, JoinDistinctValues) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b) { - STATUSOR_INT sor; - if (b) - sor = Make(); - else - sor = Make(); - - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b) { - STATUSOR_INT sor; - if (b) { - sor = Make(); - if (!sor.ok()) return; - } else { - sor = Make(); - if (!sor.ok()) return; - } - sor.value(); - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(bool b) { - STATUSOR_INT sor; - if (b) { - sor = Make(); - if (!sor.ok()) return; - } else { - sor = Make(); - } - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, VarDeclInitExprFromPairAccess) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - auto sor = Make>().second; - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - const auto& sor = Make>().second; - if (sor.ok()) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, LValueToRValueCastOfChangingValue) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - bool foo(); - - void target(bool b1) { - STATUSOR_INT sor; - if (b1) - sor = Make(); - else - sor = Make(); - - do { - const auto& b2 = foo(); - if (b2) break; - - sor.value(); // [[unsafe]] - } while (true); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, ConstructorInitializer) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - class target { - target() : foo_(Make().value()) { // [[unsafe]] - } - int foo_; - }; - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, AssignStatusToBoolVar) { - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - bool ok = sor.ok(); - if (ok) - sor.value(); - else - sor.value(); // [[unsafe]] - } - )cc"); - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor) { - bool not_ok = !sor.ok(); - if (not_ok) - sor.value(); // [[unsafe]] - else - sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, StructuredBindings) { - // Binding to a pair (which is actually a struct in the mock header). - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - const auto [sor, x] = Make>(); - if (sor.ok()) sor.value(); - } - )cc"); - - // Unsafe case. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - const auto [sor, x] = Make>(); - sor.value(); // [[unsafe]] - } - )cc"); - - // As a reference. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - const auto& [sor, x] = Make>(); - if (sor.ok()) sor.value(); - } - )cc"); - - // Binding to a ref in a struct. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - struct S { - STATUSOR_INT& sor; - int i; - }; - - void target() { - const auto& [sor, i] = Make(); - if (sor.ok()) sor.value(); - } - )cc"); - - // In a loop. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - auto vals = Make>>(); - for (const auto& [x, sor] : vals) - if (sor.ok()) sor.value(); - } - )cc"); - - // Similar to the above, but InitExpr already has the storage initialized, - // and bindings refer to them. - ExpectDiagnosticsFor(R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target() { - auto vals = Make>>(); - for (const auto& p : vals) { - const auto& [i, sor] = p; - if (sor.ok()) sor.value(); - } - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, AssignCompositeLogicExprToVar) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor, bool b) { - bool c = sor.ok() && b; - if (c) sor.value(); - } - )cc"); - - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - void target(STATUSOR_INT sor, bool b) { - bool c = !(!sor.ok() || !b); - if (c) sor.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, Subclass) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - class Foo : public STATUSOR_INT {}; - - void target(Foo opt) { - opt.value(); // [[unsafe]] - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, SubclassStatus) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - class Foo : public STATUS {}; - - void target(Foo opt) { opt.ok(); } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, SubclassOk) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - class Foo : public STATUSOR_INT {}; - - void target(Foo opt) { - if (opt.ok()) opt.value(); - } - )cc"); -} - -TEST_P(UncheckedStatusOrAccessModelTest, SubclassOperator) { - ExpectDiagnosticsFor( - R"cc( -#include "unchecked_statusor_access_test_defs.h" - - class Foo : public STATUSOR_INT {}; - - void target(Foo opt) { - *opt; // [[unsafe]] - } - )cc"); -} - -} // namespace - -std::string -GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind) { - switch (AliasKind) { - case UncheckedStatusOrAccessModelTestAliasKind::kUnaliased: - return R"cc( -#define STATUSOR_INT ::absl::StatusOr -#define STATUSOR_BOOL ::absl::StatusOr -#define STATUSOR_VOIDPTR ::absl::StatusOr -#define STATUS ::absl::Status - )cc"; - case UncheckedStatusOrAccessModelTestAliasKind::kPartiallyAliased: - return R"cc( - template - using StatusOrAlias = ::absl::StatusOr; -#define STATUSOR_INT StatusOrAlias -#define STATUSOR_BOOL StatusOrAlias -#define STATUSOR_VOIDPTR StatusOrAlias -#define STATUS ::absl::Status - )cc"; - case UncheckedStatusOrAccessModelTestAliasKind::kFullyAliased: - return R"cc( - using StatusOrIntAlias = ::absl::StatusOr; -#define STATUSOR_INT StatusOrIntAlias - using StatusOrBoolAlias = ::absl::StatusOr; -#define STATUSOR_BOOL StatusOrBoolAlias - using StatusOrVoidPtrAlias = ::absl::StatusOr; -#define STATUSOR_VOIDPTR StatusOrVoidPtrAlias - using StatusAlias = ::absl::Status; -#define STATUS StatusAlias - )cc"; - } - llvm_unreachable("Unknown alias kind."); -} - -std::vector> -GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind) { - auto Headers = test::getMockHeaders(); - - Headers.emplace_back("unchecked_statusor_access_test_defs.h", - R"cc( -#include "cstddef.h" -#include "statusor_defs.h" -#include "std_optional.h" -#include "std_vector.h" -#include "std_pair.h" -#include "absl_log.h" -#include "testing_defs.h" - - template - T Make(); - - class Fatal { - public: - ~Fatal() __attribute__((noreturn)); - int value(); - }; - )cc" + - GetAliasMacros(AliasKind)); - return Headers; -} -} // namespace clang::dataflow::statusor_model diff --git a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h b/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h deleted file mode 100644 index ff1d323a0086f..0000000000000 --- a/clang/unittests/Analysis/FlowSensitive/UncheckedStatusOrAccessModelTestFixture.h +++ /dev/null @@ -1,157 +0,0 @@ -//===- UncheckedStatusOrAccessModelTestFixture.h --------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ -#define LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ - -#include -#include -#include -#include -#include - -#include "TestingSupport.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" -#include "clang/ASTMatchers/ASTMatchers.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" -#include "clang/Analysis/FlowSensitive/MatchSwitch.h" -#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Tooling/Tooling.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/Support/Error.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace clang::dataflow::statusor_model { - -enum class UncheckedStatusOrAccessModelTestAliasKind { - kUnaliased = 0, // no alias - kPartiallyAliased = 1, // template using Alias = absl::StatusOr; - kFullyAliased = 2, // using Alias = absl::StatusOr; -}; - -// Base class for the test executors. This is needed to abstract away the -// template parameter from the UncheckedStatusOrAccessModelTestExecutor. This -// allows us to use UncheckedStatusOrAccessModelTestExecutorBase* in the -// UncheckedStatusOrAccessModelTest. -class UncheckedStatusOrAccessModelTestExecutorBase { -public: - virtual void - ExpectDiagnosticsFor(std::string SourceCode, - UncheckedStatusOrAccessModelTestAliasKind) const = 0; - virtual void ExpectDiagnosticsForLambda( - std::string SourceCode, - UncheckedStatusOrAccessModelTestAliasKind) const = 0; - virtual ~UncheckedStatusOrAccessModelTestExecutorBase() = default; -}; - -// Returns these macros according to the alias kind: -// - STATUS -// - STATUSOR_INT -// - STATUSOR_BOOL -// - STATUSOR_VOIDPTR -// Tests should use these macros instead of e.g. absl::StatusOr to ensure -// the model is insensitive to whether the StatusOr<> is aliased or not. -std::string GetAliasMacros(UncheckedStatusOrAccessModelTestAliasKind AliasKind); - -std::vector> -GetHeaders(UncheckedStatusOrAccessModelTestAliasKind AliasKind); - -// This allows us to run the same test suite for multiple models. This allows -// vendors to model internal APIs in an extension of the base model, and make -// sure that these tests still pass. -template -class UncheckedStatusOrAccessModelTestExecutor - : public UncheckedStatusOrAccessModelTestExecutorBase { -public: - void ExpectDiagnosticsFor( - std::string SourceCode, - UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - ExpectDiagnosticsFor(SourceCode, hasName("target"), AliasKind); - } - - void ExpectDiagnosticsForLambda( - std::string SourceCode, - UncheckedStatusOrAccessModelTestAliasKind AliasKind) const override { - using namespace ::clang::ast_matchers; // NOLINT: Too many names - ExpectDiagnosticsFor(SourceCode, - allOf(hasOverloadedOperatorName("()"), - hasDeclContext(cxxRecordDecl(isLambda()))), - AliasKind); - } - - template - void ExpectDiagnosticsFor( - std::string SourceCode, FuncDeclMatcher FuncMatcher, - UncheckedStatusOrAccessModelTestAliasKind AliasKind) const { - std::vector> Headers = - GetHeaders(AliasKind); - - UncheckedStatusOrAccessModelOptions Options{}; - std::vector Diagnostics; - llvm::Error Error = test::checkDataflow( - test::AnalysisInputs( - SourceCode, std::move(FuncMatcher), - [](ASTContext &Ctx, Environment &Env) { return Model(Ctx, Env); }) - .withPostVisitCFG( - [&Diagnostics, - Diagnoser = UncheckedStatusOrAccessDiagnoser(Options)]( - ASTContext &Ctx, const CFGElement &Elt, - const TransferStateForDiagnostics< - UncheckedStatusOrAccessModel::Lattice> &State) mutable { - auto EltDiagnostics = Diagnoser(Elt, Ctx, State); - llvm::move(EltDiagnostics, std::back_inserter(Diagnostics)); - }) - .withASTBuildArgs( - {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}) - .withASTBuildVirtualMappedFiles( - tooling::FileContentMappings(Headers.begin(), Headers.end())), - /*VerifyResults=*/[&Diagnostics, SourceCode]( - const llvm::DenseMap - &Annotations, - const test::AnalysisOutputs &AO) { - llvm::DenseSet AnnotationLines; - for (const auto &[Line, _] : Annotations) - AnnotationLines.insert(Line); - auto &SrcMgr = AO.ASTCtx.getSourceManager(); - llvm::DenseSet DiagnosticLines; - for (SourceLocation &Loc : Diagnostics) - DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc)); - - EXPECT_THAT(DiagnosticLines, testing::ContainerEq(AnnotationLines)) - << "\nFailing code:\n" - << SourceCode; - }); - if (Error) - FAIL() << llvm::toString(std::move(Error)); - } -}; - -class UncheckedStatusOrAccessModelTest - : public ::testing::TestWithParam< - std::pair> { -protected: - void ExpectDiagnosticsFor(std::string SourceCode) { - GetParam().first->ExpectDiagnosticsFor(SourceCode, GetParam().second); - } - - void ExpectDiagnosticsForLambda(std::string SourceCode) { - GetParam().first->ExpectDiagnosticsForLambda(SourceCode, GetParam().second); - } -}; - -} // namespace clang::dataflow::statusor_model - -#endif // LLVM_CLANG_ANALYSIS_FLOW_SENSITIVE_UNCHECKEDSTATUSORACCESSMODELTESTFIXTURE_H_ diff --git a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn index 01a05bcfda415..3fd3aab7970d5 100644 --- a/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/Analysis/FlowSensitive/Models/BUILD.gn @@ -11,6 +11,5 @@ static_library("Models") { sources = [ "ChromiumCheckModel.cpp", "UncheckedOptionalAccessModel.cpp", - "UncheckedStatusOrAccessModel.cpp", ] } diff --git a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn index c74a44c7016c6..c9f3a0745ed46 100644 --- a/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/Analysis/FlowSensitive/BUILD.gn @@ -44,8 +44,6 @@ unittest("ClangAnalysisFlowSensitiveTests") { "TransferTest.cpp", "TypeErasedDataflowAnalysisTest.cpp", "UncheckedOptionalAccessModelTest.cpp", - "UncheckedStatusOrAccessModelTest.cpp", - "UncheckedStatusOrAccessModelTestFixture.cpp", "ValueTest.cpp", "WatchedLiteralsSolverTest.cpp", ]