Skip to content
Snippets Groups Projects
Commit 3975e671 authored by Sean Callanan's avatar Sean Callanan
Browse files

[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
parent 44e7dd4b
No related branches found
No related tags found
No related merge requests found
Showing
with 319 additions and 0 deletions
//===--- 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<ASTImporter> Forward;
std::unique_ptr<ASTImporter> Reverse;
};
private:
std::vector<ImporterPair> Importers;
public:
struct ImporterEndpoint {
ASTContext &AST;
FileManager &FM;
};
ExternalASTMerger(const ImporterEndpoint &Target,
llvm::ArrayRef<ImporterEndpoint> Sources);
bool FindExternalVisibleDeclsByName(const DeclContext *DC,
DeclarationName Name) override;
void
FindExternalLexicalDecls(const DeclContext *DC,
llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
SmallVectorImpl<Decl *> &Result) override;
};
} // end namespace clang
#endif
...@@ -31,6 +31,7 @@ add_clang_library(clangAST ...@@ -31,6 +31,7 @@ add_clang_library(clangAST
ExprConstant.cpp ExprConstant.cpp
ExprCXX.cpp ExprCXX.cpp
ExprObjC.cpp ExprObjC.cpp
ExternalASTMerger.cpp
ExternalASTSource.cpp ExternalASTSource.cpp
InheritViz.cpp InheritViz.cpp
ItaniumCXXABI.cpp ItaniumCXXABI.cpp
......
//===- 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 <typename T> struct Source {
T t;
Source(T &&t) : t(std::move(t)) {}
operator T() { return t; }
template <typename U = T> U &get() { return t; }
template <typename U = T> const U &get() const { return t; }
template <typename U> operator Source<U>() { return Source<U>(t); }
};
typedef std::pair<Source<NamedDecl *>, 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<TagDecl>(To)) {
ToTag->setHasExternalLexicalStorage();
} else if (auto ToNamespace = dyn_cast<NamespaceDecl>(To)) {
ToNamespace->setHasExternalVisibleStorage();
}
return ASTImporter::Imported(From, To);
}
};
Source<const DeclContext *>
LookupSameContext(Source<TranslationUnitDecl *> SourceTU, const DeclContext *DC,
ASTImporter &ReverseImporter) {
if (DC->isTranslationUnit()) {
return SourceTU;
}
Source<const DeclContext *> 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<NamedDecl>(DC);
DeclarationName Name = ND->getDeclName();
Source<DeclarationName> 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<DeclContext>(SearchResultDecl);
}
}
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;
}
}
void ForEachMatchingDC(
const DeclContext *DC,
llvm::ArrayRef<ExternalASTMerger::ImporterPair> Importers,
std::function<void(const ExternalASTMerger::ImporterPair &IP,
Source<const DeclContext *> SourceDC)>
Callback) {
for (const ExternalASTMerger::ImporterPair &IP : Importers) {
Source<TranslationUnitDecl *> SourceTU(
IP.Forward->getFromContext().getTranslationUnitDecl());
Source<const DeclContext *> SourceDC =
LookupSameContext(SourceTU, DC, *IP.Reverse);
if (SourceDC.get()) {
Callback(IP, SourceDC);
}
}
}
bool HasDeclOfSameType(llvm::ArrayRef<Candidate> 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<ImporterEndpoint> Sources) {
for (const ImporterEndpoint &S : Sources) {
Importers.push_back(
{llvm::make_unique<LazyASTImporter>(Target.AST, Target.FM, S.AST, S.FM),
llvm::make_unique<ASTImporter>(S.AST, S.FM, Target.AST, Target.FM,
/*MinimalImport=*/true)});
}
}
bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC,
DeclarationName Name) {
llvm::SmallVector<NamedDecl *, 1> Decls;
llvm::SmallVector<Candidate, 4> CompleteDecls;
llvm::SmallVector<Candidate, 4> 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<const DeclContext *> 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<Candidate> DeclsToReport =
CompleteDecls.empty() ? ForwardDecls : CompleteDecls;
if (DeclsToReport.empty()) {
return false;
}
Decls.reserve(DeclsToReport.size());
for (const Candidate &C : DeclsToReport) {
NamedDecl *d = cast<NamedDecl>(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<bool(Decl::Kind)> IsKindWeWant,
SmallVectorImpl<Decl *> &Result) {
ForEachMatchingDC(
DC, Importers, [DC, IsKindWeWant](const ImporterPair &IP,
Source<const DeclContext *> SourceDC) {
for (Source<const Decl *> SourceDecl : SourceDC.get()->decls()) {
if (IsKindWeWant(SourceDecl.get()->getKind())) {
Decl *ImportedDecl =
IP.Forward->Import(const_cast<Decl *>(SourceDecl.get()));
assert(ImportedDecl->getDeclContext() == DC);
}
}
});
}
struct S;
struct S {
int a;
};
// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
void expr() {
struct S MyS;
MyS.a = 3;
}
struct S {
int a;
};
// RUN: clang-import-test -import %S/Inputs/S.c -expression %s
void expr() {
struct S MyS;
MyS.a = 3;
}
struct S;
struct S;
// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
void expr() {
struct S *MySPtr;
}
void f(int arg) { }
struct S { int a; };
void f(const char *arg) { }
void f(S arg) { }
// 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);
}
namespace N {
struct S {
int a;
};
}
namespace N {
struct T {
int b;
};
}
namespace N {
struct U {
int c;
};
}
namespace M {
struct V {
int d;
};
}
// 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;
}
template <typename T> struct A {
};
template <> struct A<int> {
struct B {
int f;
};
};
template <> struct A<bool> {
struct B {
int g;
};
};
// 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;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment