From: Douglas Gregor Date: Wed, 10 Feb 2010 00:15:17 +0000 (+0000) Subject: Implement basic support for importing source locations from one AST X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=885237354fd902998c6ae9d7cc3dc8de96b123dc;p=clang 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!). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95732 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ASTImporter.h b/include/clang/AST/ASTImporter.h index 71b6443f50..bdc1a51e98 100644 --- a/include/clang/AST/ASTImporter.h +++ b/include/clang/AST/ASTImporter.h @@ -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 ImportedDecls; + /// \brief Mapping from the already-imported FileIDs in the "from" source + /// manager to the corresponding FileIDs in the "to" source manager. + llvm::DenseMap 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; } diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 19ce889513..75917cf04c 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -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 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::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(); diff --git a/lib/Frontend/ASTMerge.cpp b/lib/Frontend/ASTMerge.cpp index e88d2953c3..d51647bb16 100644 --- a/lib/Frontend/ASTMerge.cpp +++ b/lib/Frontend/ASTMerge.cpp @@ -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(), diff --git a/test/ASTMerge/Inputs/var1.c b/test/ASTMerge/Inputs/var1.c index c87e58b6ad..465258988b 100644 --- a/test/ASTMerge/Inputs/var1.c +++ b/test/ASTMerge/Inputs/var1.c @@ -1,2 +1,3 @@ int *x0; float **x1; +#include "var1.h" diff --git a/test/ASTMerge/Inputs/var2.c b/test/ASTMerge/Inputs/var2.c index 526a45b9f7..e93a010cbf 100644 --- a/test/ASTMerge/Inputs/var2.c +++ b/test/ASTMerge/Inputs/var2.c @@ -1,2 +1,3 @@ int *x0; double *x1; +int x2; diff --git a/test/ASTMerge/var.c b/test/ASTMerge/var.c index 4fda4acb9d..98ad7ab4a0 100644 --- a/test/ASTMerge/var.c +++ b/test/ASTMerge/var.c @@ -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'