namespace clang {
class ASTContext;
+class ASTImporterSharedState;
class Attr;
-class ASTImporterLookupTable;
class CXXBaseSpecifier;
class CXXCtorInitializer;
class Decl;
};
private:
-
- /// Pointer to the import specific lookup table, which may be shared
- /// amongst several ASTImporter objects.
- /// This is an externally managed resource (and should exist during the
- /// lifetime of the ASTImporter object)
- /// If not set then the original C/C++ lookup is used.
- ASTImporterLookupTable *LookupTable = nullptr;
+ std::shared_ptr<ASTImporterSharedState> SharedState = nullptr;
/// The path which we go through during the import of a given AST node.
ImportPathTy ImportPath;
ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
- ASTImporterLookupTable *LookupTable = nullptr);
+ std::shared_ptr<ASTImporterSharedState> SharedState = nullptr);
virtual ~ASTImporter();
--- /dev/null
+//===- ASTImporterSharedState.h - ASTImporter specific state --*- C++ -*---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the ASTImporter specific state, which may be shared
+// amongst several ASTImporter objects.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_ASTIMPORTERSHAREDSTATE_H
+#define LLVM_CLANG_AST_ASTIMPORTERSHAREDSTATE_H
+
+#include "clang/AST/ASTImporterLookupTable.h"
+#include "llvm/ADT/DenseMap.h"
+// FIXME We need this because of ImportError.
+#include "clang/AST/ASTImporter.h"
+
+namespace clang {
+
+class TranslationUnitDecl;
+
+/// Importer specific state, which may be shared amongst several ASTImporter
+/// objects.
+class ASTImporterSharedState {
+
+ /// Pointer to the import specific lookup table.
+ std::unique_ptr<ASTImporterLookupTable> LookupTable;
+
+ /// Mapping from the already-imported declarations in the "to"
+ /// context to the error status of the import of that declaration.
+ /// This map contains only the declarations that were not correctly
+ /// imported. The same declaration may or may not be included in
+ /// ImportedFromDecls. This map is updated continuously during imports and
+ /// never cleared (like ImportedFromDecls).
+ llvm::DenseMap<Decl *, ImportError> ImportErrors;
+
+ // FIXME put ImportedFromDecls here!
+ // And from that point we can better encapsulate the lookup table.
+
+public:
+ ASTImporterSharedState() = default;
+
+ ASTImporterSharedState(TranslationUnitDecl &ToTU) {
+ LookupTable = llvm::make_unique<ASTImporterLookupTable>(ToTU);
+ }
+
+ ASTImporterLookupTable *getLookupTable() { return LookupTable.get(); }
+
+ void addDeclToLookup(Decl *D) {
+ if (LookupTable)
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ LookupTable->add(ND);
+ }
+
+ void removeDeclFromLookup(Decl *D) {
+ if (LookupTable)
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ LookupTable->remove(ND);
+ }
+
+ llvm::Optional<ImportError> getImportDeclErrorIfAny(Decl *ToD) const {
+ auto Pos = ImportErrors.find(ToD);
+ if (Pos != ImportErrors.end())
+ return Pos->second;
+ else
+ return Optional<ImportError>();
+ }
+
+ void setImportDeclError(Decl *To, ImportError Error) {
+ ImportErrors[To] = Error;
+ }
+};
+
+} // namespace clang
+#endif // LLVM_CLANG_AST_ASTIMPORTERSHAREDSTATE_H
#ifndef LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
#define LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
-#include "clang/AST/ASTImporterLookupTable.h"
+#include "clang/AST/ASTImporterSharedState.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
void emitCrossTUDiagnostics(const IndexError &IE);
private:
- void lazyInitLookupTable(TranslationUnitDecl *ToTU);
+ void lazyInitImporterSharedSt(TranslationUnitDecl *ToTU);
ASTImporter &getOrCreateASTImporter(ASTContext &From);
template <typename T>
llvm::Expected<const T *> getCrossTUDefinitionImpl(const T *D,
ASTUnitImporterMap;
CompilerInstance &CI;
ASTContext &Context;
- std::unique_ptr<ASTImporterLookupTable> LookupTable;
+ std::shared_ptr<ASTImporterSharedState> ImporterSharedSt;
};
} // namespace cross_tu
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTImporter.h"
-#include "clang/AST/ASTImporterLookupTable.h"
+#include "clang/AST/ASTImporterSharedState.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTStructuralEquivalence.h"
ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
- ASTImporterLookupTable *LookupTable)
- : LookupTable(LookupTable), ToContext(ToContext), FromContext(FromContext),
+ std::shared_ptr<ASTImporterSharedState> SharedState)
+ : SharedState(SharedState), ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
Minimal(MinimalImport) {
+ // Create a default state without the lookup table: LLDB case.
+ if (!SharedState) {
+ this->SharedState = std::make_shared<ASTImporterSharedState>();
+ }
+
ImportedDecls[FromContext.getTranslationUnitDecl()] =
ToContext.getTranslationUnitDecl();
}
// then the enum constant 'A' and the variable 'A' violates ODR.
// We can diagnose this only if we search in the redecl context.
DeclContext *ReDC = DC->getRedeclContext();
- if (LookupTable) {
+ if (SharedState->getLookupTable()) {
ASTImporterLookupTable::LookupResult LookupResult =
- LookupTable->lookup(ReDC, Name);
+ SharedState->getLookupTable()->lookup(ReDC, Name);
return FoundDeclsTy(LookupResult.begin(), LookupResult.end());
} else {
// FIXME Can we remove this kind of lookup?
}
void ASTImporter::AddToLookupTable(Decl *ToD) {
- if (LookupTable)
- if (auto *ToND = dyn_cast<NamedDecl>(ToD))
- LookupTable->add(ToND);
+ SharedState->addDeclToLookup(ToD);
}
Expected<Decl *> ASTImporter::ImportImpl(Decl *FromD) {
// Check whether we've already imported this declaration.
Decl *ToD = GetAlreadyImportedOrNull(FromD);
if (ToD) {
+ // Already imported (possibly from another TU) and with an error.
+ if (auto Error = SharedState->getImportDeclErrorIfAny(ToD)) {
+ setImportDeclError(FromD, *Error);
+ return make_error<ImportError>(*Error);
+ }
+
// If FromD has some updated flags after last import, apply it
updateFlags(FromD, ToD);
// If we encounter a cycle during an import then we save the relevant part
// traverse of the 'to' context).
auto PosF = ImportedFromDecls.find(ToD);
if (PosF != ImportedFromDecls.end()) {
- if (LookupTable)
- if (auto *ToND = dyn_cast<NamedDecl>(ToD))
- LookupTable->remove(ToND);
+ SharedState->removeDeclFromLookup(ToD);
ImportedFromDecls.erase(PosF);
}
// FIXME: AST may contain remaining references to the failed object.
+ // However, the ImportDeclErrors in the shared state contains all the
+ // failed objects together with their error.
}
- // After takeError the error is not usable anymore in ToDOrErr.
+ // Error encountered for the first time.
+ // After takeError the error is not usable any more in ToDOrErr.
// Get a copy of the error object (any more simple solution for this?).
ImportError ErrOut;
handleAllErrors(ToDOrErr.takeError(),
[&ErrOut](const ImportError &E) { ErrOut = E; });
setImportDeclError(FromD, ErrOut);
+ // Set the error for the mapped to Decl, which is in the "to" context.
+ if (Pos != ImportedDecls.end())
+ SharedState->setImportDeclError(Pos->second, ErrOut);
// Set the error for all nodes which have been created before we
// recognized the error.
for (const auto &Path : SavedImportPaths[FromD])
- for (Decl *Di : Path)
- setImportDeclError(Di, ErrOut);
+ for (Decl *FromDi : Path) {
+ setImportDeclError(FromDi, ErrOut);
+ //FIXME Should we remove these Decls from ImportedDecls?
+ // Set the error for the mapped to Decl, which is in the "to" context.
+ auto Ii = ImportedDecls.find(FromDi);
+ if (Ii != ImportedDecls.end())
+ SharedState->setImportDeclError(Ii->second, ErrOut);
+ // FIXME Should we remove these Decls from the LookupTable,
+ // and from ImportedFromDecls?
+ }
SavedImportPaths[FromD].clear();
// Do not return ToDOrErr, error was taken out of it.
return make_error<ImportError>(*Err);
}
+ // We could import from the current TU without error. But previously we
+ // already had imported a Decl as `ToD` from another TU (with another
+ // ASTImporter object) and with an error.
+ if (auto Error = SharedState->getImportDeclErrorIfAny(ToD)) {
+ setImportDeclError(FromD, *Error);
+ return make_error<ImportError>(*Error);
+ }
+
// Make sure that ImportImpl registered the imported decl.
assert(ImportedDecls.count(FromD) != 0 && "Missing call to MapImported?");
+
// Notify subclasses.
Imported(FromD, ToD);
return importDefinitionImpl(VD);
}
-void CrossTranslationUnitContext::lazyInitLookupTable(
+void CrossTranslationUnitContext::lazyInitImporterSharedSt(
TranslationUnitDecl *ToTU) {
- if (!LookupTable)
- LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+ if (!ImporterSharedSt)
+ ImporterSharedSt = std::make_shared<ASTImporterSharedState>(*ToTU);
}
ASTImporter &
auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
if (I != ASTUnitImporterMap.end())
return *I->second;
- lazyInitLookupTable(Context.getTranslationUnitDecl());
+ lazyInitImporterSharedSt(Context.getTranslationUnitDecl());
ASTImporter *NewImporter = new ASTImporter(
Context, Context.getSourceManager().getFileManager(), From,
- From.getSourceManager().getFileManager(), false, LookupTable.get());
+ From.getSourceManager().getFileManager(), false, ImporterSharedSt);
ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
return *NewImporter;
}
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTImporter.h"
-#include "clang/AST/ASTImporterLookupTable.h"
+#include "clang/AST/ASTImporterSharedState.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
&CI.getASTContext());
IntrusiveRefCntPtr<DiagnosticIDs>
DiagIDs(CI.getDiagnostics().getDiagnosticIDs());
- ASTImporterLookupTable LookupTable(
+ auto SharedState = std::make_shared<ASTImporterSharedState>(
*CI.getASTContext().getTranslationUnitDecl());
for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) {
IntrusiveRefCntPtr<DiagnosticsEngine>
ASTImporter Importer(CI.getASTContext(), CI.getFileManager(),
Unit->getASTContext(), Unit->getFileManager(),
- /*MinimalImport=*/false, &LookupTable);
+ /*MinimalImport=*/false, SharedState);
TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
for (auto *D : TU->decls()) {
#include "ASTImporterFixtures.h"
#include "clang/AST/ASTImporter.h"
-#include "clang/AST/ASTImporterLookupTable.h"
+#include "clang/AST/ASTImporterSharedState.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/Tooling.h"
if (!Creator)
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
- bool MinimalImport, ASTImporterLookupTable *LookupTable) {
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new ASTImporter(ToContext, ToFileManager, FromContext,
- FromFileManager, MinimalImport, LookupTable);
+ FromFileManager, MinimalImport, SharedState);
};
}
ASTImporterTestBase::TU::~TU() {}
void ASTImporterTestBase::TU::lazyInitImporter(
- ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) {
+ const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST) {
assert(ToAST);
if (!Importer)
Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(),
Unit->getASTContext(), Unit->getFileManager(), false,
- &LookupTable));
+ SharedState));
assert(&ToAST->getASTContext() == &Importer->getToContext());
createVirtualFileIfNeeded(ToAST, FileName, Code);
}
-Decl *ASTImporterTestBase::TU::import(ASTImporterLookupTable &LookupTable,
- ASTUnit *ToAST, Decl *FromDecl) {
- lazyInitImporter(LookupTable, ToAST);
+Decl *ASTImporterTestBase::TU::import(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
+ Decl *FromDecl) {
+ lazyInitImporter(SharedState, ToAST);
if (auto ImportedOrErr = Importer->Import(FromDecl))
return *ImportedOrErr;
else {
}
}
-QualType ASTImporterTestBase::TU::import(ASTImporterLookupTable &LookupTable,
- ASTUnit *ToAST, QualType FromType) {
- lazyInitImporter(LookupTable, ToAST);
+QualType ASTImporterTestBase::TU::import(
+ const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
+ QualType FromType) {
+ lazyInitImporter(SharedState, ToAST);
if (auto ImportedOrErr = Importer->Import(FromType))
return *ImportedOrErr;
else {
}
}
-void ASTImporterTestBase::lazyInitLookupTable(TranslationUnitDecl *ToTU) {
+void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) {
assert(ToTU);
- if (!LookupTablePtr)
- LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+ if (!SharedStatePtr)
+ SharedStatePtr = std::make_shared<ASTImporterSharedState>(*ToTU);
}
void ASTImporterTestBase::lazyInitToAST(Language ToLang, StringRef ToSrcCode,
// Build the AST from an empty file.
ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
ToAST->enableSourceFileDiagnostics();
- lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl());
+ lazyInitSharedState(ToAST->getASTContext().getTranslationUnitDecl());
}
ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) {
assert(FoundDecls.size() == 1);
Decl *Imported =
- FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front());
+ FromTU.import(SharedStatePtr, ToAST.get(), FoundDecls.front());
assert(Imported);
return std::make_tuple(*FoundDecls.begin(), Imported);
Decl *ASTImporterTestBase::Import(Decl *From, Language ToLang) {
lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(From);
- assert(LookupTablePtr);
- return FromTU->import(*LookupTablePtr, ToAST.get(), From);
+ assert(SharedStatePtr);
+ Decl *To = FromTU->import(SharedStatePtr, ToAST.get(), From);
+ return To;
}
QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,
Language ToLang) {
lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(TUDecl);
- assert(LookupTablePtr);
- return FromTU->import(*LookupTablePtr, ToAST.get(), FromType);
+ assert(SharedStatePtr);
+ return FromTU->import(SharedStatePtr, ToAST.get(), FromType);
}
ASTImporterTestBase::~ASTImporterTestBase() {
#include "gmock/gmock.h"
#include "clang/AST/ASTImporter.h"
-#include "clang/AST/ASTImporterLookupTable.h"
#include "clang/Frontend/ASTUnit.h"
+#include "clang/AST/ASTImporterSharedState.h"
#include "DeclMatcher.h"
#include "Language.h"
namespace clang {
class ASTImporter;
-class ASTImporterLookupTable;
+class ASTImporterSharedState;
class ASTUnit;
namespace ast_matchers {
public:
/// Allocates an ASTImporter (or one of its subclasses).
- typedef std::function<ASTImporter *(ASTContext &, FileManager &, ASTContext &,
- FileManager &, bool,
- ASTImporterLookupTable *)>
+ typedef std::function<ASTImporter *(
+ ASTContext &, FileManager &, ASTContext &, FileManager &, bool,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState)>
ImporterConstructor;
// The lambda that constructs the ASTImporter we use in this test.
ImporterConstructor C = ImporterConstructor());
~TU();
- void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST);
- Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
- Decl *FromDecl);
- QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
- QualType FromType);
+ void
+ lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST);
+ Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST, Decl *FromDecl);
+ QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
+ ASTUnit *ToAST, QualType FromType);
};
// We may have several From contexts and related translation units. In each
// vector is expanding, with the list we won't have these issues.
std::list<TU> FromTUs;
- // Initialize the lookup table if not initialized already.
- void lazyInitLookupTable(TranslationUnitDecl *ToTU);
+ // Initialize the shared state if not initialized already.
+ void lazyInitSharedState(TranslationUnitDecl *ToTU);
void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName);
protected:
- std::unique_ptr<ASTImporterLookupTable> LookupTablePtr;
+ std::shared_ptr<ASTImporterSharedState> SharedStatePtr;
public:
// We may have several From context but only one To context.
RedirectingImporterTest() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
- bool MinimalImport, ASTImporterLookupTable *LookupTable) {
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new RedirectingImporter(ToContext, ToFileManager, FromContext,
FromFileManager, MinimalImport,
- LookupTable);
+ SharedState);
};
}
};
CXXMethodDecl *Method =
FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
ToClass->removeDecl(Method);
- LookupTablePtr->remove(Method);
+ SharedStatePtr->getLookupTable()->remove(Method);
}
ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
ErrorHandlingTest() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
- bool MinimalImport, ASTImporterLookupTable *LookupTable) {
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new ASTImporterWithFakeErrors(ToContext, ToFileManager,
FromContext, FromFileManager,
- MinimalImport, LookupTable);
+ MinimalImport, SharedState);
};
}
// In this test we purposely report an error (UnsupportedConstruct) when
EXPECT_TRUE(ImportedOK);
}
+// An error should be set for a class if it had a previous import with an error
+// from another TU.
+TEST_P(ErrorHandlingTest,
+ ImportedDeclWithErrorShouldFailTheImportOfDeclWhichMapToIt) {
+ // We already have a fwd decl.
+ TranslationUnitDecl *ToTU = getToTuDecl(
+ "class X;", Lang_CXX);
+ // Then we import a definition.
+ {
+ TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( }
+ void ok();
+ };
+ )",
+ Lang_CXX);
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // An error is set for X ...
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ }
+ // ... but the node had been created.
+ auto *ToXDef = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X"), isDefinition()));
+ // An error is set for "ToXDef" in the shared state.
+ Optional<ImportError> OptErr =
+ SharedStatePtr->getImportDeclErrorIfAny(ToXDef);
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+
+ auto *ToXFwd = FirstDeclMatcher<CXXRecordDecl>().match(
+ ToTU, cxxRecordDecl(hasName("X"), unless(isDefinition())));
+ // An error is NOT set for the fwd Decl of X in the shared state.
+ OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXFwd);
+ ASSERT_FALSE(OptErr);
+
+ // Try to import X again but from another TU.
+ {
+ TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
+ class X {
+ void f() { )") + ErroneousStmt + R"( }
+ void ok();
+ };
+ )",
+ Lang_CXX, "input1.cc");
+
+ auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("X")));
+ CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);
+
+ // If we did not save the errors for the "to" context then the below checks
+ // would fail, because the lookup finds the fwd Decl of the existing
+ // definition in the "to" context. We can reach the existing definition via
+ // the found fwd Decl. That existing definition is structurally equivalent
+ // (we check only the fields) with this one we want to import, so we return
+ // with the existing definition, which is erroneous (one method is missing).
+
+ // The import should fail.
+ EXPECT_FALSE(ImportedX);
+ ASTImporter *Importer = findFromTU(FromX)->Importer.get();
+ Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
+ // And an error is set for this new X in the "from" ctx.
+ ASSERT_TRUE(OptErr);
+ EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
+ }
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest,
DefaultTestValuesForRunOptions, );