aboutsummaryrefslogtreecommitdiff
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/include/clang/Lex/PreprocessingRecord.h18
-rw-r--r--clang/include/clang/Serialization/ASTBitCodes.h22
-rw-r--r--clang/include/clang/Serialization/ASTReader.h10
-rw-r--r--clang/include/clang/Serialization/Module.h6
-rw-r--r--clang/lib/Lex/PPDirectives.cpp4
-rw-r--r--clang/lib/Lex/PreprocessingRecord.cpp21
-rw-r--r--clang/lib/Serialization/ASTReader.cpp32
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp21
-rw-r--r--clang/tools/libclang/CIndex.cpp3
-rw-r--r--clang/unittests/libclang/LibclangTest.cpp78
10 files changed, 212 insertions, 3 deletions
diff --git a/clang/include/clang/Lex/PreprocessingRecord.h b/clang/include/clang/Lex/PreprocessingRecord.h
index 5f7a6ef..77da1eb 100644
--- a/clang/include/clang/Lex/PreprocessingRecord.h
+++ b/clang/include/clang/Lex/PreprocessingRecord.h
@@ -297,6 +297,9 @@ class Token;
FileID FID) {
return None;
}
+
+ /// \brief Read a preallocated skipped range from the external source.
+ virtual SourceRange ReadSkippedRange(unsigned Index) = 0;
};
/// \brief A record of the steps taken while preprocessing a source file,
@@ -322,6 +325,8 @@ class Token;
/// \brief The set of ranges that were skipped by the preprocessor,
std::vector<SourceRange> SkippedRanges;
+ bool SkippedRangesAllLoaded = true;
+
/// \brief Global (loaded or local) ID for a preprocessed entity.
/// Negative values are used to indicate preprocessed entities
/// loaded from the external source while non-negative values are used to
@@ -377,6 +382,16 @@ class Token;
/// corresponds to the first newly-allocated entity.
unsigned allocateLoadedEntities(unsigned NumEntities);
+ /// \brief Allocate space for a new set of loaded preprocessed skipped
+ /// ranges.
+ ///
+ /// \returns The index into the set of loaded preprocessed ranges, which
+ /// corresponds to the first newly-allocated range.
+ unsigned allocateSkippedRanges(unsigned NumRanges);
+
+ /// \brief Ensures that all external skipped ranges have been loaded.
+ void ensureSkippedRangesLoaded();
+
/// \brief Register a new macro definition.
void RegisterMacroDefinition(MacroInfo *Macro, MacroDefinitionRecord *Def);
@@ -499,7 +514,8 @@ class Token;
MacroDefinitionRecord *findMacroDefinition(const MacroInfo *MI);
/// \brief Retrieve all ranges that got skipped while preprocessing.
- const std::vector<SourceRange> &getSkippedRanges() const {
+ const std::vector<SourceRange> &getSkippedRanges() {
+ ensureSkippedRangesLoaded();
return SkippedRanges;
}
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 34a7bb3..1f4e034 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -198,6 +198,25 @@ namespace serialization {
}
};
+ /// \brief Source range of a skipped preprocessor region
+ struct PPSkippedRange {
+ /// \brief Raw source location of beginning of range.
+ unsigned Begin;
+ /// \brief Raw source location of end of range.
+ unsigned End;
+
+ PPSkippedRange(SourceRange R)
+ : Begin(R.getBegin().getRawEncoding()),
+ End(R.getEnd().getRawEncoding()) { }
+
+ SourceLocation getBegin() const {
+ return SourceLocation::getFromRawEncoding(Begin);
+ }
+ SourceLocation getEnd() const {
+ return SourceLocation::getFromRawEncoding(End);
+ }
+ };
+
/// \brief Source range/offset of a preprocessed entity.
struct DeclOffset {
/// \brief Raw source location.
@@ -627,6 +646,9 @@ namespace serialization {
/// \brief The stack of open #ifs/#ifdefs recorded in a preamble.
PP_CONDITIONAL_STACK = 62,
+
+ /// \brief A table of skipped ranges within the preprocessing record.
+ PPD_SKIPPED_RANGES = 63
};
/// \brief Record types used within a source manager block.
diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h
index 37920fc..ed1f4cb 100644
--- a/clang/include/clang/Serialization/ASTReader.h
+++ b/clang/include/clang/Serialization/ASTReader.h
@@ -753,6 +753,13 @@ private:
/// added to the global preprocessing entity ID to produce a local ID.
GlobalPreprocessedEntityMapType GlobalPreprocessedEntityMap;
+ typedef ContinuousRangeMap<unsigned, ModuleFile *, 4>
+ GlobalSkippedRangeMapType;
+
+ /// \brief Mapping from global skipped range base IDs to the module in which
+ /// the skipped ranges reside.
+ GlobalSkippedRangeMapType GlobalSkippedRangeMap;
+
/// \name CodeGen-relevant special data
/// \brief Fields containing data that is relevant to CodeGen.
//@{
@@ -1693,6 +1700,9 @@ public:
Optional<bool> isPreprocessedEntityInFileID(unsigned Index,
FileID FID) override;
+ /// \brief Read a preallocated skipped range from the external source.
+ SourceRange ReadSkippedRange(unsigned Index) override;
+
/// \brief Read the header file information for the given file entry.
HeaderFileInfo GetHeaderFileInfo(const FileEntry *FE) override;
diff --git a/clang/include/clang/Serialization/Module.h b/clang/include/clang/Serialization/Module.h
index 4e4bf44..26b3bce 100644
--- a/clang/include/clang/Serialization/Module.h
+++ b/clang/include/clang/Serialization/Module.h
@@ -324,6 +324,12 @@ public:
const PPEntityOffset *PreprocessedEntityOffsets = nullptr;
unsigned NumPreprocessedEntities = 0;
+ /// \brief Base ID for preprocessed skipped ranges local to this module.
+ unsigned BasePreprocessedSkippedRangeID = 0;
+
+ const PPSkippedRange *PreprocessedSkippedRangeOffsets = nullptr;
+ unsigned NumPreprocessedSkippedRanges = 0;
+
// === Header search information ===
/// \brief The number of local HeaderFileInfo structures.
diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp
index 77ee1fa..405d19b 100644
--- a/clang/lib/Lex/PPDirectives.cpp
+++ b/clang/lib/Lex/PPDirectives.cpp
@@ -558,7 +558,9 @@ void Preprocessor::SkipExcludedConditionalBlock(SourceLocation HashTokenLoc,
// the #if block.
CurPPLexer->LexingRawMode = false;
- if (Callbacks)
+ // The last skipped range isn't actually skipped yet if it's truncated
+ // by the end of the preamble; we'll resume parsing after the preamble.
+ if (Callbacks && (Tok.isNot(tok::eof) || !isRecordingPreamble()))
Callbacks->SourceRangeSkipped(
SourceRange(HashTokenLoc, CurPPLexer->getSourceLocation()),
Tok.getLocation());
diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp
index af439dbf..cd4a63f 100644
--- a/clang/lib/Lex/PreprocessingRecord.cpp
+++ b/clang/lib/Lex/PreprocessingRecord.cpp
@@ -329,6 +329,23 @@ unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) {
return Result;
}
+unsigned PreprocessingRecord::allocateSkippedRanges(unsigned NumRanges) {
+ unsigned Result = SkippedRanges.size();
+ SkippedRanges.resize(SkippedRanges.size() + NumRanges);
+ SkippedRangesAllLoaded = false;
+ return Result;
+}
+
+void PreprocessingRecord::ensureSkippedRangesLoaded() {
+ if (SkippedRangesAllLoaded || !ExternalSource)
+ return;
+ for (unsigned Index = 0; Index != SkippedRanges.size(); ++Index) {
+ if (SkippedRanges[Index].isInvalid())
+ SkippedRanges[Index] = ExternalSource->ReadSkippedRange(Index);
+ }
+ SkippedRangesAllLoaded = true;
+}
+
void PreprocessingRecord::RegisterMacroDefinition(MacroInfo *Macro,
MacroDefinitionRecord *Def) {
MacroDefinitions[Macro] = Def;
@@ -418,6 +435,7 @@ void PreprocessingRecord::Defined(const Token &MacroNameTok,
void PreprocessingRecord::SourceRangeSkipped(SourceRange Range,
SourceLocation EndifLoc) {
+ assert(Range.isValid());
SkippedRanges.emplace_back(Range.getBegin(), EndifLoc);
}
@@ -497,5 +515,6 @@ size_t PreprocessingRecord::getTotalMemory() const {
return BumpAlloc.getTotalMemory()
+ llvm::capacity_in_bytes(MacroDefinitions)
+ llvm::capacity_in_bytes(PreprocessedEntities)
- + llvm::capacity_in_bytes(LoadedPreprocessedEntities);
+ + llvm::capacity_in_bytes(LoadedPreprocessedEntities)
+ + llvm::capacity_in_bytes(SkippedRanges);
}
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 66dcf14..b62c47c 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -3215,6 +3215,24 @@ ASTReader::ReadASTBlock(ModuleFile &F, unsigned ClientLoadCapabilities) {
break;
}
+ case PPD_SKIPPED_RANGES: {
+ F.PreprocessedSkippedRangeOffsets = (const PPSkippedRange*)Blob.data();
+ assert(Blob.size() % sizeof(PPSkippedRange) == 0);
+ F.NumPreprocessedSkippedRanges = Blob.size() / sizeof(PPSkippedRange);
+
+ if (!PP.getPreprocessingRecord())
+ PP.createPreprocessingRecord();
+ if (!PP.getPreprocessingRecord()->getExternalSource())
+ PP.getPreprocessingRecord()->SetExternalSource(*this);
+ F.BasePreprocessedSkippedRangeID = PP.getPreprocessingRecord()
+ ->allocateSkippedRanges(F.NumPreprocessedSkippedRanges);
+
+ if (F.NumPreprocessedSkippedRanges > 0)
+ GlobalSkippedRangeMap.insert(
+ std::make_pair(F.BasePreprocessedSkippedRangeID, &F));
+ break;
+ }
+
case DECL_UPDATE_OFFSETS:
if (Record.size() % 2 != 0) {
Error("invalid DECL_UPDATE_OFFSETS block in AST file");
@@ -5387,6 +5405,20 @@ ASTReader::getModuleFileLevelDecls(ModuleFile &Mod) {
Mod.FileSortedDecls + Mod.NumFileSortedDecls));
}
+SourceRange ASTReader::ReadSkippedRange(unsigned GlobalIndex) {
+ auto I = GlobalSkippedRangeMap.find(GlobalIndex);
+ assert(I != GlobalSkippedRangeMap.end() &&
+ "Corrupted global skipped range map");
+ ModuleFile *M = I->second;
+ unsigned LocalIndex = GlobalIndex - M->BasePreprocessedSkippedRangeID;
+ assert(LocalIndex < M->NumPreprocessedSkippedRanges);
+ PPSkippedRange RawRange = M->PreprocessedSkippedRangeOffsets[LocalIndex];
+ SourceRange Range(TranslateSourceLocation(*M, RawRange.getBegin()),
+ TranslateSourceLocation(*M, RawRange.getEnd()));
+ assert(Range.isValid());
+ return Range;
+}
+
PreprocessedEntity *ASTReader::ReadPreprocessedEntity(unsigned Index) {
PreprocessedEntityID PPID = Index+1;
std::pair<ModuleFile *, unsigned> PPInfo = getModulePreprocessedEntity(Index);
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 1e72ced..8d5eda1 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -1104,6 +1104,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(UNUSED_FILESCOPED_DECLS);
RECORD(PPD_ENTITIES_OFFSETS);
RECORD(VTABLE_USES);
+ RECORD(PPD_SKIPPED_RANGES);
RECORD(REFERENCED_SELECTOR_POOL);
RECORD(TU_UPDATE_LEXICAL);
RECORD(SEMA_DECL_REFS);
@@ -2726,6 +2727,26 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec) {
Stream.EmitRecordWithBlob(PPEOffsetAbbrev, Record,
bytes(PreprocessedEntityOffsets));
}
+
+ // Write the skipped region table for the preprocessing record.
+ ArrayRef<SourceRange> SkippedRanges = PPRec.getSkippedRanges();
+ if (SkippedRanges.size() > 0) {
+ std::vector<PPSkippedRange> SerializedSkippedRanges;
+ SerializedSkippedRanges.reserve(SkippedRanges.size());
+ for (auto const& Range : SkippedRanges)
+ SerializedSkippedRanges.emplace_back(Range);
+
+ using namespace llvm;
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(PPD_SKIPPED_RANGES));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob));
+ unsigned PPESkippedRangeAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+
+ Record.clear();
+ Record.push_back(PPD_SKIPPED_RANGES);
+ Stream.EmitRecordWithBlob(PPESkippedRangeAbbrev, Record,
+ bytes(SerializedSkippedRanges));
+ }
}
unsigned ASTWriter::getLocalOrImportedSubmoduleID(Module *Mod) {
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 429fca3..cea3bd9 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -8160,6 +8160,7 @@ CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) {
SourceManager &sm = Ctx.getSourceManager();
FileEntry *fileEntry = static_cast<FileEntry *>(file);
FileID wantedFileID = sm.translateFile(fileEntry);
+ bool isMainFile = wantedFileID == sm.getMainFileID();
const std::vector<SourceRange> &SkippedRanges = ppRec->getSkippedRanges();
std::vector<SourceRange> wantedRanges;
@@ -8167,6 +8168,8 @@ CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) {
i != ei; ++i) {
if (sm.getFileID(i->getBegin()) == wantedFileID || sm.getFileID(i->getEnd()) == wantedFileID)
wantedRanges.push_back(*i);
+ else if (isMainFile && (astUnit->isInPreambleFileID(i->getBegin()) || astUnit->isInPreambleFileID(i->getEnd())))
+ wantedRanges.push_back(*i);
}
skipped->count = wantedRanges.size();
diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index f2a96d6..c0f9eb6 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -572,3 +572,81 @@ TEST_F(LibclangReparseTest, clang_parseTranslationUnit2FullArgv) {
EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
DisplayDiagnostics();
}
+
+TEST_F(LibclangReparseTest, PreprocessorSkippedRanges) {
+ std::string Header = "header.h", Main = "main.cpp";
+ WriteFile(Header,
+ "#ifdef MANGOS\n"
+ "printf(\"mmm\");\n"
+ "#endif");
+ WriteFile(Main,
+ "#include \"header.h\"\n"
+ "#ifdef GUAVA\n"
+ "#endif\n"
+ "#ifdef KIWIS\n"
+ "printf(\"mmm!!\");\n"
+ "#endif");
+
+ for (int i = 0; i != 3; ++i) {
+ unsigned flags = TUFlags | CXTranslationUnit_PrecompiledPreamble;
+ if (i == 2)
+ flags |= CXTranslationUnit_CreatePreambleOnFirstParse;
+
+ // parse once
+ ClangTU = clang_parseTranslationUnit(Index, Main.c_str(), nullptr, 0,
+ nullptr, 0, flags);
+ if (i != 0) {
+ // reparse
+ ASSERT_TRUE(ReparseTU(0, nullptr /* No unsaved files. */));
+ }
+
+ // Check all ranges are there
+ CXSourceRangeList *Ranges = clang_getAllSkippedRanges(ClangTU);
+ EXPECT_EQ(3U, Ranges->count);
+
+ CXSourceLocation cxl;
+ unsigned line;
+ cxl = clang_getRangeStart(Ranges->ranges[0]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(1U, line);
+ cxl = clang_getRangeEnd(Ranges->ranges[0]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(3U, line);
+
+ cxl = clang_getRangeStart(Ranges->ranges[1]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(2U, line);
+ cxl = clang_getRangeEnd(Ranges->ranges[1]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(3U, line);
+
+ cxl = clang_getRangeStart(Ranges->ranges[2]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(4U, line);
+ cxl = clang_getRangeEnd(Ranges->ranges[2]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(6U, line);
+
+ clang_disposeSourceRangeList(Ranges);
+
+ // Check obtaining ranges by each file works
+ CXFile cxf = clang_getFile(ClangTU, Header.c_str());
+ Ranges = clang_getSkippedRanges(ClangTU, cxf);
+ EXPECT_EQ(1U, Ranges->count);
+ cxl = clang_getRangeStart(Ranges->ranges[0]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(1U, line);
+ clang_disposeSourceRangeList(Ranges);
+
+ cxf = clang_getFile(ClangTU, Main.c_str());
+ Ranges = clang_getSkippedRanges(ClangTU, cxf);
+ EXPECT_EQ(2U, Ranges->count);
+ cxl = clang_getRangeStart(Ranges->ranges[0]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(2U, line);
+ cxl = clang_getRangeStart(Ranges->ranges[1]);
+ clang_getSpellingLocation(cxl, nullptr, &line, nullptr, nullptr);
+ EXPECT_EQ(4U, line);
+ clang_disposeSourceRangeList(Ranges);
+ }
+}