| 1 | //===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | /// \file |
| 10 | /// Fixture classes for testing the ASTImporter. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H |
| 15 | #define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H |
| 16 | |
| 17 | #include "gmock/gmock.h" |
| 18 | |
| 19 | #include "clang/AST/ASTImporter.h" |
| 20 | #include "clang/AST/ASTImporterSharedState.h" |
| 21 | #include "clang/Frontend/ASTUnit.h" |
| 22 | #include "clang/Testing/CommandLineArgs.h" |
| 23 | #include "llvm/Support/Error.h" |
| 24 | #include "llvm/Support/ErrorHandling.h" |
| 25 | |
| 26 | #include "DeclMatcher.h" |
| 27 | #include "MatchVerifier.h" |
| 28 | |
| 29 | #include <sstream> |
| 30 | |
| 31 | namespace clang { |
| 32 | |
| 33 | class ASTImporter; |
| 34 | class ASTImporterSharedState; |
| 35 | class ASTUnit; |
| 36 | |
| 37 | namespace ast_matchers { |
| 38 | |
| 39 | const StringRef DeclToImportID = "declToImport" ; |
| 40 | const StringRef DeclToVerifyID = "declToVerify" ; |
| 41 | |
| 42 | // Creates a virtual file and assigns that to the context of given AST. If the |
| 43 | // file already exists then the file will not be created again as a duplicate. |
| 44 | void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, |
| 45 | std::unique_ptr<llvm::MemoryBuffer> &&Buffer); |
| 46 | |
| 47 | void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, |
| 48 | StringRef Code); |
| 49 | |
| 50 | // Common base for the different families of ASTImporter tests that are |
| 51 | // parameterized on the compiler options which may result a different AST. E.g. |
| 52 | // -fms-compatibility or -fdelayed-template-parsing. |
| 53 | class CompilerOptionSpecificTest : public ::testing::Test { |
| 54 | protected: |
| 55 | // Return the extra arguments appended to runtime options at compilation. |
| 56 | virtual std::vector<std::string> () const { return {}; } |
| 57 | |
| 58 | // Returns the argument vector used for a specific language option, this set |
| 59 | // can be tweaked by the test parameters. |
| 60 | std::vector<std::string> |
| 61 | getCommandLineArgsForLanguage(TestLanguage Lang) const { |
| 62 | std::vector<std::string> Args = getCommandLineArgsForTesting(Lang); |
| 63 | std::vector<std::string> = getExtraArgs(); |
| 64 | for (const auto &Arg : ExtraArgs) { |
| 65 | Args.push_back(x: Arg); |
| 66 | } |
| 67 | return Args; |
| 68 | } |
| 69 | }; |
| 70 | |
| 71 | const auto DefaultTestArrayForRunOptions = |
| 72 | std::array<std::vector<std::string>, 4>{ |
| 73 | ._M_elems: {std::vector<std::string>(), |
| 74 | std::vector<std::string>{"-fdelayed-template-parsing" }, |
| 75 | std::vector<std::string>{"-fms-compatibility" }, |
| 76 | std::vector<std::string>{"-fdelayed-template-parsing" , |
| 77 | "-fms-compatibility" }}}; |
| 78 | |
| 79 | const auto DefaultTestValuesForRunOptions = |
| 80 | ::testing::ValuesIn(container: DefaultTestArrayForRunOptions); |
| 81 | |
| 82 | // This class provides generic methods to write tests which can check internal |
| 83 | // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also, |
| 84 | // this fixture makes it possible to import from several "From" contexts. |
| 85 | class ASTImporterTestBase : public CompilerOptionSpecificTest { |
| 86 | |
| 87 | const char *const InputFileName = "input.cc" ; |
| 88 | const char *const OutputFileName = "output.cc" ; |
| 89 | |
| 90 | public: |
| 91 | /// Allocates an ASTImporter (or one of its subclasses). |
| 92 | typedef std::function<ASTImporter *( |
| 93 | ASTContext &, FileManager &, ASTContext &, FileManager &, bool, |
| 94 | const std::shared_ptr<ASTImporterSharedState> &SharedState)> |
| 95 | ImporterConstructor; |
| 96 | |
| 97 | // ODR handling type for the AST importer. |
| 98 | ASTImporter::ODRHandlingType ODRHandling; |
| 99 | |
| 100 | // The lambda that constructs the ASTImporter we use in this test. |
| 101 | ImporterConstructor Creator; |
| 102 | |
| 103 | private: |
| 104 | // Buffer for the To context, must live in the test scope. |
| 105 | std::string ToCode; |
| 106 | |
| 107 | // Represents a "From" translation unit and holds an importer object which we |
| 108 | // use to import from this translation unit. |
| 109 | struct TU { |
| 110 | // Buffer for the context, must live in the test scope. |
| 111 | std::string Code; |
| 112 | std::string FileName; |
| 113 | std::unique_ptr<ASTUnit> Unit; |
| 114 | TranslationUnitDecl *TUDecl = nullptr; |
| 115 | std::unique_ptr<ASTImporter> Importer; |
| 116 | ImporterConstructor Creator; |
| 117 | ASTImporter::ODRHandlingType ODRHandling; |
| 118 | |
| 119 | TU(StringRef Code, StringRef FileName, std::vector<std::string> Args, |
| 120 | ImporterConstructor C = ImporterConstructor(), |
| 121 | ASTImporter::ODRHandlingType ODRHandling = |
| 122 | ASTImporter::ODRHandlingType::Conservative); |
| 123 | ~TU(); |
| 124 | |
| 125 | void |
| 126 | lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| 127 | ASTUnit *ToAST); |
| 128 | Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| 129 | ASTUnit *ToAST, Decl *FromDecl); |
| 130 | llvm::Expected<Decl *> |
| 131 | importOrError(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| 132 | ASTUnit *ToAST, Decl *FromDecl); |
| 133 | QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| 134 | ASTUnit *ToAST, QualType FromType); |
| 135 | }; |
| 136 | |
| 137 | // We may have several From contexts and related translation units. In each |
| 138 | // AST, the buffers for the source are handled via references and are set |
| 139 | // during the creation of the AST. These references must point to a valid |
| 140 | // buffer until the AST is alive. Thus, we must use a list in order to avoid |
| 141 | // moving of the stored objects because that would mean breaking the |
| 142 | // references in the AST. By using a vector a move could happen when the |
| 143 | // vector is expanding, with the list we won't have these issues. |
| 144 | std::list<TU> FromTUs; |
| 145 | |
| 146 | // Initialize the shared state if not initialized already. |
| 147 | void lazyInitSharedState(TranslationUnitDecl *ToTU); |
| 148 | |
| 149 | void lazyInitToAST(TestLanguage ToLang, StringRef ToSrcCode, |
| 150 | StringRef FileName); |
| 151 | |
| 152 | protected: |
| 153 | std::shared_ptr<ASTImporterSharedState> SharedStatePtr; |
| 154 | |
| 155 | public: |
| 156 | // We may have several From context but only one To context. |
| 157 | std::unique_ptr<ASTUnit> ToAST; |
| 158 | |
| 159 | // Returns with the TU associated with the given Decl. |
| 160 | TU *findFromTU(Decl *From); |
| 161 | |
| 162 | // Creates an AST both for the From and To source code and imports the Decl |
| 163 | // of the identifier into the To context. |
| 164 | // Must not be called more than once within the same test. |
| 165 | std::tuple<Decl *, Decl *> |
| 166 | getImportedDecl(StringRef FromSrcCode, TestLanguage FromLang, |
| 167 | StringRef ToSrcCode, TestLanguage ToLang, |
| 168 | StringRef Identifier = DeclToImportID); |
| 169 | |
| 170 | // Creates a TU decl for the given source code which can be used as a From |
| 171 | // context. May be called several times in a given test (with different file |
| 172 | // name). |
| 173 | TranslationUnitDecl *getTuDecl(StringRef SrcCode, TestLanguage Lang, |
| 174 | StringRef FileName = "input.cc" ); |
| 175 | |
| 176 | // Creates the To context with the given source code and returns the TU decl. |
| 177 | TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, TestLanguage ToLang); |
| 178 | |
| 179 | // Import the given Decl into the ToCtx. |
| 180 | // May be called several times in a given test. |
| 181 | // The different instances of the param From may have different ASTContext. |
| 182 | Decl *Import(Decl *From, TestLanguage ToLang); |
| 183 | |
| 184 | template <class DeclT> DeclT *Import(DeclT *From, TestLanguage Lang) { |
| 185 | return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang)); |
| 186 | } |
| 187 | |
| 188 | // Import the given Decl into the ToCtx. |
| 189 | // Same as Import but returns the result of the import which can be an error. |
| 190 | llvm::Expected<Decl *> importOrError(Decl *From, TestLanguage ToLang); |
| 191 | |
| 192 | QualType ImportType(QualType FromType, Decl *TUDecl, TestLanguage ToLang); |
| 193 | |
| 194 | ASTImporterTestBase() |
| 195 | : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {} |
| 196 | ~ASTImporterTestBase(); |
| 197 | }; |
| 198 | |
| 199 | class ASTImporterOptionSpecificTestBase |
| 200 | : public ASTImporterTestBase, |
| 201 | public ::testing::WithParamInterface<std::vector<std::string>> { |
| 202 | protected: |
| 203 | std::vector<std::string> () const override { return GetParam(); } |
| 204 | }; |
| 205 | |
| 206 | // Base class for those tests which use the family of `testImport` functions. |
| 207 | class TestImportBase |
| 208 | : public CompilerOptionSpecificTest, |
| 209 | public ::testing::WithParamInterface<std::vector<std::string>> { |
| 210 | |
| 211 | template <typename NodeType> |
| 212 | llvm::Expected<NodeType> importNode(ASTUnit *From, ASTUnit *To, |
| 213 | ASTImporter &Importer, NodeType Node) { |
| 214 | ASTContext &ToCtx = To->getASTContext(); |
| 215 | |
| 216 | // Add 'From' file to virtual file system so importer can 'find' it |
| 217 | // while importing SourceLocations. It is safe to add same file multiple |
| 218 | // times - it just isn't replaced. |
| 219 | StringRef FromFileName = From->getMainFileName(); |
| 220 | createVirtualFileIfNeeded(ToAST: To, FileName: FromFileName, |
| 221 | Buffer: From->getBufferForFile(Filename: FromFileName)); |
| 222 | |
| 223 | auto Imported = Importer.Import(Node); |
| 224 | |
| 225 | if (Imported) { |
| 226 | // This should dump source locations and assert if some source locations |
| 227 | // were not imported. |
| 228 | SmallString<1024> ImportChecker; |
| 229 | llvm::raw_svector_ostream ToNothing(ImportChecker); |
| 230 | ToCtx.getTranslationUnitDecl()->print(ToNothing); |
| 231 | |
| 232 | // This traverses the AST to catch certain bugs like poorly or not |
| 233 | // implemented subtrees. |
| 234 | (*Imported)->dump(ToNothing); |
| 235 | } |
| 236 | |
| 237 | return Imported; |
| 238 | } |
| 239 | |
| 240 | template <typename NodeType> |
| 241 | testing::AssertionResult |
| 242 | testImport(const std::string &FromCode, |
| 243 | const std::vector<std::string> &FromArgs, |
| 244 | const std::string &ToCode, const std::vector<std::string> &ToArgs, |
| 245 | MatchVerifier<NodeType> &Verifier, |
| 246 | const internal::BindableMatcher<NodeType> &SearchMatcher, |
| 247 | const internal::BindableMatcher<NodeType> &VerificationMatcher) { |
| 248 | const char *const InputFileName = "input.cc" ; |
| 249 | const char *const OutputFileName = "output.cc" ; |
| 250 | |
| 251 | std::unique_ptr<ASTUnit> FromAST = tooling::buildASTFromCodeWithArgs( |
| 252 | Code: FromCode, Args: FromArgs, FileName: InputFileName), |
| 253 | ToAST = tooling::buildASTFromCodeWithArgs( |
| 254 | Code: ToCode, Args: ToArgs, FileName: OutputFileName); |
| 255 | |
| 256 | ASTContext &FromCtx = FromAST->getASTContext(), |
| 257 | &ToCtx = ToAST->getASTContext(); |
| 258 | |
| 259 | ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, |
| 260 | FromAST->getFileManager(), false); |
| 261 | |
| 262 | auto FoundNodes = match(SearchMatcher, FromCtx); |
| 263 | if (FoundNodes.empty()) |
| 264 | return testing::AssertionFailure() << "No node was found!" ; |
| 265 | if (FoundNodes.size() != 1) |
| 266 | return testing::AssertionFailure() |
| 267 | << "Multiple potential nodes were found!" ; |
| 268 | |
| 269 | auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes); |
| 270 | if (!ToImport) |
| 271 | return testing::AssertionFailure() << "Node type mismatch!" ; |
| 272 | |
| 273 | // The node being imported should match in the same way as |
| 274 | // the result node. |
| 275 | internal::BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher); |
| 276 | EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); |
| 277 | |
| 278 | auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); |
| 279 | if (!Imported) { |
| 280 | std::string ErrorText; |
| 281 | handleAllErrors(Imported.takeError(), |
| 282 | [&ErrorText](const ASTImportError &Err) { |
| 283 | ErrorText = Err.message(); |
| 284 | }); |
| 285 | return testing::AssertionFailure() |
| 286 | << "Import failed, error: \"" << ErrorText << "\"!" ; |
| 287 | } |
| 288 | |
| 289 | return Verifier.match(*Imported, WrapperMatcher); |
| 290 | } |
| 291 | |
| 292 | template <typename NodeType> |
| 293 | testing::AssertionResult |
| 294 | testImport(const std::string &FromCode, |
| 295 | const std::vector<std::string> &FromArgs, |
| 296 | const std::string &ToCode, const std::vector<std::string> &ToArgs, |
| 297 | MatchVerifier<NodeType> &Verifier, |
| 298 | const internal::BindableMatcher<NodeType> &VerificationMatcher) { |
| 299 | return testImport( |
| 300 | FromCode, FromArgs, ToCode, ToArgs, Verifier, |
| 301 | translationUnitDecl( |
| 302 | has(namedDecl(hasName(Name: DeclToImportID)).bind(ID: DeclToImportID))), |
| 303 | VerificationMatcher); |
| 304 | } |
| 305 | |
| 306 | protected: |
| 307 | std::vector<std::string> () const override { return GetParam(); } |
| 308 | |
| 309 | public: |
| 310 | /// Test how AST node named "declToImport" located in the translation unit |
| 311 | /// of "FromCode" virtual file is imported to "ToCode" virtual file. |
| 312 | /// The verification is done by running AMatcher over the imported node. |
| 313 | template <typename NodeType, typename MatcherType> |
| 314 | void testImport(const std::string &FromCode, TestLanguage FromLang, |
| 315 | const std::string &ToCode, TestLanguage ToLang, |
| 316 | MatchVerifier<NodeType> &Verifier, |
| 317 | const MatcherType &AMatcher) { |
| 318 | std::vector<std::string> FromArgs = getCommandLineArgsForLanguage(Lang: FromLang); |
| 319 | std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(Lang: ToLang); |
| 320 | EXPECT_TRUE( |
| 321 | testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher)); |
| 322 | } |
| 323 | |
| 324 | struct ImportAction { |
| 325 | StringRef FromFilename; |
| 326 | StringRef ToFilename; |
| 327 | // FIXME: Generalize this to support other node kinds. |
| 328 | internal::BindableMatcher<Decl> ImportPredicate; |
| 329 | |
| 330 | ImportAction(StringRef FromFilename, StringRef ToFilename, |
| 331 | DeclarationMatcher ImportPredicate) |
| 332 | : FromFilename(FromFilename), ToFilename(ToFilename), |
| 333 | ImportPredicate(ImportPredicate) {} |
| 334 | |
| 335 | ImportAction(StringRef FromFilename, StringRef ToFilename, |
| 336 | const std::string &DeclName) |
| 337 | : FromFilename(FromFilename), ToFilename(ToFilename), |
| 338 | ImportPredicate(namedDecl(hasName(Name: DeclName))) {} |
| 339 | }; |
| 340 | |
| 341 | using SingleASTUnit = std::unique_ptr<ASTUnit>; |
| 342 | using AllASTUnits = llvm::StringMap<SingleASTUnit>; |
| 343 | |
| 344 | struct CodeEntry { |
| 345 | std::string CodeSample; |
| 346 | TestLanguage Lang; |
| 347 | }; |
| 348 | |
| 349 | using CodeFiles = llvm::StringMap<CodeEntry>; |
| 350 | |
| 351 | /// Builds an ASTUnit for one potential compile options set. |
| 352 | SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const { |
| 353 | std::vector<std::string> Args = getCommandLineArgsForLanguage(Lang: CE.Lang); |
| 354 | auto AST = tooling::buildASTFromCodeWithArgs(Code: CE.CodeSample, Args, FileName); |
| 355 | EXPECT_TRUE(AST.get()); |
| 356 | return AST; |
| 357 | } |
| 358 | |
| 359 | /// Test an arbitrary sequence of imports for a set of given in-memory files. |
| 360 | /// The verification is done by running VerificationMatcher against a |
| 361 | /// specified AST node inside of one of given files. |
| 362 | /// \param CodeSamples Map whose key is the file name and the value is the |
| 363 | /// file content. |
| 364 | /// \param ImportActions Sequence of imports. Each import in sequence |
| 365 | /// specifies "from file" and "to file" and a matcher that is used for |
| 366 | /// searching a declaration for import in "from file". |
| 367 | /// \param FileForFinalCheck Name of virtual file for which the final check is |
| 368 | /// applied. |
| 369 | /// \param FinalSelectPredicate Matcher that specifies the AST node in the |
| 370 | /// FileForFinalCheck for which the verification will be done. |
| 371 | /// \param VerificationMatcher Matcher that will be used for verification |
| 372 | /// after all imports in sequence are done. |
| 373 | void testImportSequence(const CodeFiles &CodeSamples, |
| 374 | const std::vector<ImportAction> &ImportActions, |
| 375 | StringRef FileForFinalCheck, |
| 376 | internal::BindableMatcher<Decl> FinalSelectPredicate, |
| 377 | internal::BindableMatcher<Decl> VerificationMatcher) { |
| 378 | AllASTUnits AllASTs; |
| 379 | using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>; |
| 380 | llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers; |
| 381 | |
| 382 | auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) { |
| 383 | if (!AllASTs.count(Key: Filename)) { |
| 384 | auto Found = CodeSamples.find(Key: Filename); |
| 385 | assert(Found != CodeSamples.end() && "Wrong file for import!" ); |
| 386 | AllASTs[Filename] = createASTUnit(FileName: Filename, CE: Found->getValue()); |
| 387 | } |
| 388 | }; |
| 389 | |
| 390 | for (const ImportAction &Action : ImportActions) { |
| 391 | StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename; |
| 392 | GenASTsIfNeeded(FromFile); |
| 393 | GenASTsIfNeeded(ToFile); |
| 394 | |
| 395 | ASTUnit *From = AllASTs[FromFile].get(); |
| 396 | ASTUnit *To = AllASTs[ToFile].get(); |
| 397 | |
| 398 | // Create a new importer if needed. |
| 399 | std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}]; |
| 400 | if (!ImporterRef) |
| 401 | ImporterRef.reset(p: new ASTImporter( |
| 402 | To->getASTContext(), To->getFileManager(), From->getASTContext(), |
| 403 | From->getFileManager(), false)); |
| 404 | |
| 405 | // Find the declaration and import it. |
| 406 | auto FoundDecl = match(Matcher: Action.ImportPredicate.bind(ID: DeclToImportID), |
| 407 | Context&: From->getASTContext()); |
| 408 | EXPECT_TRUE(FoundDecl.size() == 1); |
| 409 | const Decl *ToImport = selectFirst<Decl>(BoundTo: DeclToImportID, Results: FoundDecl); |
| 410 | auto Imported = importNode(From, To, Importer&: *ImporterRef, Node: ToImport); |
| 411 | EXPECT_TRUE(static_cast<bool>(Imported)); |
| 412 | if (!Imported) |
| 413 | llvm::consumeError(Err: Imported.takeError()); |
| 414 | } |
| 415 | |
| 416 | // Find the declaration and import it. |
| 417 | auto FoundDecl = match(Matcher: FinalSelectPredicate.bind(ID: DeclToVerifyID), |
| 418 | Context&: AllASTs[FileForFinalCheck]->getASTContext()); |
| 419 | EXPECT_TRUE(FoundDecl.size() == 1); |
| 420 | const Decl *ToVerify = selectFirst<Decl>(BoundTo: DeclToVerifyID, Results: FoundDecl); |
| 421 | MatchVerifier<Decl> Verifier; |
| 422 | EXPECT_TRUE(Verifier.match( |
| 423 | ToVerify, internal::BindableMatcher<Decl>(VerificationMatcher))); |
| 424 | } |
| 425 | }; |
| 426 | |
| 427 | template <typename T> RecordDecl *getRecordDecl(T *D) { |
| 428 | auto *ET = cast<ElaboratedType>(D->getType().getTypePtr()); |
| 429 | return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl(); |
| 430 | } |
| 431 | |
| 432 | template <class T> |
| 433 | ::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) { |
| 434 | if (ValOrErr) |
| 435 | return ::testing::AssertionSuccess() << "Expected<> contains no error." ; |
| 436 | else |
| 437 | return ::testing::AssertionFailure() |
| 438 | << "Expected<> contains error: " << toString(ValOrErr.takeError()); |
| 439 | } |
| 440 | |
| 441 | template <class T> |
| 442 | ::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr, |
| 443 | ASTImportError::ErrorKind Kind) { |
| 444 | if (ValOrErr) { |
| 445 | return ::testing::AssertionFailure() << "Expected<> is expected to contain " |
| 446 | "error but does contain value \"" |
| 447 | << (*ValOrErr) << "\"" ; |
| 448 | } else { |
| 449 | std::ostringstream OS; |
| 450 | bool Result = false; |
| 451 | auto Err = llvm::handleErrors( |
| 452 | ValOrErr.takeError(), [&OS, &Result, Kind](clang::ASTImportError &IE) { |
| 453 | if (IE.Error == Kind) { |
| 454 | Result = true; |
| 455 | OS << "Expected<> contains an ImportError " << IE.toString(); |
| 456 | } else { |
| 457 | OS << "Expected<> contains an ImportError " << IE.toString() |
| 458 | << " instead of kind " << Kind; |
| 459 | } |
| 460 | }); |
| 461 | if (Err) { |
| 462 | OS << "Expected<> contains unexpected error: " |
| 463 | << toString(std::move(Err)); |
| 464 | } |
| 465 | if (Result) |
| 466 | return ::testing::AssertionSuccess() << OS.str(); |
| 467 | else |
| 468 | return ::testing::AssertionFailure() << OS.str(); |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | } // end namespace ast_matchers |
| 473 | } // end namespace clang |
| 474 | |
| 475 | #endif |
| 476 | |