[clang] def7c7f - [ASTImporter] Fix handling of not defined FromRecord in ImportContext(...)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 23 15:19:23 PDT 2020
Author: shafik
Date: 2020-04-23T15:18:48-07:00
New Revision: def7c7f6020530ef2deb64f786d28775b5d0e47f
URL: https://github.com/llvm/llvm-project/commit/def7c7f6020530ef2deb64f786d28775b5d0e47f
DIFF: https://github.com/llvm/llvm-project/commit/def7c7f6020530ef2deb64f786d28775b5d0e47f.diff
LOG: [ASTImporter] Fix handling of not defined FromRecord in ImportContext(...)
In ImportContext(…) we may call into CompleteDecl(…) which if FromRecrord is not
defined will start the definition of a ToRecord but from what I can tell at least
one of the paths though here don't ensure we complete the definition.
For a RecordDecl this can be problematic since this means we won’t import base
classes and we won’t have any of the methods or types we inherit from these bases.
Differential Revision: https://reviews.llvm.org/D78000
Added:
lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/TestImportBaseClassWhenClassHasDerivedMember.py
lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/main.cpp
Modified:
clang/lib/AST/ASTImporter.cpp
clang/unittests/AST/ASTImporterTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index afd35e0137b6..ecb5ce85c204 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8176,15 +8176,22 @@ Expected<DeclContext *> ASTImporter::ImportContext(DeclContext *FromDC) {
// need it to have a definition.
if (auto *ToRecord = dyn_cast<RecordDecl>(ToDC)) {
auto *FromRecord = cast<RecordDecl>(FromDC);
- if (ToRecord->isCompleteDefinition()) {
- // Do nothing.
- } else if (FromRecord->isCompleteDefinition()) {
+ if (ToRecord->isCompleteDefinition())
+ return ToDC;
+
+ // If FromRecord is not defined we need to force it to be.
+ // Simply calling CompleteDecl(...) for a RecordDecl will break some cases
+ // it will start the definition but we never finish it.
+ // If there are base classes they won't be imported and we will
+ // be missing anything that we inherit from those bases.
+ if (FromRecord->getASTContext().getExternalSource() &&
+ !FromRecord->isCompleteDefinition())
+ FromRecord->getASTContext().getExternalSource()->CompleteType(FromRecord);
+
+ if (FromRecord->isCompleteDefinition())
if (Error Err = ASTNodeImporter(*this).ImportDefinition(
FromRecord, ToRecord, ASTNodeImporter::IDK_Basic))
return std::move(Err);
- } else {
- CompleteDecl(ToRecord);
- }
} else if (auto *ToEnum = dyn_cast<EnumDecl>(ToDC)) {
auto *FromEnum = cast<EnumDecl>(FromDC);
if (ToEnum->isCompleteDefinition()) {
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 9f35a86e2937..8ed6c487cb53 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -5942,6 +5942,70 @@ auto ExtendWithOptions(const T &Values, const ArgVector &Args) {
return ::testing::ValuesIn(Copy);
}
+struct ImportWithExternalSource : ASTImporterOptionSpecificTestBase {
+ ImportWithExternalSource() {
+ Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
+ ASTContext &FromContext, FileManager &FromFileManager,
+ bool MinimalImport,
+ const std::shared_ptr<ASTImporterSharedState> &SharedState) {
+ return new ASTImporter(ToContext, ToFileManager, FromContext,
+ FromFileManager, MinimalImport,
+ // We use the regular lookup.
+ /*SharedState=*/nullptr);
+ };
+ }
+};
+
+/// An ExternalASTSource that keeps track of the tags is completed.
+struct SourceWithCompletedTagList : clang::ExternalASTSource {
+ std::vector<clang::TagDecl *> &CompletedTags;
+ SourceWithCompletedTagList(std::vector<clang::TagDecl *> &CompletedTags)
+ : CompletedTags(CompletedTags) {}
+ void CompleteType(TagDecl *Tag) override {
+ auto *Record = cast<CXXRecordDecl>(Tag);
+ Record->startDefinition();
+ Record->completeDefinition();
+ CompletedTags.push_back(Tag);
+ }
+ void
+ FindExternalLexicalDecls(const DeclContext *DC,
+ llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
+ SmallVectorImpl<Decl *> &Result) override {}
+};
+
+TEST_P(ImportWithExternalSource, CompleteRecordBeforeImporting) {
+ // Create an empty TU.
+ TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX, "input.cpp");
+
+ // Create and add the test ExternalASTSource.
+ std::vector<clang::TagDecl *> CompletedTags;
+ IntrusiveRefCntPtr<ExternalASTSource> source =
+ new SourceWithCompletedTagList(CompletedTags);
+ clang::ASTContext &Context = FromTU->getASTContext();
+ Context.setExternalSource(std::move(source));
+
+ // Create a dummy class by hand with external lexical storage.
+ IdentifierInfo &Ident = Context.Idents.get("test_class");
+ auto *Record = CXXRecordDecl::Create(
+ Context, TTK_Class, FromTU, SourceLocation(), SourceLocation(), &Ident);
+ Record->setHasExternalLexicalStorage();
+ FromTU->addDecl(Record);
+
+ // Do a minimal import of the created class.
+ EXPECT_EQ(0U, CompletedTags.size());
+ Import(Record, Lang_CXX);
+ EXPECT_EQ(0U, CompletedTags.size());
+
+ // Import the definition of the created class.
+ llvm::Error Err = findFromTU(Record)->Importer->ImportDefinition(Record);
+ EXPECT_FALSE((bool)Err);
+ consumeError(std::move(Err));
+
+ // Make sure the class was completed once.
+ EXPECT_EQ(1U, CompletedTags.size());
+ EXPECT_EQ(Record, CompletedTags.front());
+}
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
DefaultTestValuesForRunOptions, );
@@ -6007,5 +6071,8 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest,
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportSourceLocations,
DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportWithExternalSource,
+ DefaultTestValuesForRunOptions, );
+
} // end namespace ast_matchers
} // end namespace clang
diff --git a/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/TestImportBaseClassWhenClassHasDerivedMember.py b/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/TestImportBaseClassWhenClassHasDerivedMember.py
new file mode 100644
index 000000000000..c8308c16011e
--- /dev/null
+++ b/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/TestImportBaseClassWhenClassHasDerivedMember.py
@@ -0,0 +1,4 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(__file__, globals())
diff --git a/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/main.cpp b/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/main.cpp
new file mode 100644
index 000000000000..0c146c27ab97
--- /dev/null
+++ b/lldb/test/API/commands/expression/import_base_class_when_class_has_derived_member/main.cpp
@@ -0,0 +1,35 @@
+struct B {
+ int dump() const;
+};
+
+int B::dump() const { return 42; }
+
+// Derived class D obtains dump() method from base class B
+struct D : public B {
+ // Introduce a TypedefNameDecl
+ using Iterator = D *;
+};
+
+struct C {
+ // This will cause use to invoke VisitTypedefNameDecl(...) when Importing
+ // the DeclContext of C.
+ // We will invoke ImportContext(...) which should force the From Decl to
+ // be defined if it not already defined. We will then Import the definition
+ // to the To Decl.
+ // This will force processing of the base class of D which allows us to see
+ // base class methods such as dump().
+ D::Iterator iter;
+
+ bool f(D *DD) {
+ return true; //%self.expect_expr("DD->dump()", result_type="int", result_value="42")
+ }
+};
+
+int main() {
+ C c;
+ D d;
+
+ c.f(&d);
+
+ return 0;
+}
More information about the cfe-commits
mailing list