Implement basic support for importing source locations from one AST

into another AST, including their include history. Here's an example
error that involves a conflict merging a variable with different types
in two translation units (diagnosed in the third AST context into
which everything is merged).

/Volumes/Data/dgregor/Projects/llvm/tools/clang/test/ASTMerge/Inputs/var2.c:3:5:
error: external variable 'x2' declared with incompatible types in
different translation units ('int' vs. 'double')
int x2;
    ^
In file included from
/Volumes/Data/dgregor/Projects/llvm/tools/clang/test/ASTMerge/Inputs/var1.c:3:
/Volumes/Data/dgregor/Projects/llvm/tools/clang/test/ASTMerge/Inputs/var1.h:1:8:
note: declared here with type 'double'
double x2;
       ^

Although we maintain include history, we do not maintain macro
instantiation history across a merge. Instead, we map down to the
spelling location (for now!).

llvm-svn: 95732
This commit is contained in:
Douglas Gregor 2010-02-10 00:15:17 +00:00
parent 08f618cd03
commit 811663eb11
6 changed files with 99 additions and 12 deletions

View File

@ -25,6 +25,7 @@ namespace clang {
class DeclContext;
class Diagnostic;
class Expr;
class FileManager;
class IdentifierInfo;
class NestedNameSpecifier;
class Stmt;
@ -36,6 +37,9 @@ namespace clang {
/// \brief The contexts we're importing to and from.
ASTContext &ToContext, &FromContext;
/// \brief The file managers we're importing to and from.
FileManager &ToFileManager, &FromFileManager;
/// \brief The diagnostics object that we should use to emit diagnostics
/// within the context we're importing to and from.
Diagnostic &ToDiags, &FromDiags;
@ -48,9 +52,15 @@ namespace clang {
/// context to the corresponding declarations in the "to" context.
llvm::DenseMap<Decl *, Decl *> ImportedDecls;
/// \brief Mapping from the already-imported FileIDs in the "from" source
/// manager to the corresponding FileIDs in the "to" source manager.
llvm::DenseMap<unsigned, FileID> ImportedFileIDs;
public:
ASTImporter(ASTContext &ToContext, Diagnostic &ToDiags,
ASTContext &FromContext, Diagnostic &FromDiags);
ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
Diagnostic &ToDiags,
ASTContext &FromContext, FileManager &FromFileManager,
Diagnostic &FromDiags);
virtual ~ASTImporter();
@ -102,7 +112,7 @@ namespace clang {
/// \returns the equivalent nested-name-specifier in the "to"
/// context, or NULL if an error occurred.
NestedNameSpecifier *Import(NestedNameSpecifier *FromNNS);
/// \brief Import the given source location from the "from" context into
/// the "to" context.
///
@ -130,6 +140,13 @@ namespace clang {
/// \returns the equivalent identifier in the "to" context.
IdentifierInfo *Import(IdentifierInfo *FromId);
/// \brief Import the given file ID from the "from" context into the
/// "to" context.
///
/// \returns the equivalent file ID in the source manager of the "to"
/// context.
FileID Import(FileID);
/// \brief Cope with a name conflict when importing a declaration into the
/// given context.
///
@ -168,6 +185,12 @@ namespace clang {
/// \brief Retrieve the context that AST nodes are being imported from.
ASTContext &getFromContext() const { return FromContext; }
/// \brief Retrieve the file manager that AST nodes are being imported into.
FileManager &getToFileManager() const { return ToFileManager; }
/// \brief Retrieve the file manager that AST nodes are being imported from.
FileManager &getFromFileManager() const { return FromFileManager; }
/// \brief Retrieve the diagnostics object to use to report errors within
/// the context we're importing into.
Diagnostic &getToDiags() const { return ToDiags; }

View File

@ -14,10 +14,13 @@
#include "clang/AST/ASTImporter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DeclVisitor.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace clang;
@ -444,7 +447,7 @@ QualType ASTNodeImporter::VisitObjCObjectPointerType(ObjCObjectPointerType *T) {
// Import Declarations
//----------------------------------------------------------------------------
Decl *ASTNodeImporter::VisitDecl(Decl *D) {
Importer.FromDiag(SourceLocation(), diag::err_unsupported_ast_node)
Importer.FromDiag(D->getLocation(), diag::err_unsupported_ast_node)
<< D->getDeclKindName();
return 0;
}
@ -567,9 +570,12 @@ Decl *ASTNodeImporter::VisitVarDecl(VarDecl *D) {
return ToVar;
}
ASTImporter::ASTImporter(ASTContext &ToContext, Diagnostic &ToDiags,
ASTContext &FromContext, Diagnostic &FromDiags)
ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
Diagnostic &ToDiags,
ASTContext &FromContext, FileManager &FromFileManager,
Diagnostic &FromDiags)
: ToContext(ToContext), FromContext(FromContext),
ToFileManager(ToFileManager), FromFileManager(FromFileManager),
ToDiags(ToDiags), FromDiags(FromDiags) {
ImportedDecls[FromContext.getTranslationUnitDecl()]
= ToContext.getTranslationUnitDecl();
@ -658,14 +664,62 @@ SourceLocation ASTImporter::Import(SourceLocation FromLoc) {
if (FromLoc.isInvalid())
return SourceLocation();
// FIXME: Implement!
return SourceLocation();
SourceManager &FromSM = FromContext.getSourceManager();
// For now, map everything down to its spelling location, so that we
// don't have to import macro instantiations.
// FIXME: Import macro instantiations!
FromLoc = FromSM.getSpellingLoc(FromLoc);
std::pair<FileID, unsigned> Decomposed = FromSM.getDecomposedLoc(FromLoc);
SourceManager &ToSM = ToContext.getSourceManager();
return ToSM.getLocForStartOfFile(Import(Decomposed.first))
.getFileLocWithOffset(Decomposed.second);
}
SourceRange ASTImporter::Import(SourceRange FromRange) {
return SourceRange(Import(FromRange.getBegin()), Import(FromRange.getEnd()));
}
FileID ASTImporter::Import(FileID FromID) {
llvm::DenseMap<unsigned, FileID>::iterator Pos
= ImportedFileIDs.find(FromID.getHashValue());
if (Pos != ImportedFileIDs.end())
return Pos->second;
SourceManager &FromSM = FromContext.getSourceManager();
SourceManager &ToSM = ToContext.getSourceManager();
const SrcMgr::SLocEntry &FromSLoc = FromSM.getSLocEntry(FromID);
assert(FromSLoc.isFile() && "Cannot handle macro instantiations yet");
// Include location of this file.
SourceLocation ToIncludeLoc = Import(FromSLoc.getFile().getIncludeLoc());
// Map the FileID for to the "to" source manager.
FileID ToID;
const SrcMgr::ContentCache *Cache = FromSLoc.getFile().getContentCache();
if (Cache->Entry) {
// FIXME: We probably want to use getVirtualFile(), so we don't hit the
// disk again
// FIXME: We definitely want to re-use the existing MemoryBuffer, rather
// than mmap the files several times.
const FileEntry *Entry = ToFileManager.getFile(Cache->Entry->getName());
ToID = ToSM.createFileID(Entry, ToIncludeLoc,
FromSLoc.getFile().getFileCharacteristic());
} else {
// FIXME: We want to re-use the existing MemoryBuffer!
const llvm::MemoryBuffer *FromBuf = Cache->getBuffer();
llvm::MemoryBuffer *ToBuf
= llvm::MemoryBuffer::getMemBufferCopy(FromBuf->getBufferStart(),
FromBuf->getBufferEnd(),
FromBuf->getBufferIdentifier());
ToID = ToSM.createFileIDForMemBuffer(ToBuf);
}
ImportedFileIDs[FromID.getHashValue()] = ToID;
return ToID;
}
DeclarationName ASTImporter::Import(DeclarationName FromName) {
if (!FromName)
return DeclarationName();

View File

@ -44,8 +44,12 @@ void ASTMergeAction::ExecuteAction() {
ASTDiags.SetArgToStringFn(&FormatASTNodeDiagnosticArgument,
&Unit->getASTContext());
ASTImporter Importer(CI.getASTContext(), CI.getDiagnostics(),
Unit->getASTContext(), ASTDiags);
ASTImporter Importer(CI.getASTContext(),
CI.getFileManager(),
CI.getDiagnostics(),
Unit->getASTContext(),
Unit->getFileManager(),
ASTDiags);
TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
for (DeclContext::decl_iterator D = TU->decls_begin(),

View File

@ -1,2 +1,3 @@
int *x0;
float **x1;
#include "var1.h"

View File

@ -1,2 +1,3 @@
int *x0;
double *x1;
int x2;

View File

@ -2,4 +2,8 @@
// RUN: %clang_cc1 -emit-pch -o %t.2.ast %S/Inputs/var2.c
// RUN: %clang_cc1 -ast-merge %t.1.ast -ast-merge %t.2.ast -fsyntax-only %s 2>&1 | FileCheck %s
// CHECK: error: external variable 'x1' declared with incompatible types in different translation units ('double *' vs. 'float **')
// CHECK: var2.c:2:9: error: external variable 'x1' declared with incompatible types in different translation units ('double *' vs. 'float **')
// CHECK: var1.c:2:9: note: declared here with type 'float **'
// CHECK: var2.c:3:5: error: external variable 'x2' declared with incompatible types in different translation units ('int' vs. 'double')
// CHECK: In file included from{{.*}}var1.c:3:
// CHECK: var1.h:1:8: note: declared here with type 'double'