Index: lib/AST/ASTImporter.cpp =================================================================== --- lib/AST/ASTImporter.cpp +++ lib/AST/ASTImporter.cpp @@ -1066,8 +1066,26 @@ DeclarationName &Name, NamedDecl *&ToD, SourceLocation &Loc) { + // Check if RecordDecl is in FunctionDecl parameters to avoid infinite loop. + // example: int struct_in_proto(struct data_t{int a;int b;} *d); + DeclContext *OrigDC = D->getDeclContext(); + FunctionDecl *FunDecl; + if (isa(D) && (FunDecl = dyn_cast(OrigDC)) && + FunDecl->hasBody()) { + SourceRange RecR = D->getSourceRange(); + SourceRange BodyR = FunDecl->getBody()->getSourceRange(); + // If RecordDecl is not in Body (it is a param), we bail out. + if (RecR.isValid() && BodyR.isValid() && + (RecR.getBegin() < BodyR.getBegin() || + BodyR.getEnd() < RecR.getEnd())) { + Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node) + << D->getDeclKindName(); + return true; + } + } + // Import the context of this declaration. - DC = Importer.ImportContext(D->getDeclContext()); + DC = Importer.ImportContext(OrigDC); if (!DC) return true; Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -284,12 +284,16 @@ // Buffer for the To context, must live in the test scope. std::string ToCode; + // Represents a "From" translation unit and holds an importer object which we + // use to import from this translation unit. struct TU { // Buffer for the context, must live in the test scope. std::string Code; std::string FileName; std::unique_ptr Unit; TranslationUnitDecl *TUDecl = nullptr; + std::unique_ptr Importer; + TU(StringRef Code, StringRef FileName, ArgVector Args) : Code(Code), FileName(FileName), Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, @@ -297,6 +301,27 @@ TUDecl(Unit->getASTContext().getTranslationUnitDecl()) { Unit->enableSourceFileDiagnostics(); } + + void lazyInitImporter(ASTUnit *ToAST) { + assert(ToAST); + if (!Importer) { + Importer.reset(new ASTImporter( + ToAST->getASTContext(), ToAST->getFileManager(), + Unit->getASTContext(), Unit->getFileManager(), false)); + } + assert(&ToAST->getASTContext() == &Importer->getToContext()); + createVirtualFileIfNeeded(ToAST, FileName, Code); + } + + Decl *import(ASTUnit *ToAST, Decl *FromDecl) { + lazyInitImporter(ToAST); + return Importer->Import(FromDecl); + } + + QualType import(ASTUnit *ToAST, QualType FromType) { + lazyInitImporter(ToAST); + return Importer->Import(FromType); + } }; // We may have several From contexts and related translation units. In each @@ -308,6 +333,26 @@ // vector is expanding, with the list we won't have these issues. std::list FromTUs; + void lazyInitToAST(Language ToLang) { + if (ToAST) + return; + ArgVector ToArgs = getArgVectorForLanguage(ToLang); + // Build the AST from an empty file. + ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc"); + ToAST->enableSourceFileDiagnostics(); + } + + TU *findFromTU(Decl *From) { + // Create a virtual file in the To Ctx which corresponds to the file from + // which we want to import the `From` Decl. Without this source locations + // will be invalid in the ToCtx. + auto It = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) { + return E.TUDecl == From->getTranslationUnitDecl(); + }); + assert(It != FromTUs.end()); + return &*It; + } + public: // We may have several From context but only one To context. std::unique_ptr ToAST; @@ -329,14 +374,10 @@ ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName); ToAST->enableSourceFileDiagnostics(); - ASTContext &FromCtx = FromTU.Unit->getASTContext(), - &ToCtx = ToAST->getASTContext(); + ASTContext &FromCtx = FromTU.Unit->getASTContext(); createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code); - ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, - FromTU.Unit->getFileManager(), false); - IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier); assert(ImportedII && "Declaration with the given identifier " "should be specified in test!"); @@ -347,7 +388,7 @@ assert(FoundDecls.size() == 1); - Decl *Imported = Importer.Import(FoundDecls.front()); + Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front()); assert(Imported); return std::make_tuple(*FoundDecls.begin(), Imported); } @@ -384,30 +425,17 @@ // May be called several times in a given test. // The different instances of the param From may have different ASTContext. Decl *Import(Decl *From, Language ToLang) { - if (!ToAST) { - ArgVector ToArgs = getArgVectorForLanguage(ToLang); - // Build the AST from an empty file. - ToAST = - tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc"); - ToAST->enableSourceFileDiagnostics(); - } - - // Create a virtual file in the To Ctx which corresponds to the file from - // which we want to import the `From` Decl. Without this source locations - // will be invalid in the ToCtx. - auto It = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) { - return E.TUDecl == From->getTranslationUnitDecl(); - }); - assert(It != FromTUs.end()); - createVirtualFileIfNeeded(ToAST.get(), It->FileName, It->Code); - - ASTContext &FromCtx = From->getASTContext(), - &ToCtx = ToAST->getASTContext(); - ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, - FromCtx.getSourceManager().getFileManager(), false); - return Importer.Import(From); + lazyInitToAST(ToLang); + TU *FromTU = findFromTU(From); + return FromTU->import(ToAST.get(), From); } + QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) { + lazyInitToAST(ToLang); + TU *FromTU = findFromTU(TUDecl); + return FromTU->import(ToAST.get(), FromType); + } + ~ASTImporterTestBase() { if (!::testing::Test::HasFailure()) return; @@ -937,6 +965,44 @@ typeTraitExpr(hasType(booleanType()))))))))); } +TEST_P(ImportDecl, ImportRecordDeclInFunc) { + MatchVerifier Verifier; + testImport("int declToImport() { " + " struct data_t {int a;int b;};" + " struct data_t d;" + " return 0;" + "}", + Lang_C, "", Lang_C, Verifier, + functionDecl(hasBody(compoundStmt( + has(declStmt(hasSingleDecl(varDecl(hasName("d"))))))))); +} + +TEST_P(ASTImporterTestBase, ImportRecordTypeInFunc) { + Decl *FromTU = getTuDecl("int declToImport() { " + " struct data_t {int a;int b;};" + " struct data_t d;" + " return 0;" + "}", + Lang_C, "input.c"); + auto FromVar = + FirstDeclMatcher().match(FromTU, varDecl(hasName("d"))); + ASSERT_TRUE(FromVar); + auto ToType = + ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C); + EXPECT_FALSE(ToType.isNull()); +} + +TEST_P(ASTImporterTestBase, ImportRecordDeclInFuncParams) { + // This construct is not supported by ASTImporter. + Decl *FromTU = + getTuDecl("int declToImport(struct data_t{int a;int b;} *d){ return 0; }", + Lang_C, "input.c"); + auto From = FirstDeclMatcher().match(FromTU, functionDecl()); + ASSERT_TRUE(From); + auto To = Import(From, Lang_C); + EXPECT_EQ(To, nullptr); +} + const internal::VariadicDynCastAllOfMatcher cxxPseudoDestructorExpr;