summaryrefslogtreecommitdiff
path: root/clang/lib/Lex/PreprocessingRecord.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Lex/PreprocessingRecord.cpp')
-rw-r--r--clang/lib/Lex/PreprocessingRecord.cpp516
1 files changed, 516 insertions, 0 deletions
diff --git a/clang/lib/Lex/PreprocessingRecord.cpp b/clang/lib/Lex/PreprocessingRecord.cpp
new file mode 100644
index 000000000000..115256db4809
--- /dev/null
+++ b/clang/lib/Lex/PreprocessingRecord.cpp
@@ -0,0 +1,516 @@
+//===- PreprocessingRecord.cpp - Record of Preprocessing ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the PreprocessingRecord class, which maintains a record
+// of what occurred during preprocessing, and its helpers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Lex/PreprocessingRecord.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Token.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Capacity.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+#include <iterator>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+
+ExternalPreprocessingRecordSource::~ExternalPreprocessingRecordSource() =
+ default;
+
+InclusionDirective::InclusionDirective(PreprocessingRecord &PPRec,
+ InclusionKind Kind, StringRef FileName,
+ bool InQuotes, bool ImportedModule,
+ const FileEntry *File, SourceRange Range)
+ : PreprocessingDirective(InclusionDirectiveKind, Range), InQuotes(InQuotes),
+ Kind(Kind), ImportedModule(ImportedModule), File(File) {
+ char *Memory = (char *)PPRec.Allocate(FileName.size() + 1, alignof(char));
+ memcpy(Memory, FileName.data(), FileName.size());
+ Memory[FileName.size()] = 0;
+ this->FileName = StringRef(Memory, FileName.size());
+}
+
+PreprocessingRecord::PreprocessingRecord(SourceManager &SM) : SourceMgr(SM) {}
+
+/// Returns a pair of [Begin, End) iterators of preprocessed entities
+/// that source range \p Range encompasses.
+llvm::iterator_range<PreprocessingRecord::iterator>
+PreprocessingRecord::getPreprocessedEntitiesInRange(SourceRange Range) {
+ if (Range.isInvalid())
+ return llvm::make_range(iterator(), iterator());
+
+ if (CachedRangeQuery.Range == Range) {
+ return llvm::make_range(iterator(this, CachedRangeQuery.Result.first),
+ iterator(this, CachedRangeQuery.Result.second));
+ }
+
+ std::pair<int, int> Res = getPreprocessedEntitiesInRangeSlow(Range);
+
+ CachedRangeQuery.Range = Range;
+ CachedRangeQuery.Result = Res;
+
+ return llvm::make_range(iterator(this, Res.first),
+ iterator(this, Res.second));
+}
+
+static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID,
+ SourceManager &SM) {
+ assert(FID.isValid());
+ if (!PPE)
+ return false;
+
+ SourceLocation Loc = PPE->getSourceRange().getBegin();
+ if (Loc.isInvalid())
+ return false;
+
+ return SM.isInFileID(SM.getFileLoc(Loc), FID);
+}
+
+/// Returns true if the preprocessed entity that \arg PPEI iterator
+/// points to is coming from the file \arg FID.
+///
+/// Can be used to avoid implicit deserializations of preallocated
+/// preprocessed entities if we only care about entities of a specific file
+/// and not from files \#included in the range given at
+/// \see getPreprocessedEntitiesInRange.
+bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) {
+ if (FID.isInvalid())
+ return false;
+
+ int Pos = std::distance(iterator(this, 0), PPEI);
+ if (Pos < 0) {
+ if (unsigned(-Pos-1) >= LoadedPreprocessedEntities.size()) {
+ assert(0 && "Out-of bounds loaded preprocessed entity");
+ return false;
+ }
+ assert(ExternalSource && "No external source to load from");
+ unsigned LoadedIndex = LoadedPreprocessedEntities.size()+Pos;
+ if (PreprocessedEntity *PPE = LoadedPreprocessedEntities[LoadedIndex])
+ return isPreprocessedEntityIfInFileID(PPE, FID, SourceMgr);
+
+ // See if the external source can see if the entity is in the file without
+ // deserializing it.
+ Optional<bool> IsInFile =
+ ExternalSource->isPreprocessedEntityInFileID(LoadedIndex, FID);
+ if (IsInFile.hasValue())
+ return IsInFile.getValue();
+
+ // The external source did not provide a definite answer, go and deserialize
+ // the entity to check it.
+ return isPreprocessedEntityIfInFileID(
+ getLoadedPreprocessedEntity(LoadedIndex),
+ FID, SourceMgr);
+ }
+
+ if (unsigned(Pos) >= PreprocessedEntities.size()) {
+ assert(0 && "Out-of bounds local preprocessed entity");
+ return false;
+ }
+ return isPreprocessedEntityIfInFileID(PreprocessedEntities[Pos],
+ FID, SourceMgr);
+}
+
+/// Returns a pair of [Begin, End) iterators of preprocessed entities
+/// that source range \arg R encompasses.
+std::pair<int, int>
+PreprocessingRecord::getPreprocessedEntitiesInRangeSlow(SourceRange Range) {
+ assert(Range.isValid());
+ assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin()));
+
+ std::pair<unsigned, unsigned>
+ Local = findLocalPreprocessedEntitiesInRange(Range);
+
+ // Check if range spans local entities.
+ if (!ExternalSource || SourceMgr.isLocalSourceLocation(Range.getBegin()))
+ return std::make_pair(Local.first, Local.second);
+
+ std::pair<unsigned, unsigned>
+ Loaded = ExternalSource->findPreprocessedEntitiesInRange(Range);
+
+ // Check if range spans local entities.
+ if (Loaded.first == Loaded.second)
+ return std::make_pair(Local.first, Local.second);
+
+ unsigned TotalLoaded = LoadedPreprocessedEntities.size();
+
+ // Check if range spans loaded entities.
+ if (Local.first == Local.second)
+ return std::make_pair(int(Loaded.first)-TotalLoaded,
+ int(Loaded.second)-TotalLoaded);
+
+ // Range spands loaded and local entities.
+ return std::make_pair(int(Loaded.first)-TotalLoaded, Local.second);
+}
+
+std::pair<unsigned, unsigned>
+PreprocessingRecord::findLocalPreprocessedEntitiesInRange(
+ SourceRange Range) const {
+ if (Range.isInvalid())
+ return std::make_pair(0,0);
+ assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin()));
+
+ unsigned Begin = findBeginLocalPreprocessedEntity(Range.getBegin());
+ unsigned End = findEndLocalPreprocessedEntity(Range.getEnd());
+ return std::make_pair(Begin, End);
+}
+
+namespace {
+
+template <SourceLocation (SourceRange::*getRangeLoc)() const>
+struct PPEntityComp {
+ const SourceManager &SM;
+
+ explicit PPEntityComp(const SourceManager &SM) : SM(SM) {}
+
+ bool operator()(PreprocessedEntity *L, PreprocessedEntity *R) const {
+ SourceLocation LHS = getLoc(L);
+ SourceLocation RHS = getLoc(R);
+ return SM.isBeforeInTranslationUnit(LHS, RHS);
+ }
+
+ bool operator()(PreprocessedEntity *L, SourceLocation RHS) const {
+ SourceLocation LHS = getLoc(L);
+ return SM.isBeforeInTranslationUnit(LHS, RHS);
+ }
+
+ bool operator()(SourceLocation LHS, PreprocessedEntity *R) const {
+ SourceLocation RHS = getLoc(R);
+ return SM.isBeforeInTranslationUnit(LHS, RHS);
+ }
+
+ SourceLocation getLoc(PreprocessedEntity *PPE) const {
+ SourceRange Range = PPE->getSourceRange();
+ return (Range.*getRangeLoc)();
+ }
+};
+
+} // namespace
+
+unsigned PreprocessingRecord::findBeginLocalPreprocessedEntity(
+ SourceLocation Loc) const {
+ if (SourceMgr.isLoadedSourceLocation(Loc))
+ return 0;
+
+ size_t Count = PreprocessedEntities.size();
+ size_t Half;
+ std::vector<PreprocessedEntity *>::const_iterator
+ First = PreprocessedEntities.begin();
+ std::vector<PreprocessedEntity *>::const_iterator I;
+
+ // Do a binary search manually instead of using std::lower_bound because
+ // The end locations of entities may be unordered (when a macro expansion
+ // is inside another macro argument), but for this case it is not important
+ // whether we get the first macro expansion or its containing macro.
+ while (Count > 0) {
+ Half = Count/2;
+ I = First;
+ std::advance(I, Half);
+ if (SourceMgr.isBeforeInTranslationUnit((*I)->getSourceRange().getEnd(),
+ Loc)){
+ First = I;
+ ++First;
+ Count = Count - Half - 1;
+ } else
+ Count = Half;
+ }
+
+ return First - PreprocessedEntities.begin();
+}
+
+unsigned
+PreprocessingRecord::findEndLocalPreprocessedEntity(SourceLocation Loc) const {
+ if (SourceMgr.isLoadedSourceLocation(Loc))
+ return 0;
+
+ auto I = llvm::upper_bound(PreprocessedEntities, Loc,
+ PPEntityComp<&SourceRange::getBegin>(SourceMgr));
+ return I - PreprocessedEntities.begin();
+}
+
+PreprocessingRecord::PPEntityID
+PreprocessingRecord::addPreprocessedEntity(PreprocessedEntity *Entity) {
+ assert(Entity);
+ SourceLocation BeginLoc = Entity->getSourceRange().getBegin();
+
+ if (isa<MacroDefinitionRecord>(Entity)) {
+ assert((PreprocessedEntities.empty() ||
+ !SourceMgr.isBeforeInTranslationUnit(
+ BeginLoc,
+ PreprocessedEntities.back()->getSourceRange().getBegin())) &&
+ "a macro definition was encountered out-of-order");
+ PreprocessedEntities.push_back(Entity);
+ return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false);
+ }
+
+ // Check normal case, this entity begin location is after the previous one.
+ if (PreprocessedEntities.empty() ||
+ !SourceMgr.isBeforeInTranslationUnit(BeginLoc,
+ PreprocessedEntities.back()->getSourceRange().getBegin())) {
+ PreprocessedEntities.push_back(Entity);
+ return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false);
+ }
+
+ // The entity's location is not after the previous one; this can happen with
+ // include directives that form the filename using macros, e.g:
+ // "#include MACRO(STUFF)"
+ // or with macro expansions inside macro arguments where the arguments are
+ // not expanded in the same order as listed, e.g:
+ // \code
+ // #define M1 1
+ // #define M2 2
+ // #define FM(x,y) y x
+ // FM(M1, M2)
+ // \endcode
+
+ using pp_iter = std::vector<PreprocessedEntity *>::iterator;
+
+ // Usually there are few macro expansions when defining the filename, do a
+ // linear search for a few entities.
+ unsigned count = 0;
+ for (pp_iter RI = PreprocessedEntities.end(),
+ Begin = PreprocessedEntities.begin();
+ RI != Begin && count < 4; --RI, ++count) {
+ pp_iter I = RI;
+ --I;
+ if (!SourceMgr.isBeforeInTranslationUnit(BeginLoc,
+ (*I)->getSourceRange().getBegin())) {
+ pp_iter insertI = PreprocessedEntities.insert(RI, Entity);
+ return getPPEntityID(insertI - PreprocessedEntities.begin(),
+ /*isLoaded=*/false);
+ }
+ }
+
+ // Linear search unsuccessful. Do a binary search.
+ pp_iter I =
+ llvm::upper_bound(PreprocessedEntities, BeginLoc,
+ PPEntityComp<&SourceRange::getBegin>(SourceMgr));
+ pp_iter insertI = PreprocessedEntities.insert(I, Entity);
+ return getPPEntityID(insertI - PreprocessedEntities.begin(),
+ /*isLoaded=*/false);
+}
+
+void PreprocessingRecord::SetExternalSource(
+ ExternalPreprocessingRecordSource &Source) {
+ assert(!ExternalSource &&
+ "Preprocessing record already has an external source");
+ ExternalSource = &Source;
+}
+
+unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) {
+ unsigned Result = LoadedPreprocessedEntities.size();
+ LoadedPreprocessedEntities.resize(LoadedPreprocessedEntities.size()
+ + 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;
+}
+
+/// Retrieve the preprocessed entity at the given ID.
+PreprocessedEntity *PreprocessingRecord::getPreprocessedEntity(PPEntityID PPID){
+ if (PPID.ID < 0) {
+ unsigned Index = -PPID.ID - 1;
+ assert(Index < LoadedPreprocessedEntities.size() &&
+ "Out-of bounds loaded preprocessed entity");
+ return getLoadedPreprocessedEntity(Index);
+ }
+
+ if (PPID.ID == 0)
+ return nullptr;
+ unsigned Index = PPID.ID - 1;
+ assert(Index < PreprocessedEntities.size() &&
+ "Out-of bounds local preprocessed entity");
+ return PreprocessedEntities[Index];
+}
+
+/// Retrieve the loaded preprocessed entity at the given index.
+PreprocessedEntity *
+PreprocessingRecord::getLoadedPreprocessedEntity(unsigned Index) {
+ assert(Index < LoadedPreprocessedEntities.size() &&
+ "Out-of bounds loaded preprocessed entity");
+ assert(ExternalSource && "No external source to load from");
+ PreprocessedEntity *&Entity = LoadedPreprocessedEntities[Index];
+ if (!Entity) {
+ Entity = ExternalSource->ReadPreprocessedEntity(Index);
+ if (!Entity) // Failed to load.
+ Entity = new (*this)
+ PreprocessedEntity(PreprocessedEntity::InvalidKind, SourceRange());
+ }
+ return Entity;
+}
+
+MacroDefinitionRecord *
+PreprocessingRecord::findMacroDefinition(const MacroInfo *MI) {
+ llvm::DenseMap<const MacroInfo *, MacroDefinitionRecord *>::iterator Pos =
+ MacroDefinitions.find(MI);
+ if (Pos == MacroDefinitions.end())
+ return nullptr;
+
+ return Pos->second;
+}
+
+void PreprocessingRecord::addMacroExpansion(const Token &Id,
+ const MacroInfo *MI,
+ SourceRange Range) {
+ // We don't record nested macro expansions.
+ if (Id.getLocation().isMacroID())
+ return;
+
+ if (MI->isBuiltinMacro())
+ addPreprocessedEntity(new (*this)
+ MacroExpansion(Id.getIdentifierInfo(), Range));
+ else if (MacroDefinitionRecord *Def = findMacroDefinition(MI))
+ addPreprocessedEntity(new (*this) MacroExpansion(Def, Range));
+}
+
+void PreprocessingRecord::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ // This is not actually a macro expansion but record it as a macro reference.
+ if (MD)
+ addMacroExpansion(MacroNameTok, MD.getMacroInfo(),
+ MacroNameTok.getLocation());
+}
+
+void PreprocessingRecord::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+ const MacroDefinition &MD) {
+ // This is not actually a macro expansion but record it as a macro reference.
+ if (MD)
+ addMacroExpansion(MacroNameTok, MD.getMacroInfo(),
+ MacroNameTok.getLocation());
+}
+
+void PreprocessingRecord::Defined(const Token &MacroNameTok,
+ const MacroDefinition &MD,
+ SourceRange Range) {
+ // This is not actually a macro expansion but record it as a macro reference.
+ if (MD)
+ addMacroExpansion(MacroNameTok, MD.getMacroInfo(),
+ MacroNameTok.getLocation());
+}
+
+void PreprocessingRecord::SourceRangeSkipped(SourceRange Range,
+ SourceLocation EndifLoc) {
+ assert(Range.isValid());
+ SkippedRanges.emplace_back(Range.getBegin(), EndifLoc);
+}
+
+void PreprocessingRecord::MacroExpands(const Token &Id,
+ const MacroDefinition &MD,
+ SourceRange Range,
+ const MacroArgs *Args) {
+ addMacroExpansion(Id, MD.getMacroInfo(), Range);
+}
+
+void PreprocessingRecord::MacroDefined(const Token &Id,
+ const MacroDirective *MD) {
+ const MacroInfo *MI = MD->getMacroInfo();
+ SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());
+ MacroDefinitionRecord *Def =
+ new (*this) MacroDefinitionRecord(Id.getIdentifierInfo(), R);
+ addPreprocessedEntity(Def);
+ MacroDefinitions[MI] = Def;
+}
+
+void PreprocessingRecord::MacroUndefined(const Token &Id,
+ const MacroDefinition &MD,
+ const MacroDirective *Undef) {
+ MD.forAllDefinitions([&](MacroInfo *MI) { MacroDefinitions.erase(MI); });
+}
+
+void PreprocessingRecord::InclusionDirective(
+ SourceLocation HashLoc,
+ const Token &IncludeTok,
+ StringRef FileName,
+ bool IsAngled,
+ CharSourceRange FilenameRange,
+ const FileEntry *File,
+ StringRef SearchPath,
+ StringRef RelativePath,
+ const Module *Imported,
+ SrcMgr::CharacteristicKind FileType) {
+ InclusionDirective::InclusionKind Kind = InclusionDirective::Include;
+
+ switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) {
+ case tok::pp_include:
+ Kind = InclusionDirective::Include;
+ break;
+
+ case tok::pp_import:
+ Kind = InclusionDirective::Import;
+ break;
+
+ case tok::pp_include_next:
+ Kind = InclusionDirective::IncludeNext;
+ break;
+
+ case tok::pp___include_macros:
+ Kind = InclusionDirective::IncludeMacros;
+ break;
+
+ default:
+ llvm_unreachable("Unknown include directive kind");
+ }
+
+ SourceLocation EndLoc;
+ if (!IsAngled) {
+ EndLoc = FilenameRange.getBegin();
+ } else {
+ EndLoc = FilenameRange.getEnd();
+ if (FilenameRange.isCharRange())
+ EndLoc = EndLoc.getLocWithOffset(-1); // the InclusionDirective expects
+ // a token range.
+ }
+ clang::InclusionDirective *ID =
+ new (*this) clang::InclusionDirective(*this, Kind, FileName, !IsAngled,
+ (bool)Imported, File,
+ SourceRange(HashLoc, EndLoc));
+ addPreprocessedEntity(ID);
+}
+
+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(SkippedRanges);
+}