Path: blob/main/contrib/llvm-project/clang/lib/Lex/PreprocessingRecord.cpp
35234 views
//===- PreprocessingRecord.cpp - Record of Preprocessing ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the PreprocessingRecord class, which maintains a record9// of what occurred during preprocessing, and its helpers.10//11//===----------------------------------------------------------------------===//1213#include "clang/Lex/PreprocessingRecord.h"14#include "clang/Basic/IdentifierTable.h"15#include "clang/Basic/LLVM.h"16#include "clang/Basic/SourceLocation.h"17#include "clang/Basic/SourceManager.h"18#include "clang/Basic/TokenKinds.h"19#include "clang/Lex/MacroInfo.h"20#include "clang/Lex/Token.h"21#include "llvm/ADT/DenseMap.h"22#include "llvm/ADT/StringRef.h"23#include "llvm/ADT/iterator_range.h"24#include "llvm/Support/Capacity.h"25#include "llvm/Support/Casting.h"26#include "llvm/Support/ErrorHandling.h"27#include <algorithm>28#include <cassert>29#include <cstddef>30#include <cstring>31#include <iterator>32#include <optional>33#include <utility>34#include <vector>3536using namespace clang;3738ExternalPreprocessingRecordSource::~ExternalPreprocessingRecordSource() =39default;4041InclusionDirective::InclusionDirective(PreprocessingRecord &PPRec,42InclusionKind Kind, StringRef FileName,43bool InQuotes, bool ImportedModule,44OptionalFileEntryRef File,45SourceRange Range)46: PreprocessingDirective(InclusionDirectiveKind, Range), InQuotes(InQuotes),47Kind(Kind), ImportedModule(ImportedModule), File(File) {48char *Memory = (char *)PPRec.Allocate(FileName.size() + 1, alignof(char));49memcpy(Memory, FileName.data(), FileName.size());50Memory[FileName.size()] = 0;51this->FileName = StringRef(Memory, FileName.size());52}5354PreprocessingRecord::PreprocessingRecord(SourceManager &SM) : SourceMgr(SM) {}5556/// Returns a pair of [Begin, End) iterators of preprocessed entities57/// that source range \p Range encompasses.58llvm::iterator_range<PreprocessingRecord::iterator>59PreprocessingRecord::getPreprocessedEntitiesInRange(SourceRange Range) {60if (Range.isInvalid())61return llvm::make_range(iterator(), iterator());6263if (CachedRangeQuery.Range == Range) {64return llvm::make_range(iterator(this, CachedRangeQuery.Result.first),65iterator(this, CachedRangeQuery.Result.second));66}6768std::pair<int, int> Res = getPreprocessedEntitiesInRangeSlow(Range);6970CachedRangeQuery.Range = Range;71CachedRangeQuery.Result = Res;7273return llvm::make_range(iterator(this, Res.first),74iterator(this, Res.second));75}7677static bool isPreprocessedEntityIfInFileID(PreprocessedEntity *PPE, FileID FID,78SourceManager &SM) {79assert(FID.isValid());80if (!PPE)81return false;8283SourceLocation Loc = PPE->getSourceRange().getBegin();84if (Loc.isInvalid())85return false;8687return SM.isInFileID(SM.getFileLoc(Loc), FID);88}8990/// Returns true if the preprocessed entity that \arg PPEI iterator91/// points to is coming from the file \arg FID.92///93/// Can be used to avoid implicit deserializations of preallocated94/// preprocessed entities if we only care about entities of a specific file95/// and not from files \#included in the range given at96/// \see getPreprocessedEntitiesInRange.97bool PreprocessingRecord::isEntityInFileID(iterator PPEI, FileID FID) {98if (FID.isInvalid())99return false;100101int Pos = std::distance(iterator(this, 0), PPEI);102if (Pos < 0) {103if (unsigned(-Pos-1) >= LoadedPreprocessedEntities.size()) {104assert(0 && "Out-of bounds loaded preprocessed entity");105return false;106}107assert(ExternalSource && "No external source to load from");108unsigned LoadedIndex = LoadedPreprocessedEntities.size()+Pos;109if (PreprocessedEntity *PPE = LoadedPreprocessedEntities[LoadedIndex])110return isPreprocessedEntityIfInFileID(PPE, FID, SourceMgr);111112// See if the external source can see if the entity is in the file without113// deserializing it.114if (std::optional<bool> IsInFile =115ExternalSource->isPreprocessedEntityInFileID(LoadedIndex, FID))116return *IsInFile;117118// The external source did not provide a definite answer, go and deserialize119// the entity to check it.120return isPreprocessedEntityIfInFileID(121getLoadedPreprocessedEntity(LoadedIndex),122FID, SourceMgr);123}124125if (unsigned(Pos) >= PreprocessedEntities.size()) {126assert(0 && "Out-of bounds local preprocessed entity");127return false;128}129return isPreprocessedEntityIfInFileID(PreprocessedEntities[Pos],130FID, SourceMgr);131}132133/// Returns a pair of [Begin, End) iterators of preprocessed entities134/// that source range \arg R encompasses.135std::pair<int, int>136PreprocessingRecord::getPreprocessedEntitiesInRangeSlow(SourceRange Range) {137assert(Range.isValid());138assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin()));139140std::pair<unsigned, unsigned>141Local = findLocalPreprocessedEntitiesInRange(Range);142143// Check if range spans local entities.144if (!ExternalSource || SourceMgr.isLocalSourceLocation(Range.getBegin()))145return std::make_pair(Local.first, Local.second);146147std::pair<unsigned, unsigned>148Loaded = ExternalSource->findPreprocessedEntitiesInRange(Range);149150// Check if range spans local entities.151if (Loaded.first == Loaded.second)152return std::make_pair(Local.first, Local.second);153154unsigned TotalLoaded = LoadedPreprocessedEntities.size();155156// Check if range spans loaded entities.157if (Local.first == Local.second)158return std::make_pair(int(Loaded.first)-TotalLoaded,159int(Loaded.second)-TotalLoaded);160161// Range spands loaded and local entities.162return std::make_pair(int(Loaded.first)-TotalLoaded, Local.second);163}164165std::pair<unsigned, unsigned>166PreprocessingRecord::findLocalPreprocessedEntitiesInRange(167SourceRange Range) const {168if (Range.isInvalid())169return std::make_pair(0,0);170assert(!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(),Range.getBegin()));171172unsigned Begin = findBeginLocalPreprocessedEntity(Range.getBegin());173unsigned End = findEndLocalPreprocessedEntity(Range.getEnd());174return std::make_pair(Begin, End);175}176177namespace {178179template <SourceLocation (SourceRange::*getRangeLoc)() const>180struct PPEntityComp {181const SourceManager &SM;182183explicit PPEntityComp(const SourceManager &SM) : SM(SM) {}184185bool operator()(PreprocessedEntity *L, PreprocessedEntity *R) const {186SourceLocation LHS = getLoc(L);187SourceLocation RHS = getLoc(R);188return SM.isBeforeInTranslationUnit(LHS, RHS);189}190191bool operator()(PreprocessedEntity *L, SourceLocation RHS) const {192SourceLocation LHS = getLoc(L);193return SM.isBeforeInTranslationUnit(LHS, RHS);194}195196bool operator()(SourceLocation LHS, PreprocessedEntity *R) const {197SourceLocation RHS = getLoc(R);198return SM.isBeforeInTranslationUnit(LHS, RHS);199}200201SourceLocation getLoc(PreprocessedEntity *PPE) const {202SourceRange Range = PPE->getSourceRange();203return (Range.*getRangeLoc)();204}205};206207} // namespace208209unsigned PreprocessingRecord::findBeginLocalPreprocessedEntity(210SourceLocation Loc) const {211if (SourceMgr.isLoadedSourceLocation(Loc))212return 0;213214size_t Count = PreprocessedEntities.size();215size_t Half;216std::vector<PreprocessedEntity *>::const_iterator217First = PreprocessedEntities.begin();218std::vector<PreprocessedEntity *>::const_iterator I;219220// Do a binary search manually instead of using std::lower_bound because221// The end locations of entities may be unordered (when a macro expansion222// is inside another macro argument), but for this case it is not important223// whether we get the first macro expansion or its containing macro.224while (Count > 0) {225Half = Count/2;226I = First;227std::advance(I, Half);228if (SourceMgr.isBeforeInTranslationUnit((*I)->getSourceRange().getEnd(),229Loc)){230First = I;231++First;232Count = Count - Half - 1;233} else234Count = Half;235}236237return First - PreprocessedEntities.begin();238}239240unsigned241PreprocessingRecord::findEndLocalPreprocessedEntity(SourceLocation Loc) const {242if (SourceMgr.isLoadedSourceLocation(Loc))243return 0;244245auto I = llvm::upper_bound(PreprocessedEntities, Loc,246PPEntityComp<&SourceRange::getBegin>(SourceMgr));247return I - PreprocessedEntities.begin();248}249250PreprocessingRecord::PPEntityID251PreprocessingRecord::addPreprocessedEntity(PreprocessedEntity *Entity) {252assert(Entity);253SourceLocation BeginLoc = Entity->getSourceRange().getBegin();254255if (isa<MacroDefinitionRecord>(Entity)) {256assert((PreprocessedEntities.empty() ||257!SourceMgr.isBeforeInTranslationUnit(258BeginLoc,259PreprocessedEntities.back()->getSourceRange().getBegin())) &&260"a macro definition was encountered out-of-order");261PreprocessedEntities.push_back(Entity);262return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false);263}264265// Check normal case, this entity begin location is after the previous one.266if (PreprocessedEntities.empty() ||267!SourceMgr.isBeforeInTranslationUnit(BeginLoc,268PreprocessedEntities.back()->getSourceRange().getBegin())) {269PreprocessedEntities.push_back(Entity);270return getPPEntityID(PreprocessedEntities.size()-1, /*isLoaded=*/false);271}272273// The entity's location is not after the previous one; this can happen with274// include directives that form the filename using macros, e.g:275// "#include MACRO(STUFF)"276// or with macro expansions inside macro arguments where the arguments are277// not expanded in the same order as listed, e.g:278// \code279// #define M1 1280// #define M2 2281// #define FM(x,y) y x282// FM(M1, M2)283// \endcode284285using pp_iter = std::vector<PreprocessedEntity *>::iterator;286287// Usually there are few macro expansions when defining the filename, do a288// linear search for a few entities.289unsigned count = 0;290for (pp_iter RI = PreprocessedEntities.end(),291Begin = PreprocessedEntities.begin();292RI != Begin && count < 4; --RI, ++count) {293pp_iter I = RI;294--I;295if (!SourceMgr.isBeforeInTranslationUnit(BeginLoc,296(*I)->getSourceRange().getBegin())) {297pp_iter insertI = PreprocessedEntities.insert(RI, Entity);298return getPPEntityID(insertI - PreprocessedEntities.begin(),299/*isLoaded=*/false);300}301}302303// Linear search unsuccessful. Do a binary search.304pp_iter I =305llvm::upper_bound(PreprocessedEntities, BeginLoc,306PPEntityComp<&SourceRange::getBegin>(SourceMgr));307pp_iter insertI = PreprocessedEntities.insert(I, Entity);308return getPPEntityID(insertI - PreprocessedEntities.begin(),309/*isLoaded=*/false);310}311312void PreprocessingRecord::SetExternalSource(313ExternalPreprocessingRecordSource &Source) {314assert(!ExternalSource &&315"Preprocessing record already has an external source");316ExternalSource = &Source;317}318319unsigned PreprocessingRecord::allocateLoadedEntities(unsigned NumEntities) {320unsigned Result = LoadedPreprocessedEntities.size();321LoadedPreprocessedEntities.resize(LoadedPreprocessedEntities.size()322+ NumEntities);323return Result;324}325326unsigned PreprocessingRecord::allocateSkippedRanges(unsigned NumRanges) {327unsigned Result = SkippedRanges.size();328SkippedRanges.resize(SkippedRanges.size() + NumRanges);329SkippedRangesAllLoaded = false;330return Result;331}332333void PreprocessingRecord::ensureSkippedRangesLoaded() {334if (SkippedRangesAllLoaded || !ExternalSource)335return;336for (unsigned Index = 0; Index != SkippedRanges.size(); ++Index) {337if (SkippedRanges[Index].isInvalid())338SkippedRanges[Index] = ExternalSource->ReadSkippedRange(Index);339}340SkippedRangesAllLoaded = true;341}342343void PreprocessingRecord::RegisterMacroDefinition(MacroInfo *Macro,344MacroDefinitionRecord *Def) {345MacroDefinitions[Macro] = Def;346}347348/// Retrieve the preprocessed entity at the given ID.349PreprocessedEntity *PreprocessingRecord::getPreprocessedEntity(PPEntityID PPID){350if (PPID.ID < 0) {351unsigned Index = -PPID.ID - 1;352assert(Index < LoadedPreprocessedEntities.size() &&353"Out-of bounds loaded preprocessed entity");354return getLoadedPreprocessedEntity(Index);355}356357if (PPID.ID == 0)358return nullptr;359unsigned Index = PPID.ID - 1;360assert(Index < PreprocessedEntities.size() &&361"Out-of bounds local preprocessed entity");362return PreprocessedEntities[Index];363}364365/// Retrieve the loaded preprocessed entity at the given index.366PreprocessedEntity *367PreprocessingRecord::getLoadedPreprocessedEntity(unsigned Index) {368assert(Index < LoadedPreprocessedEntities.size() &&369"Out-of bounds loaded preprocessed entity");370assert(ExternalSource && "No external source to load from");371PreprocessedEntity *&Entity = LoadedPreprocessedEntities[Index];372if (!Entity) {373Entity = ExternalSource->ReadPreprocessedEntity(Index);374if (!Entity) // Failed to load.375Entity = new (*this)376PreprocessedEntity(PreprocessedEntity::InvalidKind, SourceRange());377}378return Entity;379}380381MacroDefinitionRecord *382PreprocessingRecord::findMacroDefinition(const MacroInfo *MI) {383return MacroDefinitions.lookup(MI);384}385386void PreprocessingRecord::addMacroExpansion(const Token &Id,387const MacroInfo *MI,388SourceRange Range) {389// We don't record nested macro expansions.390if (Id.getLocation().isMacroID())391return;392393if (MI->isBuiltinMacro())394addPreprocessedEntity(new (*this)395MacroExpansion(Id.getIdentifierInfo(), Range));396else if (MacroDefinitionRecord *Def = findMacroDefinition(MI))397addPreprocessedEntity(new (*this) MacroExpansion(Def, Range));398}399400void PreprocessingRecord::Ifdef(SourceLocation Loc, const Token &MacroNameTok,401const MacroDefinition &MD) {402// This is not actually a macro expansion but record it as a macro reference.403if (MD)404addMacroExpansion(MacroNameTok, MD.getMacroInfo(),405MacroNameTok.getLocation());406}407408void PreprocessingRecord::Elifdef(SourceLocation Loc, const Token &MacroNameTok,409const MacroDefinition &MD) {410// This is not actually a macro expansion but record it as a macro reference.411if (MD)412addMacroExpansion(MacroNameTok, MD.getMacroInfo(),413MacroNameTok.getLocation());414}415416void PreprocessingRecord::Ifndef(SourceLocation Loc, const Token &MacroNameTok,417const MacroDefinition &MD) {418// This is not actually a macro expansion but record it as a macro reference.419if (MD)420addMacroExpansion(MacroNameTok, MD.getMacroInfo(),421MacroNameTok.getLocation());422}423424void PreprocessingRecord::Elifndef(SourceLocation Loc,425const Token &MacroNameTok,426const MacroDefinition &MD) {427// This is not actually a macro expansion but record it as a macro reference.428if (MD)429addMacroExpansion(MacroNameTok, MD.getMacroInfo(),430MacroNameTok.getLocation());431}432433void PreprocessingRecord::Defined(const Token &MacroNameTok,434const MacroDefinition &MD,435SourceRange Range) {436// This is not actually a macro expansion but record it as a macro reference.437if (MD)438addMacroExpansion(MacroNameTok, MD.getMacroInfo(),439MacroNameTok.getLocation());440}441442void PreprocessingRecord::SourceRangeSkipped(SourceRange Range,443SourceLocation EndifLoc) {444assert(Range.isValid());445SkippedRanges.emplace_back(Range.getBegin(), EndifLoc);446}447448void PreprocessingRecord::MacroExpands(const Token &Id,449const MacroDefinition &MD,450SourceRange Range,451const MacroArgs *Args) {452addMacroExpansion(Id, MD.getMacroInfo(), Range);453}454455void PreprocessingRecord::MacroDefined(const Token &Id,456const MacroDirective *MD) {457const MacroInfo *MI = MD->getMacroInfo();458SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc());459MacroDefinitionRecord *Def =460new (*this) MacroDefinitionRecord(Id.getIdentifierInfo(), R);461addPreprocessedEntity(Def);462MacroDefinitions[MI] = Def;463}464465void PreprocessingRecord::MacroUndefined(const Token &Id,466const MacroDefinition &MD,467const MacroDirective *Undef) {468MD.forAllDefinitions([&](MacroInfo *MI) { MacroDefinitions.erase(MI); });469}470471void PreprocessingRecord::InclusionDirective(472SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,473bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,474StringRef SearchPath, StringRef RelativePath, const Module *SuggestedModule,475bool ModuleImported, SrcMgr::CharacteristicKind FileType) {476InclusionDirective::InclusionKind Kind = InclusionDirective::Include;477478switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) {479case tok::pp_include:480Kind = InclusionDirective::Include;481break;482483case tok::pp_import:484Kind = InclusionDirective::Import;485break;486487case tok::pp_include_next:488Kind = InclusionDirective::IncludeNext;489break;490491case tok::pp___include_macros:492Kind = InclusionDirective::IncludeMacros;493break;494495default:496llvm_unreachable("Unknown include directive kind");497}498499SourceLocation EndLoc;500if (!IsAngled) {501EndLoc = FilenameRange.getBegin();502} else {503EndLoc = FilenameRange.getEnd();504if (FilenameRange.isCharRange())505EndLoc = EndLoc.getLocWithOffset(-1); // the InclusionDirective expects506// a token range.507}508clang::InclusionDirective *ID = new (*this) clang::InclusionDirective(509*this, Kind, FileName, !IsAngled, ModuleImported, File,510SourceRange(HashLoc, EndLoc));511addPreprocessedEntity(ID);512}513514size_t PreprocessingRecord::getTotalMemory() const {515return BumpAlloc.getTotalMemory()516+ llvm::capacity_in_bytes(MacroDefinitions)517+ llvm::capacity_in_bytes(PreprocessedEntities)518+ llvm::capacity_in_bytes(LoadedPreprocessedEntities)519+ llvm::capacity_in_bytes(SkippedRanges);520}521522523