spyffe created this revision. Herald added a subscriber: jgosnell. clang-import-test has until now been only able to report top-level entities. This is clearly insufficient; we should be able to look inside structs and namespaces also. This patch adds new test cases for a variety of lookups inside existing entities, and adds the functionality necessary to make most of these testcases work. One testcase is known to fail because of ASTImporter limitations when importing templates; this will be addressed separately.
Repository: rL LLVM https://reviews.llvm.org/D30435 Files: test/Import/forward-declared-struct/Inputs/S1.c test/Import/forward-declared-struct/Inputs/S2.c test/Import/forward-declared-struct/test.c test/Import/member-in-struct/Inputs/S.c test/Import/member-in-struct/test.c test/Import/multiple-forward-declarations/Inputs/S1.c test/Import/multiple-forward-declarations/Inputs/S2.c test/Import/multiple-forward-declarations/test.c test/Import/overloaded-function/Inputs/F1.c test/Import/overloaded-function/Inputs/F2.c test/Import/overloaded-function/test.c test/Import/struct-in-namespace/Inputs/N1.cpp test/Import/struct-in-namespace/Inputs/N2.cpp test/Import/struct-in-namespace/test.cpp test/Import/template-specialization/Inputs/T.cpp test/Import/template-specialization/test.cpp tools/clang-import-test/clang-import-test.cpp
Index: tools/clang-import-test/clang-import-test.cpp =================================================================== --- tools/clang-import-test/clang-import-test.cpp +++ tools/clang-import-test/clang-import-test.cpp @@ -9,6 +9,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclObjC.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" @@ -189,19 +190,80 @@ } // end namespace namespace { +class TestASTImporter : public ASTImporter { +public: + TestASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager, + bool MinimalImport) + : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, + MinimalImport) {} + Decl *Imported(Decl *From, Decl *To) override { + if (auto ToTag = llvm::dyn_cast<TagDecl>(To)) { + ToTag->setHasExternalLexicalStorage(); + } else if (auto ToNamespace = llvm::dyn_cast<NamespaceDecl>(To)) { + ToNamespace->setHasExternalVisibleStorage(); + } + return ASTImporter::Imported(From, To); + } +}; + +const DeclContext *LookupSameContext(const TranslationUnitDecl *SearchTU, + const DeclContext *SourceDC, + ASTImporter &SourceToSearchImporter) { + if (SourceDC->isTranslationUnit()) { + return SearchTU; + } + const DeclContext *SearchParentDC = + LookupSameContext(SearchTU, SourceDC->getParent(), SourceToSearchImporter); + if (!SearchParentDC) { + return nullptr; + } + if (auto SourceNamedDecl = dyn_cast<NamedDecl>(SourceDC)) { + DeclarationName SourceName = SourceNamedDecl->getDeclName(); + DeclarationName SearchName = SourceToSearchImporter.Import(SourceName); + DeclContext::lookup_result SearchResult = + SearchParentDC->lookup(SearchName); + size_t SearchResultSize = SearchResult.size(); + if (SearchResultSize == 0) { + return nullptr; // couldn't find the name, so we have to give up + } else if (SearchResultSize > 1) { + return nullptr; // unhandled case; here we need to perform overload + // resolution + } else { + NamedDecl *SearchResultDecl = SearchResult[0]; + return dyn_cast<DeclContext>(SearchResultDecl); + } + } else { + return nullptr; // unhandled case; here we need to handle e.g. + // NamespaceDecls + } +} + +bool IsForwardDeclaration(Decl *D) { + assert(!isa<ObjCInterfaceDecl>(D)); // TODO handle this case + if (auto TD = dyn_cast<TagDecl>(D)) { + return !TD->isThisDeclarationADefinition(); + } else if (auto FD = dyn_cast<FunctionDecl>(D)) { + return !FD->isThisDeclarationADefinition(); + } else { + return false; + } +} + class TestExternalASTSource : public ExternalASTSource { private: llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs; - std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters; + std::map<CompilerInstance *, std::unique_ptr<TestASTImporter>> + ForwardImporters; std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters; public: TestExternalASTSource( CompilerInstance &ExpressionCI, llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs) : ImportCIs(ImportCIs) { for (const std::unique_ptr<CompilerInstance> &ImportCI : ImportCIs) { - ForwardImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>( + ForwardImporters[ImportCI.get()] = llvm::make_unique<TestASTImporter>( ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), ImportCI->getASTContext(), ImportCI->getFileManager(), /*MinimalImport=*/true); @@ -212,29 +274,88 @@ } } + void ForEachMatchingDC(const DeclContext *DC, + std::function<void(ASTImporter &ForwardImporter, + ASTImporter &ReverseImporter, + const DeclContext *SearchDC)> + Callback) { + for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) { + TranslationUnitDecl *SearchTU = + I->getASTContext().getTranslationUnitDecl(); + ASTImporter &ForwardImporter = *ForwardImporters[I.get()]; + ASTImporter &ReverseImporter = *ReverseImporters[I.get()]; + const DeclContext *SearchDC = + LookupSameContext(SearchTU, DC, ReverseImporter); + if (SearchDC) { + Callback(ForwardImporter, ReverseImporter, SearchDC); + } + } + } + bool FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name) override { llvm::SmallVector<NamedDecl *, 1> Decls; - if (isa<TranslationUnitDecl>(DC)) { - for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) { - DeclarationName FromName = ReverseImporters[I.get()]->Import(Name); - DeclContextLookupResult Result = - I->getASTContext().getTranslationUnitDecl()->lookup(FromName); - for (NamedDecl *FromD : Result) { - NamedDecl *D = - llvm::cast<NamedDecl>(ForwardImporters[I.get()]->Import(FromD)); - Decls.push_back(D); + typedef std::pair<NamedDecl *, ASTImporter *> Candidate; + + std::vector<Candidate> CompleteDecls; + std::vector<Candidate> ForwardDecls; + + auto FilterFoundDecl = [&CompleteDecls, &ForwardDecls](const Candidate &C) { + if (IsForwardDeclaration(C.first)) { + if (std::none_of(ForwardDecls.begin(), ForwardDecls.end(), + [&C](Candidate &D) { + return C.first->getKind() == D.first->getKind(); + })) { + ForwardDecls.push_back(C); } + } else { + CompleteDecls.push_back(C); } - } - if (Decls.empty()) { + }; + + ForEachMatchingDC( + DC, [Name, &FilterFoundDecl](ASTImporter &ForwardImporter, + ASTImporter &ReverseImporter, + const DeclContext *SearchDC) { + DeclarationName FromName = ReverseImporter.Import(Name); + DeclContextLookupResult Result = SearchDC->lookup(FromName); + for (NamedDecl *FromD : Result) { + FilterFoundDecl(std::make_pair(FromD, &ForwardImporter)); + } + }); + + std::vector<Candidate> &DeclsToReport = + CompleteDecls.empty() ? ForwardDecls : CompleteDecls; + + if (DeclsToReport.empty()) { return false; - } else { - SetExternalVisibleDeclsForName(DC, Name, Decls); - return true; } + + Decls.resize(DeclsToReport.size()); + std::transform(DeclsToReport.begin(), DeclsToReport.end(), Decls.begin(), + [](Candidate &C) { + return llvm::cast<NamedDecl>(C.second->Import(C.first)); + }); + SetExternalVisibleDeclsForName(DC, Name, Decls); + return true; } + + void + FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref<bool(Decl::Kind)> IsKindWeWant, + SmallVectorImpl<Decl *> &Result) override { + ForEachMatchingDC(DC, [DC, IsKindWeWant](ASTImporter &ForwardImporter, + ASTImporter &ReverseImporter, + const DeclContext *SearchDC) { + for (Decl *SearchDecl : SearchDC->decls()) { + if (IsKindWeWant(SearchDecl->getKind())) { + Decl *ImportedDecl = ForwardImporter.Import(SearchDecl); + assert(ImportedDecl->getDeclContext() == DC); + } + } + }); + } }; void AddExternalSource( Index: test/Import/template-specialization/test.cpp =================================================================== --- test/Import/template-specialization/test.cpp +++ test/Import/template-specialization/test.cpp @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/T.cpp -expression %s +// XFAIL: * +void expr() { + A<int>::B b1; + A<bool>::B b2; + b1.f + b2.g; +} Index: test/Import/template-specialization/Inputs/T.cpp =================================================================== --- test/Import/template-specialization/Inputs/T.cpp +++ test/Import/template-specialization/Inputs/T.cpp @@ -0,0 +1,14 @@ +template <typename T> struct A { +}; + +template <> struct A<int> { + struct B { + int f; + }; +}; + +template <> struct A<bool> { + struct B { + int g; + }; +}; \ No newline at end of file Index: test/Import/struct-in-namespace/test.cpp =================================================================== --- test/Import/struct-in-namespace/test.cpp +++ test/Import/struct-in-namespace/test.cpp @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/N1.cpp -import %S/Inputs/N2.cpp -expression %s +void expr() { + N::S s; + N::T t; + N::U u; + int d = s.a + t.b + u.c; +} Index: test/Import/struct-in-namespace/Inputs/N2.cpp =================================================================== --- test/Import/struct-in-namespace/Inputs/N2.cpp +++ test/Import/struct-in-namespace/Inputs/N2.cpp @@ -0,0 +1,5 @@ +namespace N { + struct U { + int c; + }; +} \ No newline at end of file Index: test/Import/struct-in-namespace/Inputs/N1.cpp =================================================================== --- test/Import/struct-in-namespace/Inputs/N1.cpp +++ test/Import/struct-in-namespace/Inputs/N1.cpp @@ -0,0 +1,11 @@ +namespace N { + struct S { + int a; + }; +} + +namespace N { + struct T { + int b; + }; +} \ No newline at end of file Index: test/Import/overloaded-function/test.c =================================================================== --- test/Import/overloaded-function/test.c +++ test/Import/overloaded-function/test.c @@ -0,0 +1,7 @@ +// RUN: clang-import-test -import %S/Inputs/F1.c -import %S/Inputs/F2.c -expression %s +void expr() { + f(2); + f("world"); + S s; + f(s); +} Index: test/Import/overloaded-function/Inputs/F2.c =================================================================== --- test/Import/overloaded-function/Inputs/F2.c +++ test/Import/overloaded-function/Inputs/F2.c @@ -0,0 +1,4 @@ +struct S { int a; }; + +void f(const char *arg) { } +void f(S arg) { } \ No newline at end of file Index: test/Import/overloaded-function/Inputs/F1.c =================================================================== --- test/Import/overloaded-function/Inputs/F1.c +++ test/Import/overloaded-function/Inputs/F1.c @@ -0,0 +1 @@ +void f(int arg) { } \ No newline at end of file Index: test/Import/multiple-forward-declarations/test.c =================================================================== --- test/Import/multiple-forward-declarations/test.c +++ test/Import/multiple-forward-declarations/test.c @@ -0,0 +1,4 @@ +// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s +void expr() { + struct S *MySPtr; +} Index: test/Import/multiple-forward-declarations/Inputs/S2.c =================================================================== --- test/Import/multiple-forward-declarations/Inputs/S2.c +++ test/Import/multiple-forward-declarations/Inputs/S2.c @@ -0,0 +1 @@ +struct S; Index: test/Import/multiple-forward-declarations/Inputs/S1.c =================================================================== --- test/Import/multiple-forward-declarations/Inputs/S1.c +++ test/Import/multiple-forward-declarations/Inputs/S1.c @@ -0,0 +1 @@ +struct S; Index: test/Import/member-in-struct/test.c =================================================================== --- test/Import/member-in-struct/test.c +++ test/Import/member-in-struct/test.c @@ -0,0 +1,5 @@ +// RUN: clang-import-test -import %S/Inputs/S.c -expression %s +void expr() { + struct S MyS; + MyS.a = 3; +} Index: test/Import/member-in-struct/Inputs/S.c =================================================================== --- test/Import/member-in-struct/Inputs/S.c +++ test/Import/member-in-struct/Inputs/S.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; Index: test/Import/forward-declared-struct/test.c =================================================================== --- test/Import/forward-declared-struct/test.c +++ test/Import/forward-declared-struct/test.c @@ -0,0 +1,5 @@ +// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s +void expr() { + struct S MyS; + MyS.a = 3; +} Index: test/Import/forward-declared-struct/Inputs/S2.c =================================================================== --- test/Import/forward-declared-struct/Inputs/S2.c +++ test/Import/forward-declared-struct/Inputs/S2.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; Index: test/Import/forward-declared-struct/Inputs/S1.c =================================================================== --- test/Import/forward-declared-struct/Inputs/S1.c +++ test/Import/forward-declared-struct/Inputs/S1.c @@ -0,0 +1 @@ +struct S;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits