diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -8479,6 +8479,15 @@ if (!ToIncludeLoc) return ToIncludeLoc.takeError(); + // Every FileID that is not the main FileID needs to have a valid include + // location so that the include chain points to the main FileID. When + // importing the main FileID (which has no include location), we need to + // create a fake include location in the main file to keep this property + // intact. + SourceLocation ToIncludeLocOrFakeLoc = *ToIncludeLoc; + if (FromID == FromSM.getMainFileID()) + ToIncludeLocOrFakeLoc = ToSM.getLocForStartOfFile(ToSM.getMainFileID()); + if (Cache->OrigEntry && Cache->OrigEntry->getDir()) { // FIXME: We probably want to use getVirtualFile(), so we don't hit the // disk again @@ -8490,7 +8499,7 @@ // point to a valid file and we get no Entry here. In this case try with // the memory buffer below. if (Entry) - ToID = ToSM.createFileID(*Entry, *ToIncludeLoc, + ToID = ToSM.createFileID(*Entry, ToIncludeLocOrFakeLoc, FromSLoc.getFile().getFileCharacteristic()); } } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -5845,6 +5845,48 @@ EXPECT_TRUE(isa(To->getReturnType())); } +struct ImportSourceLocations : ASTImporterOptionSpecificTestBase {}; + +TEST_P(ImportSourceLocations, PreserveFileIDTreeStructure) { + // Tests that the FileID tree structure (with the links being the include + // chains) is preserved while importing other files (which need to be + // added to this structure with fake include locations. + + SourceLocation Location1; + { + auto Pattern = varDecl(hasName("X")); + Decl *FromTU = getTuDecl("int X;", Lang_C, "input0.c"); + auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); + + Location1 = Import(FromD, Lang_C)->getLocation(); + } + SourceLocation Location2; + { + auto Pattern = varDecl(hasName("Y")); + Decl *FromTU = getTuDecl("int Y;", Lang_C, "input1.c"); + auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); + + Location2 = Import(FromD, Lang_C)->getLocation(); + } + + SourceManager &ToSM = ToAST->getSourceManager(); + FileID FileID1 = ToSM.getFileID(Location1); + FileID FileID2 = ToSM.getFileID(Location2); + + // Check that the imported files look like as if they were included from the + // start of the main file. + SourceLocation FileStart = ToSM.getLocForStartOfFile(ToSM.getMainFileID()); + EXPECT_NE(FileID1, ToSM.getMainFileID()); + EXPECT_NE(FileID2, ToSM.getMainFileID()); + EXPECT_EQ(ToSM.getIncludeLoc(FileID1), FileStart); + EXPECT_EQ(ToSM.getIncludeLoc(FileID2), FileStart); + + // Let the SourceManager check the order of the locations. The order should + // be the order in which the declarations are imported. + EXPECT_TRUE(ToSM.isBeforeInTranslationUnit(Location1, Location2)); + EXPECT_FALSE(ToSM.isBeforeInTranslationUnit(Location2, Location1)); +} + INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest, DefaultTestValuesForRunOptions, ); @@ -5903,5 +5945,8 @@ INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest, DefaultTestValuesForRunOptions, ); +INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportSourceLocations, + DefaultTestValuesForRunOptions, ); + } // end namespace ast_matchers } // end namespace clang