From: Sean Callanan Date: Tue, 11 Apr 2017 19:33:35 +0000 (+0000) Subject: [clang-import-test] Lookup inside contexts X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3975e67148c7382405ddd5b9004cdad6836cf443;p=clang [clang-import-test] Lookup inside contexts clang-import-test has until now been only able to report top-level Decls. 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 ASTContexts, and adds the functionality necessar to make most of these testcases work. (One testcase is known to fail because of ASTImporter limitations when importing templates; I'll look into that separately.) This patch also separates the core functionality out into ExternalASTMerger, an interface that allows clients like LLDB to make use of it. clang-import-test now only has the machinery necessary to set up the tests. Differential revision: https://reviews.llvm.org/D30435 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@299976 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExternalASTMerger.h b/include/clang/AST/ExternalASTMerger.h new file mode 100644 index 0000000000..51d0c30ad2 --- /dev/null +++ b/include/clang/AST/ExternalASTMerger.h @@ -0,0 +1,51 @@ +//===--- ExternalASTMerger.h - Merging External AST Interface ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the ExternalASTMerger, which vends a combination of ASTs +// from several different ASTContext/FileManager pairs +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_AST_EXTERNALASTMERGER_H +#define LLVM_CLANG_AST_EXTERNALASTMERGER_H + +#include "clang/AST/ASTImporter.h" +#include "clang/AST/ExternalASTSource.h" + +namespace clang { + +class ExternalASTMerger : public ExternalASTSource { +public: + struct ImporterPair { + std::unique_ptr Forward; + std::unique_ptr Reverse; + }; + +private: + std::vector Importers; + +public: + struct ImporterEndpoint { + ASTContext &AST; + FileManager &FM; + }; + ExternalASTMerger(const ImporterEndpoint &Target, + llvm::ArrayRef Sources); + + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override; + + void + FindExternalLexicalDecls(const DeclContext *DC, + llvm::function_ref IsKindWeWant, + SmallVectorImpl &Result) override; +}; + +} // end namespace clang + +#endif diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index 2e98f524da..13bf352c2f 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -31,6 +31,7 @@ add_clang_library(clangAST ExprConstant.cpp ExprCXX.cpp ExprObjC.cpp + ExternalASTMerger.cpp ExternalASTSource.cpp InheritViz.cpp ItaniumCXXABI.cpp diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp new file mode 100644 index 0000000000..1d144047bd --- /dev/null +++ b/lib/AST/ExternalASTMerger.cpp @@ -0,0 +1,183 @@ +//===- ExternalASTMerger.cpp - Merging External AST Interface ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the ExternalASTMerger, which vends a combination of +// ASTs from several different ASTContext/FileManager pairs +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExternalASTMerger.h" + +using namespace clang; + +namespace { + +template struct Source { + T t; + Source(T &&t) : t(std::move(t)) {} + operator T() { return t; } + template U &get() { return t; } + template const U &get() const { return t; } + template operator Source() { return Source(t); } +}; + +typedef std::pair, ASTImporter *> Candidate; + +class LazyASTImporter : public ASTImporter { +public: + LazyASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager) + : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, + /*MinimalImport=*/true) {} + Decl *Imported(Decl *From, Decl *To) override { + if (auto ToTag = dyn_cast(To)) { + ToTag->setHasExternalLexicalStorage(); + } else if (auto ToNamespace = dyn_cast(To)) { + ToNamespace->setHasExternalVisibleStorage(); + } + return ASTImporter::Imported(From, To); + } +}; + +Source +LookupSameContext(Source SourceTU, const DeclContext *DC, + ASTImporter &ReverseImporter) { + if (DC->isTranslationUnit()) { + return SourceTU; + } + Source SourceParentDC = + LookupSameContext(SourceTU, DC->getParent(), ReverseImporter); + if (!SourceParentDC) { + // If we couldn't find the parent DC in this TranslationUnit, give up. + return nullptr; + } + auto ND = cast(DC); + DeclarationName Name = ND->getDeclName(); + Source SourceName = ReverseImporter.Import(Name); + DeclContext::lookup_result SearchResult = + SourceParentDC.get()->lookup(SourceName.get()); + size_t SearchResultSize = SearchResult.size(); + // Handle multiple candidates once we have a test for it. + // This may turn up when we import template specializations correctly. + assert(SearchResultSize < 2); + if (SearchResultSize == 0) { + // couldn't find the name, so we have to give up + return nullptr; + } else { + NamedDecl *SearchResultDecl = SearchResult[0]; + return dyn_cast(SearchResultDecl); + } +} + +bool IsForwardDeclaration(Decl *D) { + assert(!isa(D)); // TODO handle this case + if (auto TD = dyn_cast(D)) { + return !TD->isThisDeclarationADefinition(); + } else if (auto FD = dyn_cast(D)) { + return !FD->isThisDeclarationADefinition(); + } else { + return false; + } +} + +void ForEachMatchingDC( + const DeclContext *DC, + llvm::ArrayRef Importers, + std::function SourceDC)> + Callback) { + for (const ExternalASTMerger::ImporterPair &IP : Importers) { + Source SourceTU( + IP.Forward->getFromContext().getTranslationUnitDecl()); + Source SourceDC = + LookupSameContext(SourceTU, DC, *IP.Reverse); + if (SourceDC.get()) { + Callback(IP, SourceDC); + } + } +} + +bool HasDeclOfSameType(llvm::ArrayRef Decls, const Candidate &C) { + return std::any_of(Decls.begin(), Decls.end(), [&C](const Candidate &D) { + return C.first.get()->getKind() == D.first.get()->getKind(); + }); +} +} // end namespace + +ExternalASTMerger::ExternalASTMerger(const ImporterEndpoint &Target, + llvm::ArrayRef Sources) { + for (const ImporterEndpoint &S : Sources) { + Importers.push_back( + {llvm::make_unique(Target.AST, Target.FM, S.AST, S.FM), + llvm::make_unique(S.AST, S.FM, Target.AST, Target.FM, + /*MinimalImport=*/true)}); + } +} + +bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + llvm::SmallVector Decls; + llvm::SmallVector CompleteDecls; + llvm::SmallVector ForwardDecls; + + auto FilterFoundDecl = [&CompleteDecls, &ForwardDecls](const Candidate &C) { + if (IsForwardDeclaration(C.first.get())) { + if (!HasDeclOfSameType(ForwardDecls, C)) { + ForwardDecls.push_back(C); + } + } else { + CompleteDecls.push_back(C); + } + }; + + ForEachMatchingDC(DC, Importers, [Name, &FilterFoundDecl]( + const ImporterPair &IP, + Source SourceDC) { + DeclarationName FromName = IP.Reverse->Import(Name); + DeclContextLookupResult Result = SourceDC.get()->lookup(FromName); + for (NamedDecl *FromD : Result) { + FilterFoundDecl(std::make_pair(FromD, IP.Forward.get())); + } + }); + + llvm::ArrayRef DeclsToReport = + CompleteDecls.empty() ? ForwardDecls : CompleteDecls; + + if (DeclsToReport.empty()) { + return false; + } + + Decls.reserve(DeclsToReport.size()); + for (const Candidate &C : DeclsToReport) { + NamedDecl *d = cast(C.second->Import(C.first.get())); + assert(d); + Decls.push_back(d); + } + SetExternalVisibleDeclsForName(DC, Name, Decls); + return true; +} + +void ExternalASTMerger::FindExternalLexicalDecls( + const DeclContext *DC, llvm::function_ref IsKindWeWant, + SmallVectorImpl &Result) { + ForEachMatchingDC( + DC, Importers, [DC, IsKindWeWant](const ImporterPair &IP, + Source SourceDC) { + for (Source SourceDecl : SourceDC.get()->decls()) { + if (IsKindWeWant(SourceDecl.get()->getKind())) { + Decl *ImportedDecl = + IP.Forward->Import(const_cast(SourceDecl.get())); + assert(ImportedDecl->getDeclContext() == DC); + } + } + }); +} diff --git a/test/Import/forward-declared-struct/Inputs/S1.c b/test/Import/forward-declared-struct/Inputs/S1.c new file mode 100644 index 0000000000..28377c2760 --- /dev/null +++ b/test/Import/forward-declared-struct/Inputs/S1.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/forward-declared-struct/Inputs/S2.c b/test/Import/forward-declared-struct/Inputs/S2.c new file mode 100644 index 0000000000..b0876d27df --- /dev/null +++ b/test/Import/forward-declared-struct/Inputs/S2.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; diff --git a/test/Import/forward-declared-struct/test.c b/test/Import/forward-declared-struct/test.c new file mode 100644 index 0000000000..7ccdcf9e97 --- /dev/null +++ b/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; +} diff --git a/test/Import/member-in-struct/Inputs/S.c b/test/Import/member-in-struct/Inputs/S.c new file mode 100644 index 0000000000..b0876d27df --- /dev/null +++ b/test/Import/member-in-struct/Inputs/S.c @@ -0,0 +1,3 @@ +struct S { + int a; +}; diff --git a/test/Import/member-in-struct/test.c b/test/Import/member-in-struct/test.c new file mode 100644 index 0000000000..bde2b60d27 --- /dev/null +++ b/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; +} diff --git a/test/Import/multiple-forward-declarations/Inputs/S1.c b/test/Import/multiple-forward-declarations/Inputs/S1.c new file mode 100644 index 0000000000..28377c2760 --- /dev/null +++ b/test/Import/multiple-forward-declarations/Inputs/S1.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/multiple-forward-declarations/Inputs/S2.c b/test/Import/multiple-forward-declarations/Inputs/S2.c new file mode 100644 index 0000000000..28377c2760 --- /dev/null +++ b/test/Import/multiple-forward-declarations/Inputs/S2.c @@ -0,0 +1 @@ +struct S; diff --git a/test/Import/multiple-forward-declarations/test.c b/test/Import/multiple-forward-declarations/test.c new file mode 100644 index 0000000000..fdaaaf6114 --- /dev/null +++ b/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; +} diff --git a/test/Import/overloaded-function/Inputs/F1.c b/test/Import/overloaded-function/Inputs/F1.c new file mode 100644 index 0000000000..fb548b464f --- /dev/null +++ b/test/Import/overloaded-function/Inputs/F1.c @@ -0,0 +1 @@ +void f(int arg) { } diff --git a/test/Import/overloaded-function/Inputs/F2.c b/test/Import/overloaded-function/Inputs/F2.c new file mode 100644 index 0000000000..937efe54d8 --- /dev/null +++ b/test/Import/overloaded-function/Inputs/F2.c @@ -0,0 +1,4 @@ +struct S { int a; }; + +void f(const char *arg) { } +void f(S arg) { } diff --git a/test/Import/overloaded-function/test.c b/test/Import/overloaded-function/test.c new file mode 100644 index 0000000000..4f7781bc8e --- /dev/null +++ b/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); +} diff --git a/test/Import/struct-in-namespace/Inputs/N1.cpp b/test/Import/struct-in-namespace/Inputs/N1.cpp new file mode 100644 index 0000000000..ddb67a5162 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N1.cpp @@ -0,0 +1,11 @@ +namespace N { + struct S { + int a; + }; +} + +namespace N { + struct T { + int b; + }; +} diff --git a/test/Import/struct-in-namespace/Inputs/N2.cpp b/test/Import/struct-in-namespace/Inputs/N2.cpp new file mode 100644 index 0000000000..ad97d5dd52 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N2.cpp @@ -0,0 +1,5 @@ +namespace N { + struct U { + int c; + }; +} diff --git a/test/Import/struct-in-namespace/Inputs/N3.cpp b/test/Import/struct-in-namespace/Inputs/N3.cpp new file mode 100644 index 0000000000..e0ec414674 --- /dev/null +++ b/test/Import/struct-in-namespace/Inputs/N3.cpp @@ -0,0 +1,5 @@ +namespace M { + struct V { + int d; + }; +} diff --git a/test/Import/struct-in-namespace/test.cpp b/test/Import/struct-in-namespace/test.cpp new file mode 100644 index 0000000000..fd14d82d17 --- /dev/null +++ b/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 -import %S/Inputs/N3.cpp -expression %s +void expr() { + N::S s; + N::T t; + N::U u; + int d = s.a + t.b + u.c; +} diff --git a/test/Import/template-specialization/Inputs/T.cpp b/test/Import/template-specialization/Inputs/T.cpp new file mode 100644 index 0000000000..b31e2439ef --- /dev/null +++ b/test/Import/template-specialization/Inputs/T.cpp @@ -0,0 +1,14 @@ +template struct A { +}; + +template <> struct A { + struct B { + int f; + }; +}; + +template <> struct A { + struct B { + int g; + }; +}; diff --git a/test/Import/template-specialization/test.cpp b/test/Import/template-specialization/test.cpp new file mode 100644 index 0000000000..43996c53a7 --- /dev/null +++ b/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::B b1; + A::B b2; + b1.f + b2.g; +} diff --git a/tools/clang-import-test/clang-import-test.cpp b/tools/clang-import-test/clang-import-test.cpp index 33190af4bf..d7ab18478c 100644 --- a/tools/clang-import-test/clang-import-test.cpp +++ b/tools/clang-import-test/clang-import-test.cpp @@ -9,6 +9,8 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExternalASTMerger.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" @@ -189,61 +191,18 @@ std::unique_ptr BuildCodeGen(CompilerInstance &CI, } // end namespace namespace { -class TestExternalASTSource : public ExternalASTSource { -private: - llvm::ArrayRef> ImportCIs; - std::map> ForwardImporters; - std::map> ReverseImporters; - -public: - TestExternalASTSource( - CompilerInstance &ExpressionCI, - llvm::ArrayRef> ImportCIs) - : ImportCIs(ImportCIs) { - for (const std::unique_ptr &ImportCI : ImportCIs) { - ForwardImporters[ImportCI.get()] = llvm::make_unique( - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - ImportCI->getASTContext(), ImportCI->getFileManager(), - /*MinimalImport=*/true); - ReverseImporters[ImportCI.get()] = llvm::make_unique( - ImportCI->getASTContext(), ImportCI->getFileManager(), - ExpressionCI.getASTContext(), ExpressionCI.getFileManager(), - /*MinimalImport=*/true); - } - } - - bool FindExternalVisibleDeclsByName(const DeclContext *DC, - DeclarationName Name) override { - llvm::SmallVector Decls; - - if (isa(DC)) { - for (const std::unique_ptr &I : ImportCIs) { - DeclarationName FromName = ReverseImporters[I.get()]->Import(Name); - DeclContextLookupResult Result = - I->getASTContext().getTranslationUnitDecl()->lookup(FromName); - for (NamedDecl *FromD : Result) { - NamedDecl *D = - llvm::cast(ForwardImporters[I.get()]->Import(FromD)); - Decls.push_back(D); - } - } - } - if (Decls.empty()) { - return false; - } else { - SetExternalVisibleDeclsForName(DC, Name, Decls); - return true; - } - } -}; - + void AddExternalSource( CompilerInstance &CI, llvm::ArrayRef> Imports) { - ASTContext &AST = CI.getASTContext(); - auto ES = llvm::make_unique(CI, Imports); - AST.setExternalSource(ES.release()); - AST.getTranslationUnitDecl()->setHasExternalVisibleStorage(); + ExternalASTMerger::ImporterEndpoint Target({CI.getASTContext(), CI.getFileManager()}); + llvm::SmallVector Sources; + for (const std::unique_ptr &CI : Imports) { + Sources.push_back({CI->getASTContext(), CI->getFileManager()}); + } + auto ES = llvm::make_unique(Target, Sources); + CI.getASTContext().setExternalSource(ES.release()); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); } llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, @@ -292,6 +251,7 @@ Parse(const std::string &Path, return std::move(CI); } } + } // end namespace int main(int argc, const char **argv) {