| //===- 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::Elifdef(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::Elifndef(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); |
| } |