Index: cfe/trunk/include/clang/AST/ASTImporter.h =================================================================== --- cfe/trunk/include/clang/AST/ASTImporter.h +++ cfe/trunk/include/clang/AST/ASTImporter.h @@ -34,6 +34,7 @@ class ASTContext; class ASTImporterSharedState; +class ASTUnit; class Attr; class CXXBaseSpecifier; class CXXCtorInitializer; @@ -229,6 +230,11 @@ /// The file managers we're importing to and from. FileManager &ToFileManager, &FromFileManager; + /// The ASTUnit for the From context, if any. + /// This is used to create a mapping of imported ('To') FileID's to the + /// original ('From') FileID and ASTUnit. + ASTUnit *FromUnit; + /// Whether to perform a minimal import. bool Minimal; @@ -277,6 +283,11 @@ void AddToLookupTable(Decl *ToD); + ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager, + ASTUnit *FromUnit, bool MinimalImport, + std::shared_ptr SharedState); + protected: /// Can be overwritten by subclasses to implement their own import logic. /// The overwritten method should call this method if it didn't import the @@ -287,7 +298,6 @@ virtual bool returnWithErrorInTest() { return false; }; public: - /// \param ToContext The context we'll be importing into. /// /// \param ToFileManager The file manager we'll be importing into. @@ -307,6 +317,23 @@ ASTContext &FromContext, FileManager &FromFileManager, bool MinimalImport, std::shared_ptr SharedState = nullptr); + /// \param ToContext The context we'll be importing into. + /// + /// \param ToFileManager The file manager we'll be importing into. + /// + /// \param FromUnit Pointer to an ASTUnit that contains the context and + /// file manager to import from. + /// + /// \param MinimalImport If true, the importer will attempt to import + /// as little as it can, e.g., by importing declarations as forward + /// declarations that can be completed at a later point. + /// + /// \param SharedState The importer specific lookup table which may be + /// shared amongst several ASTImporter objects. + /// If not set then the original C/C++ lookup is used. + ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTUnit &FromUnit, bool MinimalImport, + std::shared_ptr SharedState = nullptr); virtual ~ASTImporter(); Index: cfe/trunk/include/clang/AST/ASTImporterSharedState.h =================================================================== --- cfe/trunk/include/clang/AST/ASTImporterSharedState.h +++ cfe/trunk/include/clang/AST/ASTImporterSharedState.h @@ -17,18 +17,24 @@ #include "clang/AST/ASTImporterLookupTable.h" #include "clang/AST/Decl.h" +#include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" // FIXME We need this because of ImportError. #include "clang/AST/ASTImporter.h" namespace clang { +class ASTUnit; class TranslationUnitDecl; /// Importer specific state, which may be shared amongst several ASTImporter /// objects. class ASTImporterSharedState { +public: + using ImportedFileIDMap = + llvm::DenseMap>; +private: /// Pointer to the import specific lookup table. std::unique_ptr LookupTable; @@ -43,6 +49,16 @@ // FIXME put ImportedFromDecls here! // And from that point we can better encapsulate the lookup table. + /// Map of imported FileID's (in "To" context) to FileID in "From" context + /// and the ASTUnit that contains the preprocessor and source manager for the + /// "From" FileID. This map is used to lookup a FileID and its SourceManager + /// when knowing only the FileID in the 'To' context. The FileID could be + /// imported by any of multiple ASTImporter objects. The map is used because + /// we do not want to loop over all ASTImporter's to find the one that + /// imported the FileID. (The ASTUnit is usable to get the SourceManager and + /// additional data.) + ImportedFileIDMap ImportedFileIDs; + public: ASTImporterSharedState() = default; @@ -75,6 +91,12 @@ void setImportDeclError(Decl *To, ImportError Error) { ImportErrors[To] = Error; } + + ImportedFileIDMap &getImportedFileIDs() { return ImportedFileIDs; } + + const ImportedFileIDMap &getImportedFileIDs() const { + return ImportedFileIDs; + } }; } // namespace clang Index: cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h +++ cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h @@ -153,8 +153,10 @@ /// was passed to the constructor. /// /// \return Returns the resulting definition or an error. - llvm::Expected importDefinition(const FunctionDecl *FD); - llvm::Expected importDefinition(const VarDecl *VD); + llvm::Expected importDefinition(const FunctionDecl *FD, + ASTUnit *Unit); + llvm::Expected importDefinition(const VarDecl *VD, + ASTUnit *Unit); /// Get a name to identify a named decl. static std::string getLookupName(const NamedDecl *ND); @@ -162,9 +164,19 @@ /// Emit diagnostics for the user for potential configuration errors. void emitCrossTUDiagnostics(const IndexError &IE); + /// Determine the original source location in the original TU for an + /// imported source location. + /// \p ToLoc Source location in the imported-to AST. + /// \return Source location in the imported-from AST and the corresponding + /// ASTUnit. + /// If any error happens (ToLoc is a non-imported source location) empty is + /// returned. + llvm::Optional> + getImportedFromSourceLocation(const clang::SourceLocation &ToLoc) const; + private: void lazyInitImporterSharedSt(TranslationUnitDecl *ToTU); - ASTImporter &getOrCreateASTImporter(ASTContext &From); + ASTImporter &getOrCreateASTImporter(ASTUnit *Unit); template llvm::Expected getCrossTUDefinitionImpl(const T *D, StringRef CrossTUDir, @@ -174,7 +186,7 @@ const T *findDefInDeclContext(const DeclContext *DC, StringRef LookupName); template - llvm::Expected importDefinitionImpl(const T *D); + llvm::Expected importDefinitionImpl(const T *D, ASTUnit *Unit); llvm::StringMap> FileASTUnitMap; llvm::StringMap NameASTUnitMap; Index: cfe/trunk/lib/AST/ASTImporter.cpp =================================================================== --- cfe/trunk/lib/AST/ASTImporter.cpp +++ cfe/trunk/lib/AST/ASTImporter.cpp @@ -12,9 +12,9 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTImporter.h" -#include "clang/AST/ASTImporterSharedState.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporterSharedState.h" #include "clang/AST/ASTStructuralEquivalence.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" @@ -52,13 +52,14 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" +#include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" -#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" @@ -7716,9 +7717,22 @@ ASTContext &FromContext, FileManager &FromFileManager, bool MinimalImport, std::shared_ptr SharedState) + : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager, + nullptr, MinimalImport, SharedState){} +ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTUnit &FromUnit, bool MinimalImport, + std::shared_ptr SharedState) + : ASTImporter(ToContext, ToFileManager, FromUnit.getASTContext(), + FromUnit.getFileManager(), &FromUnit, MinimalImport, + SharedState){} + +ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager, + ASTContext &FromContext, FileManager &FromFileManager, + ASTUnit *FromUnit, bool MinimalImport, + std::shared_ptr SharedState) : SharedState(SharedState), ToContext(ToContext), FromContext(FromContext), ToFileManager(ToFileManager), FromFileManager(FromFileManager), - Minimal(MinimalImport) { + FromUnit(FromUnit), Minimal(MinimalImport) { // Create a default state without the lookup table: LLDB case. if (!SharedState) { @@ -8421,6 +8435,13 @@ assert(ToID.isValid() && "Unexpected invalid fileID was created."); ImportedFileIDs[FromID] = ToID; + if (FromUnit) { + assert(SharedState->getImportedFileIDs().find(ToID) == + SharedState->getImportedFileIDs().end() && + "FileID already imported!"); + SharedState->getImportedFileIDs()[ToID] = std::make_pair(FromID, FromUnit); + } + return ToID; } Index: cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp +++ cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp @@ -295,7 +295,7 @@ TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); if (const T *ResultDecl = findDefInDeclContext(TU, LookupName)) - return importDefinition(ResultDecl); + return importDefinition(ResultDecl, Unit); return llvm::make_error(index_error_code::failed_import); } @@ -411,10 +411,13 @@ template llvm::Expected -CrossTranslationUnitContext::importDefinitionImpl(const T *D) { +CrossTranslationUnitContext::importDefinitionImpl(const T *D, ASTUnit *Unit) { assert(hasBodyOrInit(D) && "Decls to be imported should have body or init."); - ASTImporter &Importer = getOrCreateASTImporter(D->getASTContext()); + assert(&D->getASTContext() == &Unit->getASTContext() && + "ASTContext of Decl and the unit should match."); + ASTImporter &Importer = getOrCreateASTImporter(Unit); + auto ToDeclOrError = Importer.Import(D); if (!ToDeclOrError) { handleAllErrors(ToDeclOrError.takeError(), @@ -441,13 +444,15 @@ } llvm::Expected -CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD) { - return importDefinitionImpl(FD); +CrossTranslationUnitContext::importDefinition(const FunctionDecl *FD, + ASTUnit *Unit) { + return importDefinitionImpl(FD, Unit); } llvm::Expected -CrossTranslationUnitContext::importDefinition(const VarDecl *VD) { - return importDefinitionImpl(VD); +CrossTranslationUnitContext::importDefinition(const VarDecl *VD, + ASTUnit *Unit) { + return importDefinitionImpl(VD, Unit); } void CrossTranslationUnitContext::lazyInitImporterSharedSt( @@ -457,17 +462,40 @@ } ASTImporter & -CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) { +CrossTranslationUnitContext::getOrCreateASTImporter(ASTUnit *Unit) { + ASTContext &From = Unit->getASTContext(); + auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl()); if (I != ASTUnitImporterMap.end()) return *I->second; lazyInitImporterSharedSt(Context.getTranslationUnitDecl()); - ASTImporter *NewImporter = new ASTImporter( - Context, Context.getSourceManager().getFileManager(), From, - From.getSourceManager().getFileManager(), false, ImporterSharedSt); + ASTImporter *NewImporter = + new ASTImporter(Context, Context.getSourceManager().getFileManager(), + *Unit, false, ImporterSharedSt); ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter); return *NewImporter; } +llvm::Optional> +CrossTranslationUnitContext::getImportedFromSourceLocation( + const clang::SourceLocation &ToLoc) const { + if (!ImporterSharedSt) + return {}; + + const SourceManager &SM = Context.getSourceManager(); + auto DecToLoc = SM.getDecomposedLoc(ToLoc); + + auto I = ImporterSharedSt->getImportedFileIDs().find(DecToLoc.first); + if (I == ImporterSharedSt->getImportedFileIDs().end()) + return {}; + + FileID FromID = I->second.first; + clang::ASTUnit *Unit = I->second.second; + SourceLocation FromLoc = + Unit->getSourceManager().getComposedLoc(FromID, DecToLoc.second); + + return std::make_pair(FromLoc, Unit); +} + } // namespace cross_tu } // namespace clang Index: cfe/trunk/unittests/CrossTU/CrossTranslationUnitTest.cpp =================================================================== --- cfe/trunk/unittests/CrossTU/CrossTranslationUnitTest.cpp +++ cfe/trunk/unittests/CrossTU/CrossTranslationUnitTest.cpp @@ -28,13 +28,18 @@ : CTU(CI), Success(Success) {} void HandleTranslationUnit(ASTContext &Ctx) { + auto FindFInTU = [](const TranslationUnitDecl *TU) { + const FunctionDecl *FD = nullptr; + for (const Decl *D : TU->decls()) { + FD = dyn_cast(D); + if (FD && FD->getName() == "f") + break; + } + return FD; + }; + const TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); - const FunctionDecl *FD = nullptr; - for (const Decl *D : TU->decls()) { - FD = dyn_cast(D); - if (FD && FD->getName() == "f") - break; - } + const FunctionDecl *FD = FindFInTU(TU); assert(FD && FD->getName() == "f"); bool OrigFDHasBody = FD->hasBody(); @@ -78,6 +83,28 @@ if (NewFDorError) { const FunctionDecl *NewFD = *NewFDorError; *Success = NewFD && NewFD->hasBody() && !OrigFDHasBody; + + if (NewFD) { + // Check GetImportedFromSourceLocation. + llvm::Optional> SLocResult = + CTU.getImportedFromSourceLocation(NewFD->getLocation()); + EXPECT_TRUE(SLocResult); + if (SLocResult) { + SourceLocation OrigSLoc = (*SLocResult).first; + ASTUnit *OrigUnit = (*SLocResult).second; + // OrigUnit is created internally by CTU (is not the + // ASTWithDefinition). + TranslationUnitDecl *OrigTU = + OrigUnit->getASTContext().getTranslationUnitDecl(); + const FunctionDecl *FDWithDefinition = FindFInTU(OrigTU); + EXPECT_TRUE(FDWithDefinition); + if (FDWithDefinition) { + EXPECT_EQ(FDWithDefinition->getName(), "f"); + EXPECT_TRUE(FDWithDefinition->isThisDeclarationADefinition()); + EXPECT_EQ(OrigSLoc, FDWithDefinition->getLocation()); + } + } + } } }