|
24 | 24 | #include "llvm/Support/ErrorHandling.h"
|
25 | 25 |
|
26 | 26 | #include "DeclMatcher.h"
|
| 27 | +#include "MatchVerifier.h" |
27 | 28 |
|
28 | 29 | #include <sstream>
|
29 | 30 |
|
@@ -202,6 +203,229 @@ class ASTImporterOptionSpecificTestBase
|
202 | 203 | std::vector<std::string> getExtraArgs() const override { return GetParam(); }
|
203 | 204 | };
|
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(To, FromFileName, |
| 221 | + From->getBufferForFile(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 | + FromCode, FromArgs, InputFileName), |
| 253 | + ToAST = tooling::buildASTFromCodeWithArgs( |
| 254 | + ToCode, ToArgs, 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.size() != 1) |
| 264 | + return testing::AssertionFailure() |
| 265 | + << "Multiple potential nodes were found!"; |
| 266 | + |
| 267 | + auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes); |
| 268 | + if (!ToImport) |
| 269 | + return testing::AssertionFailure() << "Node type mismatch!"; |
| 270 | + |
| 271 | + // Sanity check: the node being imported should match in the same way as |
| 272 | + // the result node. |
| 273 | + internal::BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher); |
| 274 | + EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); |
| 275 | + |
| 276 | + auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); |
| 277 | + if (!Imported) { |
| 278 | + std::string ErrorText; |
| 279 | + handleAllErrors( |
| 280 | + Imported.takeError(), |
| 281 | + [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); }); |
| 282 | + return testing::AssertionFailure() |
| 283 | + << "Import failed, error: \"" << ErrorText << "\"!"; |
| 284 | + } |
| 285 | + |
| 286 | + return Verifier.match(*Imported, WrapperMatcher); |
| 287 | + } |
| 288 | + |
| 289 | + template <typename NodeType> |
| 290 | + testing::AssertionResult |
| 291 | + testImport(const std::string &FromCode, |
| 292 | + const std::vector<std::string> &FromArgs, |
| 293 | + const std::string &ToCode, const std::vector<std::string> &ToArgs, |
| 294 | + MatchVerifier<NodeType> &Verifier, |
| 295 | + const internal::BindableMatcher<NodeType> &VerificationMatcher) { |
| 296 | + return testImport( |
| 297 | + FromCode, FromArgs, ToCode, ToArgs, Verifier, |
| 298 | + translationUnitDecl( |
| 299 | + has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))), |
| 300 | + VerificationMatcher); |
| 301 | + } |
| 302 | + |
| 303 | +protected: |
| 304 | + std::vector<std::string> getExtraArgs() const override { return GetParam(); } |
| 305 | + |
| 306 | +public: |
| 307 | + /// Test how AST node named "declToImport" located in the translation unit |
| 308 | + /// of "FromCode" virtual file is imported to "ToCode" virtual file. |
| 309 | + /// The verification is done by running AMatcher over the imported node. |
| 310 | + template <typename NodeType, typename MatcherType> |
| 311 | + void testImport(const std::string &FromCode, TestLanguage FromLang, |
| 312 | + const std::string &ToCode, TestLanguage ToLang, |
| 313 | + MatchVerifier<NodeType> &Verifier, |
| 314 | + const MatcherType &AMatcher) { |
| 315 | + std::vector<std::string> FromArgs = getCommandLineArgsForLanguage(FromLang); |
| 316 | + std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang); |
| 317 | + EXPECT_TRUE( |
| 318 | + testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher)); |
| 319 | + } |
| 320 | + |
| 321 | + struct ImportAction { |
| 322 | + StringRef FromFilename; |
| 323 | + StringRef ToFilename; |
| 324 | + // FIXME: Generalize this to support other node kinds. |
| 325 | + internal::BindableMatcher<Decl> ImportPredicate; |
| 326 | + |
| 327 | + ImportAction(StringRef FromFilename, StringRef ToFilename, |
| 328 | + DeclarationMatcher ImportPredicate) |
| 329 | + : FromFilename(FromFilename), ToFilename(ToFilename), |
| 330 | + ImportPredicate(ImportPredicate) {} |
| 331 | + |
| 332 | + ImportAction(StringRef FromFilename, StringRef ToFilename, |
| 333 | + const std::string &DeclName) |
| 334 | + : FromFilename(FromFilename), ToFilename(ToFilename), |
| 335 | + ImportPredicate(namedDecl(hasName(DeclName))) {} |
| 336 | + }; |
| 337 | + |
| 338 | + using SingleASTUnit = std::unique_ptr<ASTUnit>; |
| 339 | + using AllASTUnits = llvm::StringMap<SingleASTUnit>; |
| 340 | + |
| 341 | + struct CodeEntry { |
| 342 | + std::string CodeSample; |
| 343 | + TestLanguage Lang; |
| 344 | + }; |
| 345 | + |
| 346 | + using CodeFiles = llvm::StringMap<CodeEntry>; |
| 347 | + |
| 348 | + /// Builds an ASTUnit for one potential compile options set. |
| 349 | + SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const { |
| 350 | + std::vector<std::string> Args = getCommandLineArgsForLanguage(CE.Lang); |
| 351 | + auto AST = tooling::buildASTFromCodeWithArgs(CE.CodeSample, Args, FileName); |
| 352 | + EXPECT_TRUE(AST.get()); |
| 353 | + return AST; |
| 354 | + } |
| 355 | + |
| 356 | + /// Test an arbitrary sequence of imports for a set of given in-memory files. |
| 357 | + /// The verification is done by running VerificationMatcher against a |
| 358 | + /// specified AST node inside of one of given files. |
| 359 | + /// \param CodeSamples Map whose key is the file name and the value is the |
| 360 | + /// file content. |
| 361 | + /// \param ImportActions Sequence of imports. Each import in sequence |
| 362 | + /// specifies "from file" and "to file" and a matcher that is used for |
| 363 | + /// searching a declaration for import in "from file". |
| 364 | + /// \param FileForFinalCheck Name of virtual file for which the final check is |
| 365 | + /// applied. |
| 366 | + /// \param FinalSelectPredicate Matcher that specifies the AST node in the |
| 367 | + /// FileForFinalCheck for which the verification will be done. |
| 368 | + /// \param VerificationMatcher Matcher that will be used for verification |
| 369 | + /// after all imports in sequence are done. |
| 370 | + void testImportSequence(const CodeFiles &CodeSamples, |
| 371 | + const std::vector<ImportAction> &ImportActions, |
| 372 | + StringRef FileForFinalCheck, |
| 373 | + internal::BindableMatcher<Decl> FinalSelectPredicate, |
| 374 | + internal::BindableMatcher<Decl> VerificationMatcher) { |
| 375 | + AllASTUnits AllASTs; |
| 376 | + using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>; |
| 377 | + llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers; |
| 378 | + |
| 379 | + auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) { |
| 380 | + if (!AllASTs.count(Filename)) { |
| 381 | + auto Found = CodeSamples.find(Filename); |
| 382 | + assert(Found != CodeSamples.end() && "Wrong file for import!"); |
| 383 | + AllASTs[Filename] = createASTUnit(Filename, Found->getValue()); |
| 384 | + } |
| 385 | + }; |
| 386 | + |
| 387 | + for (const ImportAction &Action : ImportActions) { |
| 388 | + StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename; |
| 389 | + GenASTsIfNeeded(FromFile); |
| 390 | + GenASTsIfNeeded(ToFile); |
| 391 | + |
| 392 | + ASTUnit *From = AllASTs[FromFile].get(); |
| 393 | + ASTUnit *To = AllASTs[ToFile].get(); |
| 394 | + |
| 395 | + // Create a new importer if needed. |
| 396 | + std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}]; |
| 397 | + if (!ImporterRef) |
| 398 | + ImporterRef.reset(new ASTImporter( |
| 399 | + To->getASTContext(), To->getFileManager(), From->getASTContext(), |
| 400 | + From->getFileManager(), false)); |
| 401 | + |
| 402 | + // Find the declaration and import it. |
| 403 | + auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID), |
| 404 | + From->getASTContext()); |
| 405 | + EXPECT_TRUE(FoundDecl.size() == 1); |
| 406 | + const Decl *ToImport = selectFirst<Decl>(DeclToImportID, FoundDecl); |
| 407 | + auto Imported = importNode(From, To, *ImporterRef, ToImport); |
| 408 | + EXPECT_TRUE(static_cast<bool>(Imported)); |
| 409 | + if (!Imported) |
| 410 | + llvm::consumeError(Imported.takeError()); |
| 411 | + } |
| 412 | + |
| 413 | + // Find the declaration and import it. |
| 414 | + auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID), |
| 415 | + AllASTs[FileForFinalCheck]->getASTContext()); |
| 416 | + EXPECT_TRUE(FoundDecl.size() == 1); |
| 417 | + const Decl *ToVerify = selectFirst<Decl>(DeclToVerifyID, FoundDecl); |
| 418 | + MatchVerifier<Decl> Verifier; |
| 419 | + EXPECT_TRUE(Verifier.match( |
| 420 | + ToVerify, internal::BindableMatcher<Decl>(VerificationMatcher))); |
| 421 | + } |
| 422 | +}; |
| 423 | + |
| 424 | +template <typename T> RecordDecl *getRecordDecl(T *D) { |
| 425 | + auto *ET = cast<ElaboratedType>(D->getType().getTypePtr()); |
| 426 | + return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl(); |
| 427 | +} |
| 428 | + |
205 | 429 | template <class T>
|
206 | 430 | ::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) {
|
207 | 431 | if (ValOrErr)
|
|
0 commit comments