//===- SourceManager.h - Track and cache source files -----------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
/// \file
/// Defines the SourceManager interface.
///
/// There are three different types of locations in a %file: a spelling
/// location, an expansion location, and a presumed location.
///
/// Given an example of:
/// \code
/// #define min(x, y) x < y ? x : y
/// \endcode
///
/// and then later on a use of min:
/// \code
/// #line 17
/// return min(a, b);
/// \endcode
///
/// The expansion location is the line in the source code where the macro
/// was expanded (the return statement), the spelling location is the
/// location in the source where the macro was originally defined,
/// and the presumed location is where the line directive states that
/// the line is 17, or any other line.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_BASIC_SOURCEMANAGER_H
#define LLVM_CLANG_BASIC_SOURCEMANAGER_H
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/MemoryBuffer.h"
#include <cassert>
#include <cstddef>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
namespace clang {
class ASTReader;
class ASTWriter;
class FileManager;
class LineTableInfo;
class SourceManager;
/// Public enums and private classes that are part of the
/// SourceManager implementation.
namespace SrcMgr {
/// Indicates whether a file or directory holds normal user code,
/// system code, or system code which is implicitly 'extern "C"' in C++ mode.
///
/// Entire directories can be tagged with this (this is maintained by
/// DirectoryLookup and friends) as can specific FileInfos when a \#pragma
/// system_header is seen or in various other cases.
///
enum CharacteristicKind {
C_User,
C_System,
C_ExternCSystem,
C_User_ModuleMap,
C_System_ModuleMap
};
/// Determine whether a file / directory characteristic is for system code.
inline bool isSystem(CharacteristicKind CK) {
return CK != C_User && CK != C_User_ModuleMap;
}
/// Determine whether a file characteristic is for a module map.
inline bool isModuleMap(CharacteristicKind CK) {
return CK == C_User_ModuleMap || CK == C_System_ModuleMap;
}
/// Mapping of line offsets into a source file. This does not own the storage
/// for the line numbers.
class LineOffsetMapping {
public:
explicit operator bool() const { return Storage; }
unsigned size() const {
assert(Storage);
return Storage[0];
}
ArrayRef<unsigned> getLines() const {
assert(Storage);
return ArrayRef<unsigned>(Storage + 1, Storage + 1 + size());
}
const unsigned *begin() const { return getLines().begin(); }
const unsigned *end() const { return getLines().end(); }
const unsigned &operator[](int I) const { return getLines()[I]; }
static LineOffsetMapping get(llvm::MemoryBufferRef Buffer,
llvm::BumpPtrAllocator &Alloc);
LineOffsetMapping() = default;
LineOffsetMapping(ArrayRef<unsigned> LineOffsets,
llvm::BumpPtrAllocator &Alloc);
private:
/// First element is the size, followed by elements at off-by-one indexes.
unsigned *Storage = nullptr;
};
/// One instance of this struct is kept for every file loaded or used.
///
/// This object owns the MemoryBuffer object.
class alignas(8) ContentCache {
/// The actual buffer containing the characters from the input
/// file.
mutable std::unique_ptr<llvm::MemoryBuffer> Buffer;
public:
/// Reference to the file entry representing this ContentCache.
///
/// This reference does not own the FileEntry object.
///
/// It is possible for this to be NULL if the ContentCache encapsulates
/// an imaginary text buffer.
///
/// FIXME: Turn this into a FileEntryRef and remove Filename.
const FileEntry *OrigEntry;
/// References the file which the contents were actually loaded from.
///
/// Can be different from 'Entry' if we overridden the contents of one file
/// with the contents of another file.
const FileEntry *ContentsEntry;
/// The filename that is used to access OrigEntry.
///
/// FIXME: Remove this once OrigEntry is a FileEntryRef with a stable name.
StringRef Filename;
/// A bump pointer allocated array of offsets for each source line.
///
/// This is lazily computed. The lines are owned by the SourceManager
/// BumpPointerAllocator object.
mutable LineOffsetMapping SourceLineCache;
/// Indicates whether the buffer itself was provided to override
/// the actual file contents.
///
/// When true, the original entry may be a virtual file that does not
/// exist.
unsigned BufferOverridden : 1;
/// True if this content cache was initially created for a source file
/// considered to be volatile (likely to change between stat and open).
unsigned IsFileVolatile : 1;
/// True if this file may be transient, that is, if it might not
/// exist at some later point in time when this content entry is used,
/// after serialization and deserialization.
unsigned IsTransient : 1;
mutable unsigned IsBufferInvalid : 1;
ContentCache(const FileEntry *Ent = nullptr) : ContentCache(Ent, Ent) {}
ContentCache(const FileEntry *Ent, const FileEntry *contentEnt)
: OrigEntry(Ent), ContentsEntry(contentEnt), BufferOverridden(false),
IsFileVolatile(false), IsTransient(false), IsBufferInvalid(false) {}
/// The copy ctor does not allow copies where source object has either
/// a non-NULL Buffer or SourceLineCache. Ownership of allocated memory
/// is not transferred, so this is a logical error.
ContentCache(const ContentCache &RHS)
: BufferOverridden(false), IsFileVolatile(false), IsTransient(false),
IsBufferInvalid(false) {
OrigEntry = RHS.OrigEntry;
ContentsEntry = RHS.ContentsEntry;
assert(!RHS.Buffer && !RHS.SourceLineCache &&
"Passed ContentCache object cannot own a buffer.");
}
ContentCache &operator=(const ContentCache &RHS) = delete;
/// Returns the memory buffer for the associated content.
///
/// \param Diag Object through which diagnostics will be emitted if the
/// buffer cannot be retrieved.
///
/// \param Loc If specified, is the location that invalid file diagnostics
/// will be emitted at.
llvm::Optional<llvm::MemoryBufferRef>
getBufferOrNone(DiagnosticsEngine &Diag, FileManager &FM,
SourceLocation Loc = SourceLocation()) const;
/// Returns the size of the content encapsulated by this
/// ContentCache.
///
/// This can be the size of the source file or the size of an
/// arbitrary scratch buffer. If the ContentCache encapsulates a source
/// file this size is retrieved from the file's FileEntry.
unsigned getSize() const;
/// Returns the number of bytes actually mapped for this
/// ContentCache.
///
/// This can be 0 if the MemBuffer was not actually expanded.
unsigned getSizeBytesMapped() const;
/// Returns the kind of memory used to back the memory buffer for
/// this content cache. This is used for performance analysis.
llvm::MemoryBuffer::BufferKind getMemoryBufferKind() const;
/// Return the buffer, only if it has been loaded.
llvm::Optional<llvm::MemoryBufferRef> getBufferIfLoaded() const {
if (Buffer)
return Buffer->getMemBufferRef();
return None;
}
/// Return a StringRef to the source buffer data, only if it has already
/// been loaded.
llvm::Optional<StringRef> getBufferDataIfLoaded() const {
if (Buffer)
return Buffer->getBuffer();
return None;
}
/// Set the buffer.
void setBuffer(std::unique_ptr<llvm::MemoryBuffer> B) {
IsBufferInvalid = false;
Buffer = std::move(B);
}
/// Set the buffer to one that's not owned (or to nullptr).
///
/// \pre Buffer cannot already be set.
void setUnownedBuffer(llvm::Optional<llvm::MemoryBufferRef> B) {
assert(!Buffer && "Expected to be called right after construction");
if (B)
setBuffer(llvm::MemoryBuffer::getMemBuffer(*B));
}
// If BufStr has an invalid BOM, returns the BOM name; otherwise, returns
// nullptr
static const char *getInvalidBOM(StringRef BufStr);
};
// Assert that the \c ContentCache objects will always be 8-byte aligned so
// that we can pack 3 bits of integer into pointers to such objects.
static_assert(alignof(ContentCache) >= 8,
"ContentCache must be 8-byte aligned.");
/// Information about a FileID, basically just the logical file
/// that it represents and include stack information.
///
/// Each FileInfo has include stack information, indicating where it came
/// from. This information encodes the \#include chain that a token was
/// expanded from. The main include file has an invalid IncludeLoc.
///
/// FileInfo should not grow larger than ExpansionInfo. Doing so will
/// cause memory to bloat in compilations with many unloaded macro
/// expansions, since the two data structurs are stored in a union in
/// SLocEntry. Extra fields should instead go in "ContentCache *", which
/// stores file contents and other bits on the side.
///
class FileInfo {
friend class clang::SourceManager;
friend class clang::ASTWriter;
friend class clang::ASTReader;
/// The location of the \#include that brought in this file.
///
/// This is an invalid SLOC for the main file (top of the \#include chain).
SourceLocation IncludeLoc;
/// Number of FileIDs (files and macros) that were created during
/// preprocessing of this \#include, including this SLocEntry.
///
/// Zero means the preprocessor didn't provide such info for this SLocEntry.
unsigned NumCreatedFIDs : 31;
/// Whether this FileInfo has any \#line directives.
unsigned HasLineDirectives : 1;
/// The content cache and the characteristic of the file.
llvm::PointerIntPair<const ContentCache *, 3, CharacteristicKind>
ContentAndKind;
public:
/// Return a FileInfo object.
static FileInfo get(SourceLocation IL, ContentCache &Con,
CharacteristicKind FileCharacter, StringRef Filename) {
FileInfo X;
X.IncludeLoc = IL;
X.NumCreatedFIDs = 0;
X.HasLineDirectives = false;
X.ContentAndKind.setPointer(&Con);
X.ContentAndKind.setInt(FileCharacter);
Con.Filename = Filename;
return X;
}
SourceLocation getIncludeLoc() const {
return IncludeLoc;
}
const ContentCache &getContentCache() const {
return *ContentAndKind.getPointer();
}
/// Return whether this is a system header or not.
CharacteristicKind getFileCharacteristic() const {
return ContentAndKind.getInt();
}
/// Return true if this FileID has \#line directives in it.
bool hasLineDirectives() const { return HasLineDirectives; }
/// Set the flag that indicates that this FileID has
/// line table entries associated with it.
void setHasLineDirectives() { HasLineDirectives = true; }
/// Returns the name of the file that was used when the file was loaded from
/// the underlying file system.
StringRef getName() const { return getContentCache().Filename; }
};
/// Each ExpansionInfo encodes the expansion location - where
/// the token was ultimately expanded, and the SpellingLoc - where the actual
/// character data for the token came from.
class ExpansionInfo {
// Really these are all SourceLocations.
/// Where the spelling for the token can be found.
SourceLocation SpellingLoc;
/// In a macro expansion, ExpansionLocStart and ExpansionLocEnd
/// indicate the start and end of the expansion. In object-like macros,
/// they will be the same. In a function-like macro expansion, the start
/// will be the identifier and the end will be the ')'. Finally, in
/// macro-argument instantiations, the end will be 'SourceLocation()', an
/// invalid location.
SourceLocation ExpansionLocStart, ExpansionLocEnd;
/// Whether the expansion range is a token range.
bool ExpansionIsTokenRange;
public:
SourceLocation getSpellingLoc() const {
return SpellingLoc.isInvalid() ? getExpansionLocStart() : SpellingLoc;
}
SourceLocation getExpansionLocStart() const {
return ExpansionLocStart;
}
SourceLocation getExpansionLocEnd() const {
return ExpansionLocEnd.isInvalid() ? getExpansionLocStart()
: ExpansionLocEnd;
}
bool isExpansionTokenRange() const { return ExpansionIsTokenRange; }
CharSourceRange getExpansionLocRange() const {
return CharSourceRange(
SourceRange(getExpansionLocStart(), getExpansionLocEnd()),
isExpansionTokenRange());
}
bool isMacroArgExpansion() const {
// Note that this needs to return false for default constructed objects.
return getExpansionLocStart().isValid() && ExpansionLocEnd.isInvalid();
}
bool isMacroBodyExpansion() const {
return getExpansionLocStart().isValid() && ExpansionLocEnd.isValid();
}
bool isFunctionMacroExpansion() const {
return getExpansionLocStart().isValid() &&
getExpansionLocStart() != getExpansionLocEnd();
}
/// Return a ExpansionInfo for an expansion.
///
/// Start and End specify the expansion range (where the macro is
/// expanded), and SpellingLoc specifies the spelling location (where
/// the characters from the token come from). All three can refer to
/// normal File SLocs or expansion locations.
static ExpansionInfo create(SourceLocation SpellingLoc, SourceLocation Start,
SourceLocation End,
bool ExpansionIsTokenRange = true) {
ExpansionInfo X;
X.SpellingLoc = SpellingLoc;
X.ExpansionLocStart = Start;
X.ExpansionLocEnd = End;
X.ExpansionIsTokenRange = ExpansionIsTokenRange;
return X;
}
/// Return a special ExpansionInfo for the expansion of
/// a macro argument into a function-like macro's body.
///
/// ExpansionLoc specifies the expansion location (where the macro is
/// expanded). This doesn't need to be a range because a macro is always
/// expanded at a macro parameter reference, and macro parameters are
/// always exactly one token. SpellingLoc specifies the spelling location
/// (where the characters from the token come from). ExpansionLoc and
/// SpellingLoc can both refer to normal File SLocs or expansion locations.
///
/// Given the code:
/// \code
/// #define F(x) f(x)
/// F(42);
/// \endcode
///
/// When expanding '\c F(42)', the '\c x' would call this with an
/// SpellingLoc pointing at '\c 42' and an ExpansionLoc pointing at its
/// location in the definition of '\c F'.
static ExpansionInfo createForMacroArg(SourceLocation SpellingLoc,
SourceLocation ExpansionLoc) {
// We store an intentionally invalid source location for the end of the
// expansion range to mark that this is a macro argument location rather
// than a normal one.
return create(SpellingLoc, ExpansionLoc, SourceLocation());
}
/// Return a special ExpansionInfo representing a token that ends
/// prematurely. This is used to model a '>>' token that has been split
/// into '>' tokens and similar cases. Unlike for the other forms of
/// expansion, the expansion range in this case is a character range, not
/// a token range.
static ExpansionInfo createForTokenSplit(SourceLocation SpellingLoc,
SourceLocation Start,
SourceLocation End) {
return create(SpellingLoc, Start, End, false);
}
};
// Assert that the \c FileInfo objects are no bigger than \c ExpansionInfo
// objects. This controls the size of \c SLocEntry, of which we have one for
// each macro expansion. The number of (unloaded) macro expansions can be
// very large. Any other fields needed in FileInfo should go in ContentCache.
static_assert(sizeof(FileInfo) <= sizeof(ExpansionInfo),
"FileInfo must be no larger than ExpansionInfo.");
/// This is a discriminated union of FileInfo and ExpansionInfo.
///
/// SourceManager keeps an array of these objects, and they are uniquely
/// identified by the FileID datatype.
class SLocEntry {
static constexpr int OffsetBits = 8 * sizeof(SourceLocation::UIntTy) - 1;
SourceLocation::UIntTy Offset : OffsetBits;
SourceLocation::UIntTy IsExpansion : 1;
union {
FileInfo File;
ExpansionInfo Expansion;
};
public:
SLocEntry() : Offset(), IsExpansion(), File() {}
SourceLocation::UIntTy getOffset() const { return Offset; }
bool isExpansion() const { return IsExpansion; }
bool isFile() const { return !isExpansion(); }
const FileInfo &getFile() const {
assert(isFile() && "Not a file SLocEntry!");
return File;
}
const ExpansionInfo &getExpansion() const {
assert(isExpansion() && "Not a macro expansion SLocEntry!");
return Expansion;
}
static SLocEntry get(SourceLocation::UIntTy Offset, const FileInfo &FI) {
assert(!(Offset & (1ULL << OffsetBits)) && "Offset is too large");
SLocEntry E;
E.Offset = Offset;
E.IsExpansion = false;
E.File = FI;
return E;
}
static SLocEntry get(SourceLocation::UIntTy Offset,
const ExpansionInfo &Expansion) {
assert(!(Offset & (1ULL << OffsetBits)) && "Offset is too large");
SLocEntry E;
E.Offset = Offset;
E.IsExpansion = true;
new (&E.Expansion) ExpansionInfo(Expansion);
return E;
}
};
} // namespace SrcMgr
/// External source of source location entries.
class ExternalSLocEntrySource {
public:
virtual ~ExternalSLocEntrySource();
/// Read the source location entry with index ID, which will always be
/// less than -1.
///
/// \returns true if an error occurred that prevented the source-location
/// entry from being loaded.
virtual bool ReadSLocEntry(int ID) = 0;
/// Retrieve the module import location and name for the given ID, if
/// in fact it was loaded from a module (rather than, say, a precompiled
/// header).
virtual std::pair<SourceLocation, StringRef> getModuleImportLoc(int ID) = 0;
};
/// Holds the cache used by isBeforeInTranslationUnit.
///
/// The cache structure is complex enough to be worth breaking out of
/// SourceManager.
class InBeforeInTUCacheEntry {
/// The FileID's of the cached query.
///
/// If these match up with a subsequent query, the result can be reused.
FileID LQueryFID, RQueryFID;
/// True if LQueryFID was created before RQueryFID.
///
/// This is used to compare macro expansion locations.
bool IsLQFIDBeforeRQFID;
/// The file found in common between the two \#include traces, i.e.,
/// the nearest common ancestor of the \#include tree.
FileID CommonFID;
/// The offset of the previous query in CommonFID.
///
/// Usually, this represents the location of the \#include for QueryFID, but
/// if LQueryFID is a parent of RQueryFID (or vice versa) then these can be a
/// random token in the parent.
unsigned LCommonOffset, RCommonOffset;
public:
/// Return true if the currently cached values match up with
/// the specified LHS/RHS query.
///
/// If not, we can't use the cache.
bool isCacheValid(FileID LHS, FileID RHS) const {
return LQueryFID == LHS && RQueryFID == RHS;
}
/// If the cache is valid, compute the result given the
/// specified offsets in the LHS/RHS FileID's.
bool getCachedResult(unsigned LOffset, unsigned ROffset) const {
// If one of the query files is the common file, use the offset. Otherwise,
// use the #include loc in the common file.
if (LQueryFID != CommonFID) LOffset = LCommonOffset;
if (RQueryFID != CommonFID) ROffset = RCommonOffset;
// It is common for multiple macro expansions to be "included" from the same
// location (expansion location), in which case use the order of the FileIDs
// to determine which came first. This will also take care the case where
// one of the locations points at the inclusion/expansion point of the other
// in which case its FileID will come before the other.
if (LOffset == ROffset)
return IsLQFIDBeforeRQFID;
return LOffset < ROffset;
}
/// Set up a new query.
void setQueryFIDs(FileID LHS, FileID RHS, bool isLFIDBeforeRFID) {
assert(LHS != RHS);
LQueryFID = LHS;
RQueryFID = RHS;
IsLQFIDBeforeRQFID = isLFIDBeforeRFID;
}
void clear() {
LQueryFID = RQueryFID = FileID();
IsLQFIDBeforeRQFID = false;
}
void setCommonLoc(FileID commonFID, unsigned lCommonOffset,
unsigned rCommonOffset) {
CommonFID = commonFID;
LCommonOffset = lCommonOffset;
RCommonOffset = rCommonOffset;
}
};
/// The stack used when building modules on demand, which is used
/// to provide a link between the source managers of the different compiler
/// instances.
using ModuleBuildStack = ArrayRef<std::pair<std::string, FullSourceLoc>>;
/// This class handles loading and caching of source files into memory.
///
/// This object owns the MemoryBuffer objects for all of the loaded
/// files and assigns unique FileID's for each unique \#include chain.
///
/// The SourceManager can be queried for information about SourceLocation
/// objects, turning them into either spelling or expansion locations. Spelling
/// locations represent where the bytes corresponding to a token came from and
/// expansion locations represent where the location is in the user's view. In
/// the case of a macro expansion, for example, the spelling location indicates
/// where the expanded token came from and the expansion location specifies
/// where it was expanded.
class SourceManager : public RefCountedBase<SourceManager> {
/// DiagnosticsEngine object.
DiagnosticsEngine &Diag;
FileManager &FileMgr;
mutable llvm::BumpPtrAllocator ContentCacheAlloc;
/// Memoized information about all of the files tracked by this
/// SourceManager.
///
/// This map allows us to merge ContentCache entries based
/// on their FileEntry*. All ContentCache objects will thus have unique,
/// non-null, FileEntry pointers.
llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*> FileInfos;
/// True if the ContentCache for files that are overridden by other
/// files, should report the original file name. Defaults to true.
bool OverridenFilesKeepOriginalName = true;
/// True if non-system source files should be treated as volatile
/// (likely to change while trying to use them). Defaults to false.
bool UserFilesAreVolatile;
/// True if all files read during this compilation should be treated
/// as transient (may not be present in later compilations using a module
/// file created from this compilation). Defaults to false.
bool FilesAreTransient = false;
struct OverriddenFilesInfoTy {
/// Files that have been overridden with the contents from another
/// file.
llvm::DenseMap<const FileEntry *, const FileEntry *> OverriddenFiles;
/// Files that were overridden with a memory buffer.
llvm::DenseSet<const FileEntry *> OverriddenFilesWithBuffer;
};
/// Lazily create the object keeping overridden files info, since
/// it is uncommonly used.
std::unique_ptr<OverriddenFilesInfoTy> OverriddenFilesInfo;
OverriddenFilesInfoTy &getOverriddenFilesInfo() {
if (!OverriddenFilesInfo)
OverriddenFilesInfo.reset(new OverriddenFilesInfoTy);
return *OverriddenFilesInfo;
}
/// Information about various memory buffers that we have read in.
///
/// All FileEntry* within the stored ContentCache objects are NULL,
/// as they do not refer to a file.
std::vector<SrcMgr::ContentCache*> MemBufferInfos;
/// The table of SLocEntries that are local to this module.
///
/// Positive FileIDs are indexes into this table. Entry 0 indicates an invalid
/// expansion.
SmallVector<SrcMgr::SLocEntry, 0> LocalSLocEntryTable;
/// The table of SLocEntries that are loaded from other modules.
///
/// Negative FileIDs are indexes into this table. To get from ID to an index,
/// use (-ID - 2).
SmallVector<SrcMgr::SLocEntry, 0> LoadedSLocEntryTable;
/// The starting offset of the next local SLocEntry.
///
/// This is LocalSLocEntryTable.back().Offset + the size of that entry.
SourceLocation::UIntTy NextLocalOffset;
/// The starting offset of the latest batch of loaded SLocEntries.
///
/// This is LoadedSLocEntryTable.back().Offset, except that that entry might
/// not have been loaded, so that value would be unknown.
SourceLocation::UIntTy CurrentLoadedOffset;
/// The highest possible offset is 2^32-1 (2^63-1 for 64-bit source
/// locations), so CurrentLoadedOffset starts at 2^31 (2^63 resp.).
static const SourceLocation::UIntTy MaxLoadedOffset =
1ULL << (8 * sizeof(SourceLocation::UIntTy) - 1);
/// A bitmap that indicates whether the entries of LoadedSLocEntryTable
/// have already been loaded from the external source.
///
/// Same indexing as LoadedSLocEntryTable.
llvm::BitVector SLocEntryLoaded;
/// An external source for source location entries.
ExternalSLocEntrySource *ExternalSLocEntries = nullptr;
/// A one-entry cache to speed up getFileID.
///
/// LastFileIDLookup records the last FileID looked up or created, because it
/// is very common to look up many tokens from the same file.
mutable FileID LastFileIDLookup;
/// Holds information for \#line directives.
///
/// This is referenced by indices from SLocEntryTable.
std::unique_ptr<LineTableInfo> LineTable;
/// These ivars serve as a cache used in the getLineNumber
/// method which is used to speedup getLineNumber calls to nearby locations.
mutable FileID LastLineNoFileIDQuery;
mutable const SrcMgr::ContentCache *LastLineNoContentCache;
mutable unsigned LastLineNoFilePos;
mutable unsigned LastLineNoResult;
/// The file ID for the main source file of the translation unit.
FileID MainFileID;
/// The file ID for the precompiled preamble there is one.
FileID PreambleFileID;
// Statistics for -print-stats.
mutable unsigned NumLinearScans = 0;
mutable unsigned NumBinaryProbes = 0;
/// Associates a FileID with its "included/expanded in" decomposed
/// location.
///
/// Used to cache results from and speed-up \c getDecomposedIncludedLoc
/// function.
mutable llvm::DenseMap<FileID, std::pair<FileID, unsigned>> IncludedLocMap;
/// The key value into the IsBeforeInTUCache table.
using IsBeforeInTUCacheKey = std::pair<FileID, FileID>;
/// The IsBeforeInTranslationUnitCache is a mapping from FileID pairs
/// to cache results.
using InBeforeInTUCache =
llvm::DenseMap<IsBeforeInTUCacheKey, InBeforeInTUCacheEntry>;
/// Cache results for the isBeforeInTranslationUnit method.
mutable InBeforeInTUCache IBTUCache;
mutable InBeforeInTUCacheEntry IBTUCacheOverflow;
/// Return the cache entry for comparing the given file IDs
/// for isBeforeInTranslationUnit.
InBeforeInTUCacheEntry &getInBeforeInTUCache(FileID LFID, FileID RFID) const;
// Cache for the "fake" buffer used for error-recovery purposes.
mutable std::unique_ptr<llvm::MemoryBuffer> FakeBufferForRecovery;
mutable std::unique_ptr<SrcMgr::ContentCache> FakeContentCacheForRecovery;
mutable std::unique_ptr<SrcMgr::SLocEntry> FakeSLocEntryForRecovery;
/// Lazily computed map of macro argument chunks to their expanded
/// source location.
using MacroArgsMap = std::map<unsigned, SourceLocation>;
mutable llvm::DenseMap<FileID, std::unique_ptr<MacroArgsMap>>
MacroArgsCacheMap;
/// The stack of modules being built, which is used to detect
/// cycles in the module dependency graph as modules are being built, as
/// well as to describe why we're rebuilding a particular module.
///
/// There is no way to set this value from the command line. If we ever need
/// to do so (e.g., if on-demand module construction moves out-of-process),
/// we can add a cc1-level option to do so.
SmallVector<std::pair<std::string, FullSourceLoc>, 2> StoredModuleBuildStack;
public:
SourceManager(DiagnosticsEngine &Diag, FileManager &FileMgr,
bool UserFilesAreVolatile = false);
explicit SourceManager(const SourceManager &) = delete;
SourceManager &operator=(const SourceManager &) = delete;
~SourceManager();
void clearIDTables();
/// Initialize this source manager suitably to replay the compilation
/// described by \p Old. Requires that \p Old outlive \p *this.
void initializeForReplay(const SourceManager &Old);
DiagnosticsEngine &getDiagnostics() const { return Diag; }
FileManager &getFileManager() const { return FileMgr; }
/// Set true if the SourceManager should report the original file name
/// for contents of files that were overridden by other files. Defaults to
/// true.
void setOverridenFilesKeepOriginalName(bool value) {
OverridenFilesKeepOriginalName = value;
}
/// True if non-system source files should be treated as volatile
/// (likely to change while trying to use them).
bool userFilesAreVolatile() const { return UserFilesAreVolatile; }
/// Retrieve the module build stack.
ModuleBuildStack getModuleBuildStack() const {
return StoredModuleBuildStack;
}
/// Set the module build stack.
void setModuleBuildStack(ModuleBuildStack stack) {
StoredModuleBuildStack.clear();
StoredModuleBuildStack.append(stack.begin(), stack.end());
}
/// Push an entry to the module build stack.
void pushModuleBuildStack(StringRef moduleName, FullSourceLoc importLoc) {
StoredModuleBuildStack.push_back(std::make_pair(moduleName.str(),importLoc));
}
//===--------------------------------------------------------------------===//
// MainFileID creation and querying methods.
//===--------------------------------------------------------------------===//
/// Returns the FileID of the main source file.
FileID getMainFileID() const { return MainFileID; }
/// Set the file ID for the main source file.
void setMainFileID(FileID FID) {
MainFileID = FID;
}
/// Returns true when the given FileEntry corresponds to the main file.
///
/// The main file should be set prior to calling this function.
bool isMainFile(const FileEntry &SourceFile);
/// Set the file ID for the precompiled preamble.
void setPreambleFileID(FileID Preamble) {
assert(PreambleFileID.isInvalid() && "PreambleFileID already set!");
PreambleFileID = Preamble;
}
/// Get the file ID for the precompiled preamble if there is one.
FileID getPreambleFileID() const { return PreambleFileID; }
//===--------------------------------------------------------------------===//
// Methods to create new FileID's and macro expansions.
//===--------------------------------------------------------------------===//
/// Create a new FileID that represents the specified file
/// being \#included from the specified IncludePosition.
///
/// This translates NULL into standard input.
FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos,
SrcMgr::CharacteristicKind FileCharacter,
int LoadedID = 0,
SourceLocation::UIntTy LoadedOffset = 0);
FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos,
SrcMgr::CharacteristicKind FileCharacter,
int LoadedID = 0,
SourceLocation::UIntTy LoadedOffset = 0);
/// Create a new FileID that represents the specified memory buffer.
///
/// This does no caching of the buffer and takes ownership of the
/// MemoryBuffer, so only pass a MemoryBuffer to this once.
FileID createFileID(std::unique_ptr<llvm::MemoryBuffer> Buffer,
SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User,
int LoadedID = 0, SourceLocation::UIntTy LoadedOffset = 0,
SourceLocation IncludeLoc = SourceLocation());
/// Create a new FileID that represents the specified memory buffer.
///
/// This does not take ownership of the MemoryBuffer. The memory buffer must
/// outlive the SourceManager.
FileID createFileID(const llvm::MemoryBufferRef &Buffer,
SrcMgr::CharacteristicKind FileCharacter = SrcMgr::C_User,
int LoadedID = 0, SourceLocation::UIntTy LoadedOffset = 0,
SourceLocation IncludeLoc = SourceLocation());
/// Get the FileID for \p SourceFile if it exists. Otherwise, create a
/// new FileID for the \p SourceFile.
FileID getOrCreateFileID(const FileEntry *SourceFile,
SrcMgr::CharacteristicKind FileCharacter);
/// Creates an expansion SLocEntry for the substitution of an argument into a
/// function-like macro's body. Returns the start of the expansion.
///
/// The macro argument was written at \p SpellingLoc with length \p Length.
/// \p ExpansionLoc is the parameter name in the (expanded) macro body.
SourceLocation createMacroArgExpansionLoc(SourceLocation SpellingLoc,
SourceLocation ExpansionLoc,
unsigned Length);
/// Creates an expansion SLocEntry for a macro use. Returns its start.
///
/// The macro body begins at \p SpellingLoc with length \p Length.
/// The macro use spans [ExpansionLocStart, ExpansionLocEnd].
SourceLocation createExpansionLoc(SourceLocation SpellingLoc,
SourceLocation ExpansionLocStart,
SourceLocation ExpansionLocEnd,
unsigned Length,
bool ExpansionIsTokenRange = true,
int LoadedID = 0,
SourceLocation::UIntTy LoadedOffset = 0);
/// Return a new SourceLocation that encodes that the token starting
/// at \p TokenStart ends prematurely at \p TokenEnd.
SourceLocation createTokenSplitLoc(SourceLocation SpellingLoc,
SourceLocation TokenStart,
SourceLocation TokenEnd);
/// Retrieve the memory buffer associated with the given file.
///
/// Returns None if the buffer is not valid.
llvm::Optional<llvm::MemoryBufferRef>
getMemoryBufferForFileOrNone(const FileEntry *File);
/// Retrieve the memory buffer associated with the given file.
///
/// Returns a fake buffer if there isn't a real one.
llvm::MemoryBufferRef getMemoryBufferForFileOrFake(const FileEntry *File) {
if (auto B = getMemoryBufferForFileOrNone(File))
return *B;
return getFakeBufferForRecovery();
}
/// Override the contents of the given source file by providing an
/// already-allocated buffer.
///
/// \param SourceFile the source file whose contents will be overridden.
///
/// \param Buffer the memory buffer whose contents will be used as the
/// data in the given source file.
void overrideFileContents(const FileEntry *SourceFile,
const llvm::MemoryBufferRef &Buffer) {
overrideFileContents(SourceFile, llvm::MemoryBuffer::getMemBuffer(Buffer));
}
/// Override the contents of the given source file by providing an
/// already-allocated buffer.
///
/// \param SourceFile the source file whose contents will be overridden.
///
/// \param Buffer the memory buffer whose contents will be used as the
/// data in the given source file.
void overrideFileContents(const FileEntry *SourceFile,
std::unique_ptr<llvm::MemoryBuffer> Buffer);
void overrideFileContents(FileEntryRef SourceFile,
std::unique_ptr<llvm::MemoryBuffer> Buffer) {
overrideFileContents(&SourceFile.getFileEntry(), std::move(Buffer));
}
/// Override the given source file with another one.
///
/// \param SourceFile the source file which will be overridden.
///
/// \param NewFile the file whose contents will be used as the
/// data instead of the contents of the given source file.
void overrideFileContents(const FileEntry *SourceFile,
const FileEntry *NewFile);
/// Returns true if the file contents have been overridden.
bool isFileOverridden(const FileEntry *File) const {
if (OverriddenFilesInfo) {
if (OverriddenFilesInfo->OverriddenFilesWithBuffer.count(File))
return true;
if (OverriddenFilesInfo->OverriddenFiles.find(File) !=
OverriddenFilesInfo->OverriddenFiles.end())
return true;
}
return false;
}
/// Bypass the overridden contents of a file. This creates a new FileEntry
/// and initializes the content cache for it. Returns None if there is no
/// such file in the filesystem.
///
/// This should be called before parsing has begun.
Optional<FileEntryRef> bypassFileContentsOverride(FileEntryRef File);
/// Specify that a file is transient.
void setFileIsTransient(const FileEntry *SourceFile);
/// Specify that all files that are read during this compilation are
/// transient.
void setAllFilesAreTransient(bool Transient) {
FilesAreTransient = Transient;
}
//===--------------------------------------------------------------------===//
// FileID manipulation methods.
//===--------------------------------------------------------------------===//
/// Return the buffer for the specified FileID.
///
/// If there is an error opening this buffer the first time, return None.
llvm::Optional<llvm::MemoryBufferRef>
getBufferOrNone(FileID FID, SourceLocation Loc = SourceLocation()) const {
if (auto *Entry = getSLocEntryForFile(FID))
return Entry->getFile().getContentCache().getBufferOrNone(
Diag, getFileManager(), Loc);
return None;
}
/// Return the buffer for the specified FileID.
///
/// If there is an error opening this buffer the first time, this
/// manufactures a temporary buffer and returns it.
llvm::MemoryBufferRef
getBufferOrFake(FileID FID, SourceLocation Loc = SourceLocation()) const {
if (auto B = getBufferOrNone(FID, Loc))
return *B;
return getFakeBufferForRecovery();
}
/// Returns the FileEntry record for the provided FileID.
const FileEntry *getFileEntryForID(FileID FID) const {
if (auto *Entry = getSLocEntryForFile(FID))
return Entry->getFile().getContentCache().OrigEntry;
return nullptr;
}
/// Returns the FileEntryRef for the provided FileID.
Optional<FileEntryRef> getFileEntryRefForID(FileID FID) const {
if (auto *Entry = getFileEntryForID(FID))
return Entry->getLastRef();
return None;
}
/// Returns the filename for the provided FileID, unless it's a built-in
/// buffer that's not represented by a filename.
///
/// Returns None for non-files and built-in files.
Optional<StringRef> getNonBuiltinFilenameForID(FileID FID) const;
/// Returns the FileEntry record for the provided SLocEntry.
const FileEntry *getFileEntryForSLocEntry(const SrcMgr::SLocEntry &sloc) const
{
return sloc.getFile().getContentCache().OrigEntry;
}
/// Return a StringRef to the source buffer data for the
/// specified FileID.
///
/// \param FID The file ID whose contents will be returned.
/// \param Invalid If non-NULL, will be set true if an error occurred.
StringRef getBufferData(FileID FID, bool *Invalid = nullptr) const;
/// Return a StringRef to the source buffer data for the
/// specified FileID, returning None if invalid.
///
/// \param FID The file ID whose contents will be returned.
llvm::Optional<StringRef> getBufferDataOrNone(FileID FID) const;
/// Return a StringRef to the source buffer data for the
/// specified FileID, returning None if it's not yet loaded.
///
/// \param FID The file ID whose contents will be returned.
llvm::Optional<StringRef> getBufferDataIfLoaded(FileID FID) const;
/// Get the number of FileIDs (files and macros) that were created
/// during preprocessing of \p FID, including it.
unsigned getNumCreatedFIDsForFileID(FileID FID) const {
if (auto *Entry = getSLocEntryForFile(FID))
return Entry->getFile().NumCreatedFIDs;
return 0;
}
/// Set the number of FileIDs (files and macros) that were created
/// during preprocessing of \p FID, including it.
void setNumCreatedFIDsForFileID(FileID FID, unsigned NumFIDs,
bool Force = false) const {
auto *Entry = getSLocEntryForFile(FID);
if (!Entry)
return;
assert((Force || Entry->getFile().NumCreatedFIDs == 0) && "Already set!");
const_cast<SrcMgr::FileInfo &>(Entry->getFile()).NumCreatedFIDs = NumFIDs;
}
//===--------------------------------------------------------------------===//
// SourceLocation manipulation methods.
//===--------------------------------------------------------------------===//
/// Return the FileID for a SourceLocation.
///
/// This is a very hot method that is used for all SourceManager queries
/// that start with a SourceLocation object. It is responsible for finding
/// the entry in SLocEntryTable which contains the specified location.
///
FileID getFileID(SourceLocation SpellingLoc) const {
SourceLocation::UIntTy SLocOffset = SpellingLoc.getOffset();
// If our one-entry cache covers this offset, just return it.
if (isOffsetInFileID(LastFileIDLookup, SLocOffset))
return LastFileIDLookup;
return getFileIDSlow(SLocOffset);
}
/// Return the filename of the file containing a SourceLocation.
StringRef getFilename(SourceLocation SpellingLoc) const;
/// Return the source location corresponding to the first byte of
/// the specified file.
SourceLocation getLocForStartOfFile(FileID FID) const {
if (auto *Entry = getSLocEntryForFile(FID))
return SourceLocation::getFileLoc(Entry->getOffset());
return SourceLocation();
}
/// Return the source location corresponding to the last byte of the
/// specified file.
SourceLocation getLocForEndOfFile(FileID FID) const {
if (auto *Entry = getSLocEntryForFile(FID))
return SourceLocation::getFileLoc(Entry->getOffset() +
getFileIDSize(FID));
return SourceLocation();
}
/// Returns the include location if \p FID is a \#include'd file
/// otherwise it returns an invalid location.
SourceLocation getIncludeLoc(FileID FID) const {
if (auto *Entry = getSLocEntryForFile(FID))
return Entry->getFile().getIncludeLoc();
return SourceLocation();
}
// Returns the import location if the given source location is
// located within a module, or an invalid location if the source location
// is within the current translation unit.
std::pair<SourceLocation, StringRef>
getModuleImportLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
// Positive file IDs are in the current translation unit, and -1 is a
// placeholder.
if (FID.ID >= -1)
return std::make_pair(SourceLocation(), "");
return ExternalSLocEntries->getModuleImportLoc(FID.ID);
}
/// Given a SourceLocation object \p Loc, return the expansion
/// location referenced by the ID.
SourceLocation getExpansionLoc(SourceLocation Loc) const {
// Handle the non-mapped case inline, defer to out of line code to handle
// expansions.
if (Loc.isFileID()) return Loc;
return getExpansionLocSlowCase(Loc);
}
/// Given \p Loc, if it is a macro location return the expansion
/// location or the spelling location, depending on if it comes from a
/// macro argument or not.
SourceLocation getFileLoc(SourceLocation Loc) const {
if (Loc.isFileID()) return Loc;
return getFileLocSlowCase(Loc);
}
/// Return the start/end of the expansion information for an
/// expansion location.
///
/// \pre \p Loc is required to be an expansion location.
CharSourceRange getImmediateExpansionRange(SourceLocation Loc) const;
/// Given a SourceLocation object, return the range of
/// tokens covered by the expansion in the ultimate file.
CharSourceRange getExpansionRange(SourceLocation Loc) const;
/// Given a SourceRange object, return the range of
/// tokens or characters covered by the expansion in the ultimate file.
CharSourceRange getExpansionRange(SourceRange Range) const {
SourceLocation Begin = getExpansionRange(Range.getBegin()).getBegin();
CharSourceRange End = getExpansionRange(Range.getEnd());
return CharSourceRange(SourceRange(Begin, End.getEnd()),
End.isTokenRange());
}
/// Given a CharSourceRange object, return the range of
/// tokens or characters covered by the expansion in the ultimate file.
CharSourceRange getExpansionRange(CharSourceRange Range) const {
CharSourceRange Expansion = getExpansionRange(Range.getAsRange());
if (Expansion.getEnd() == Range.getEnd())
Expansion.setTokenRange(Range.isTokenRange());
return Expansion;
}
/// Given a SourceLocation object, return the spelling
/// location referenced by the ID.
///
/// This is the place where the characters that make up the lexed token
/// can be found.
SourceLocation getSpellingLoc(SourceLocation Loc) const {
// Handle the non-mapped case inline, defer to out of line code to handle
// expansions.
if (Loc.isFileID()) return Loc;
return getSpellingLocSlowCase(Loc);
}
/// Given a SourceLocation object, return the spelling location
/// referenced by the ID.
///
/// This is the first level down towards the place where the characters
/// that make up the lexed token can be found. This should not generally
/// be used by clients.
SourceLocation getImmediateSpellingLoc(SourceLocation Loc) const;
/// Form a SourceLocation from a FileID and Offset pair.
SourceLocation getComposedLoc(FileID FID, unsigned Offset) const {
auto *Entry = getSLocEntryOrNull(FID);
if (!Entry)
return SourceLocation();
SourceLocation::UIntTy GlobalOffset = Entry->getOffset() + Offset;
return Entry->isFile() ? SourceLocation::getFileLoc(GlobalOffset)
: SourceLocation::getMacroLoc(GlobalOffset);
}
/// Decompose the specified location into a raw FileID + Offset pair.
///
/// The first element is the FileID, the second is the offset from the
/// start of the buffer of the location.
std::pair<FileID, unsigned> getDecomposedLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
auto *Entry = getSLocEntryOrNull(FID);
if (!Entry)
return std::make_pair(FileID(), 0);
return std::make_pair(FID, Loc.getOffset() - Entry->getOffset());
}
/// Decompose the specified location into a raw FileID + Offset pair.
///
/// If the location is an expansion record, walk through it until we find
/// the final location expanded.
std::pair<FileID, unsigned>
getDecomposedExpansionLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
auto *E = getSLocEntryOrNull(FID);
if (!E)
return std::make_pair(FileID(), 0);
unsigned Offset = Loc.getOffset()-E->getOffset();
if (Loc.isFileID())
return std::make_pair(FID, Offset);
return getDecomposedExpansionLocSlowCase(E);
}
/// Decompose the specified location into a raw FileID + Offset pair.
///
/// If the location is an expansion record, walk through it until we find
/// its spelling record.
std::pair<FileID, unsigned>
getDecomposedSpellingLoc(SourceLocation Loc) const {
FileID FID = getFileID(Loc);
auto *E = getSLocEntryOrNull(FID);
if (!E)
return std::make_pair(FileID(), 0);
unsigned Offset = Loc.getOffset()-E->getOffset();
if (Loc.isFileID())
return std::make_pair(FID, Offset);
return getDecomposedSpellingLocSlowCase(E, Offset);
}
/// Returns the "included/expanded in" decomposed location of the given
/// FileID.
std::pair<FileID, unsigned> getDecomposedIncludedLoc(FileID FID) const;
/// Returns the offset from the start of the file that the
/// specified SourceLocation represents.
///
/// This is not very meaningful for a macro ID.
unsigned getFileOffset(SourceLocation SpellingLoc) const {
return getDecomposedLoc(SpellingLoc).second;
}
/// Tests whether the given source location represents a macro
/// argument's expansion into the function-like macro definition.
///
/// \param StartLoc If non-null and function returns true, it is set to the
/// start location of the macro argument expansion.
///
/// Such source locations only appear inside of the expansion
/// locations representing where a particular function-like macro was
/// expanded.
bool isMacroArgExpansion(SourceLocation Loc,
SourceLocation *StartLoc = nullptr) const;
/// Tests whether the given source location represents the expansion of
/// a macro body.
///
/// This is equivalent to testing whether the location is part of a macro
/// expansion but not the expansion of an argument to a function-like macro.
bool isMacroBodyExpansion(SourceLocation Loc) const;
/// Returns true if the given MacroID location points at the beginning
/// of the immediate macro expansion.
///
/// \param MacroBegin If non-null and function returns true, it is set to the
/// begin location of the immediate macro expansion.
bool isAtStartOfImmediateMacroExpansion(SourceLocation Loc,
SourceLocation *MacroBegin = nullptr) const;
/// Returns true if the given MacroID location points at the character
/// end of the immediate macro expansion.
///
/// \param MacroEnd If non-null and function returns true, it is set to the
/// character end location of the immediate macro expansion.
bool
isAtEndOfImmediateMacroExpansion(SourceLocation Loc,
SourceLocation *MacroEnd = nullptr) const;
/// Returns true if \p Loc is inside the [\p Start, +\p Length)
/// chunk of the source location address space.
///
/// If it's true and \p RelativeOffset is non-null, it will be set to the
/// relative offset of \p Loc inside the chunk.
bool
isInSLocAddrSpace(SourceLocation Loc, SourceLocation Start, unsigned Length,
SourceLocation::UIntTy *RelativeOffset = nullptr) const {
assert(((Start.getOffset() < NextLocalOffset &&
Start.getOffset()+Length <= NextLocalOffset) ||
(Start.getOffset() >= CurrentLoadedOffset &&
Start.getOffset()+Length < MaxLoadedOffset)) &&
"Chunk is not valid SLoc address space");
SourceLocation::UIntTy LocOffs = Loc.getOffset();
SourceLocation::UIntTy BeginOffs = Start.getOffset();
SourceLocation::UIntTy EndOffs = BeginOffs + Length;
if (LocOffs >= BeginOffs && LocOffs < EndOffs) {
if (RelativeOffset)
*RelativeOffset = LocOffs - BeginOffs;
return true;
}
return false;
}
/// Return true if both \p LHS and \p RHS are in the local source
/// location address space or the loaded one.
///
/// If it's true and \p RelativeOffset is non-null, it will be set to the
/// offset of \p RHS relative to \p LHS.
bool isInSameSLocAddrSpace(SourceLocation LHS, SourceLocation RHS,
SourceLocation::IntTy *RelativeOffset) const {
SourceLocation::UIntTy LHSOffs = LHS.getOffset(), RHSOffs = RHS.getOffset();
bool LHSLoaded = LHSOffs >= CurrentLoadedOffset;
bool RHSLoaded = RHSOffs >= CurrentLoadedOffset;
if (LHSLoaded == RHSLoaded) {
if (RelativeOffset)
*RelativeOffset = RHSOffs - LHSOffs;
return true;
}
return false;
}
//===--------------------------------------------------------------------===//
// Queries about the code at a SourceLocation.
//===--------------------------------------------------------------------===//
/// Return a pointer to the start of the specified location
/// in the appropriate spelling MemoryBuffer.
///
/// \param Invalid If non-NULL, will be set \c true if an error occurs.
const char *getCharacterData(SourceLocation SL,
bool *Invalid = nullptr) const;
/// Return the column # for the specified file position.
///
/// This is significantly cheaper to compute than the line number. This
/// returns zero if the column number isn't known. This may only be called
/// on a file sloc, so you must choose a spelling or expansion location
/// before calling this method.
unsigned getColumnNumber(FileID FID, unsigned FilePos,
bool *Invalid = nullptr) const;
unsigned getSpellingColumnNumber(SourceLocation Loc,
bool *Invalid = nullptr) const;
unsigned getExpansionColumnNumber(SourceLocation Loc,
bool *Invalid = nullptr) const;
unsigned getPresumedColumnNumber(SourceLocation Loc,
bool *Invalid = nullptr) const;
/// Given a SourceLocation, return the spelling line number
/// for the position indicated.
///
/// This requires building and caching a table of line offsets for the
/// MemoryBuffer, so this is not cheap: use only when about to emit a
/// diagnostic.
unsigned getLineNumber(FileID FID, unsigned FilePos, bool *Invalid = nullptr) const;
unsigned getSpellingLineNumber(SourceLocation Loc, bool *Invalid = nullptr) const;
unsigned getExpansionLineNumber(SourceLocation Loc, bool *Invalid = nullptr) const;
unsigned getPresumedLineNumber(SourceLocation Loc, bool *Invalid = nullptr) const;
/// Return the filename or buffer identifier of the buffer the
/// location is in.
///
/// Note that this name does not respect \#line directives. Use
/// getPresumedLoc for normal clients.
StringRef getBufferName(SourceLocation Loc, bool *Invalid = nullptr) const;
/// Return the file characteristic of the specified source
/// location, indicating whether this is a normal file, a system
/// header, or an "implicit extern C" system header.
///
/// This state can be modified with flags on GNU linemarker directives like:
/// \code
/// # 4 "foo.h" 3
/// \endcode
/// which changes all source locations in the current file after that to be
/// considered to be from a system header.
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const;
/// Returns the "presumed" location of a SourceLocation specifies.
///
/// A "presumed location" can be modified by \#line or GNU line marker
/// directives. This provides a view on the data that a user should see
/// in diagnostics, for example.
///
/// Note that a presumed location is always given as the expansion point of
/// an expansion location, not at the spelling location.
///
/// \returns The presumed location of the specified SourceLocation. If the
/// presumed location cannot be calculated (e.g., because \p Loc is invalid
/// or the file containing \p Loc has changed on disk), returns an invalid
/// presumed location.
PresumedLoc getPresumedLoc(SourceLocation Loc,
bool UseLineDirectives = true) const;
/// Returns whether the PresumedLoc for a given SourceLocation is
/// in the main file.
///
/// This computes the "presumed" location for a SourceLocation, then checks
/// whether it came from a file other than the main file. This is different
/// from isWrittenInMainFile() because it takes line marker directives into
/// account.
bool isInMainFile(SourceLocation Loc) const;
/// Returns true if the spelling locations for both SourceLocations
/// are part of the same file buffer.
///
/// This check ignores line marker directives.
bool isWrittenInSameFile(SourceLocation Loc1, SourceLocation Loc2) const {
return getFileID(Loc1) == getFileID(Loc2);
}
/// Returns true if the spelling location for the given location
/// is in the main file buffer.
///
/// This check ignores line marker directives.
bool isWrittenInMainFile(SourceLocation Loc) const {
return getFileID(Loc) == getMainFileID();
}
/// Returns whether \p Loc is located in a <built-in> file.
bool isWrittenInBuiltinFile(SourceLocation Loc) const {
PresumedLoc Presumed = getPresumedLoc(Loc);
if (Presumed.isInvalid())
return false;
StringRef Filename(Presumed.getFilename());
return Filename.equals("<built-in>");
}
/// Returns whether \p Loc is located in a <command line> file.
bool isWrittenInCommandLineFile(SourceLocation Loc) const {
PresumedLoc Presumed = getPresumedLoc(Loc);
if (Presumed.isInvalid())
return false;
StringRef Filename(Presumed.getFilename());
return Filename.equals("<command line>");
}
/// Returns whether \p Loc is located in a <scratch space> file.
bool isWrittenInScratchSpace(SourceLocation Loc) const {
PresumedLoc Presumed = getPresumedLoc(Loc);
if (Presumed.isInvalid())
return false;
StringRef Filename(Presumed.getFilename());
return Filename.equals("<scratch space>");
}
/// Returns if a SourceLocation is in a system header.
bool isInSystemHeader(SourceLocation Loc) const {
if (Loc.isInvalid())
return false;
return isSystem(getFileCharacteristic(Loc));
}
/// Returns if a SourceLocation is in an "extern C" system header.
bool isInExternCSystemHeader(SourceLocation Loc) const {
return getFileCharacteristic(Loc) == SrcMgr::C_ExternCSystem;
}
/// Returns whether \p Loc is expanded from a macro in a system header.
bool isInSystemMacro(SourceLocation loc) const {
if (!loc.isMacroID())
return false;
// This happens when the macro is the result of a paste, in that case
// its spelling is the scratch memory, so we take the parent context.
// There can be several level of token pasting.
if (isWrittenInScratchSpace(getSpellingLoc(loc))) {
do {
loc = getImmediateMacroCallerLoc(loc);
} while (isWrittenInScratchSpace(getSpellingLoc(loc)));
return isInSystemMacro(loc);
}
return isInSystemHeader(getSpellingLoc(loc));
}
/// The size of the SLocEntry that \p FID represents.
unsigned getFileIDSize(FileID FID) const;
/// Given a specific FileID, returns true if \p Loc is inside that
/// FileID chunk and sets relative offset (offset of \p Loc from beginning
/// of FileID) to \p relativeOffset.
bool isInFileID(SourceLocation Loc, FileID FID,
unsigned *RelativeOffset = nullptr) const {
SourceLocation::UIntTy Offs = Loc.getOffset();
if (isOffsetInFileID(FID, Offs)) {
if (RelativeOffset)
*RelativeOffset = Offs - getSLocEntry(FID).getOffset();
return true;
}
return false;
}
//===--------------------------------------------------------------------===//
// Line Table Manipulation Routines
//===--------------------------------------------------------------------===//
/// Return the uniqued ID for the specified filename.
unsigned getLineTableFilenameID(StringRef Str);
/// Add a line note to the line table for the FileID and offset
/// specified by Loc.
///
/// If FilenameID is -1, it is considered to be unspecified.
void AddLineNote(SourceLocation Loc, unsigned LineNo, int FilenameID,
bool IsFileEntry, bool IsFileExit,
SrcMgr::CharacteristicKind FileKind);
/// Determine if the source manager has a line table.
bool hasLineTable() const { return LineTable != nullptr; }
/// Retrieve the stored line table.
LineTableInfo &getLineTable();
//===--------------------------------------------------------------------===//
// Queries for performance analysis.
//===--------------------------------------------------------------------===//
/// Return the total amount of physical memory allocated by the
/// ContentCache allocator.
size_t getContentCacheSize() const {
return ContentCacheAlloc.getTotalMemory();
}
struct MemoryBufferSizes {
const size_t malloc_bytes;
const size_t mmap_bytes;
MemoryBufferSizes(size_t malloc_bytes, size_t mmap_bytes)
: malloc_bytes(malloc_bytes), mmap_bytes(mmap_bytes) {}
};
/// Return the amount of memory used by memory buffers, breaking down
/// by heap-backed versus mmap'ed memory.
MemoryBufferSizes getMemoryBufferSizes() const;
/// Return the amount of memory used for various side tables and
/// data structures in the SourceManager.
size_t getDataStructureSizes() const;
//===--------------------------------------------------------------------===//
// Other miscellaneous methods.
//===--------------------------------------------------------------------===//
/// Get the source location for the given file:line:col triplet.
///
/// If the source file is included multiple times, the source location will
/// be based upon the first inclusion.
SourceLocation translateFileLineCol(const FileEntry *SourceFile,
unsigned Line, unsigned Col) const;
/// Get the FileID for the given file.
///
/// If the source file is included multiple times, the FileID will be the
/// first inclusion.
FileID translateFile(const FileEntry *SourceFile) const;
FileID translateFile(FileEntryRef SourceFile) const {
return translateFile(&SourceFile.getFileEntry());
}
/// Get the source location in \p FID for the given line:col.
/// Returns null location if \p FID is not a file SLocEntry.
SourceLocation translateLineCol(FileID FID,
unsigned Line, unsigned Col) const;
/// If \p Loc points inside a function macro argument, the returned
/// location will be the macro location in which the argument was expanded.
/// If a macro argument is used multiple times, the expanded location will
/// be at the first expansion of the argument.
/// e.g.
/// MY_MACRO(foo);
/// ^
/// Passing a file location pointing at 'foo', will yield a macro location
/// where 'foo' was expanded into.
SourceLocation getMacroArgExpandedLocation(SourceLocation Loc) const;
/// Determines the order of 2 source locations in the translation unit.
///
/// \returns true if LHS source location comes before RHS, false otherwise.
bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const;
/// Determines whether the two decomposed source location is in the
/// same translation unit. As a byproduct, it also calculates the order
/// of the source locations in case they are in the same TU.
///
/// \returns Pair of bools the first component is true if the two locations
/// are in the same TU. The second bool is true if the first is true
/// and \p LOffs is before \p ROffs.
std::pair<bool, bool>
isInTheSameTranslationUnit(std::pair<FileID, unsigned> &LOffs,
std::pair<FileID, unsigned> &ROffs) const;
/// Determines the order of 2 source locations in the "source location
/// address space".
bool isBeforeInSLocAddrSpace(SourceLocation LHS, SourceLocation RHS) const {
return isBeforeInSLocAddrSpace(LHS, RHS.getOffset());
}
/// Determines the order of a source location and a source location
/// offset in the "source location address space".
///
/// Note that we always consider source locations loaded from
bool isBeforeInSLocAddrSpace(SourceLocation LHS,
SourceLocation::UIntTy RHS) const {
SourceLocation::UIntTy LHSOffset = LHS.getOffset();
bool LHSLoaded = LHSOffset >= CurrentLoadedOffset;
bool RHSLoaded = RHS >= CurrentLoadedOffset;
if (LHSLoaded == RHSLoaded)
return LHSOffset < RHS;
return LHSLoaded;
}
/// Return true if the Point is within Start and End.
bool isPointWithin(SourceLocation Location, SourceLocation Start,
SourceLocation End) const {
return Location == Start || Location == End ||
(isBeforeInTranslationUnit(Start, Location) &&
isBeforeInTranslationUnit(Location, End));
}
// Iterators over FileInfos.
using fileinfo_iterator =
llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*>::const_iterator;
fileinfo_iterator fileinfo_begin() const { return FileInfos.begin(); }
fileinfo_iterator fileinfo_end() const { return FileInfos.end(); }
bool hasFileInfo(const FileEntry *File) const {
return FileInfos.find(File) != FileInfos.end();
}
/// Print statistics to stderr.
void PrintStats() const;
void dump() const;
/// Get the number of local SLocEntries we have.
unsigned local_sloc_entry_size() const { return LocalSLocEntryTable.size(); }
/// Get a local SLocEntry. This is exposed for indexing.
const SrcMgr::SLocEntry &getLocalSLocEntry(unsigned Index) const {
assert(Index < LocalSLocEntryTable.size() && "Invalid index");
return LocalSLocEntryTable[Index];
}
/// Get the number of loaded SLocEntries we have.
unsigned loaded_sloc_entry_size() const { return LoadedSLocEntryTable.size();}
/// Get a loaded SLocEntry. This is exposed for indexing.
const SrcMgr::SLocEntry &getLoadedSLocEntry(unsigned Index,
bool *Invalid = nullptr) const {
assert(Index < LoadedSLocEntryTable.size() && "Invalid index");
if (SLocEntryLoaded[Index])
return LoadedSLocEntryTable[Index];
return loadSLocEntry(Index, Invalid);
}
const SrcMgr::SLocEntry &getSLocEntry(FileID FID,
bool *Invalid = nullptr) const {
if (FID.ID == 0 || FID.ID == -1) {
if (Invalid) *Invalid = true;
return LocalSLocEntryTable[0];
}
return getSLocEntryByID(FID.ID, Invalid);
}
SourceLocation::UIntTy getNextLocalOffset() const { return NextLocalOffset; }
void setExternalSLocEntrySource(ExternalSLocEntrySource *Source) {
assert(LoadedSLocEntryTable.empty() &&
"Invalidating existing loaded entries");
ExternalSLocEntries = Source;
}
/// Allocate a number of loaded SLocEntries, which will be actually
/// loaded on demand from the external source.
///
/// NumSLocEntries will be allocated, which occupy a total of TotalSize space
/// in the global source view. The lowest ID and the base offset of the
/// entries will be returned.
std::pair<int, SourceLocation::UIntTy>
AllocateLoadedSLocEntries(unsigned NumSLocEntries,
SourceLocation::UIntTy TotalSize);
/// Returns true if \p Loc came from a PCH/Module.
bool isLoadedSourceLocation(SourceLocation Loc) const {
return Loc.getOffset() >= CurrentLoadedOffset;
}
/// Returns true if \p Loc did not come from a PCH/Module.
bool isLocalSourceLocation(SourceLocation Loc) const {
return Loc.getOffset() < NextLocalOffset;
}
/// Returns true if \p FID came from a PCH/Module.
bool isLoadedFileID(FileID FID) const {
assert(FID.ID != -1 && "Using FileID sentinel value");
return FID.ID < 0;
}
/// Returns true if \p FID did not come from a PCH/Module.
bool isLocalFileID(FileID FID) const {
return !isLoadedFileID(FID);
}
/// Gets the location of the immediate macro caller, one level up the stack
/// toward the initial macro typed into the source.
SourceLocation getImmediateMacroCallerLoc(SourceLocation Loc) const {
if (!Loc.isMacroID()) return Loc;
// When we have the location of (part of) an expanded parameter, its
// spelling location points to the argument as expanded in the macro call,
// and therefore is used to locate the macro caller.
if (isMacroArgExpansion(Loc))
return getImmediateSpellingLoc(Loc);
// Otherwise, the caller of the macro is located where this macro is
// expanded (while the spelling is part of the macro definition).
return getImmediateExpansionRange(Loc).getBegin();
}
/// \return Location of the top-level macro caller.
SourceLocation getTopMacroCallerLoc(SourceLocation Loc) const;
private:
friend class ASTReader;
friend class ASTWriter;
llvm::MemoryBufferRef getFakeBufferForRecovery() const;
SrcMgr::ContentCache &getFakeContentCacheForRecovery() const;
const SrcMgr::SLocEntry &loadSLocEntry(unsigned Index, bool *Invalid) const;
const SrcMgr::SLocEntry *getSLocEntryOrNull(FileID FID) const {
bool Invalid = false;
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID, &Invalid);
return Invalid ? nullptr : &Entry;
}
const SrcMgr::SLocEntry *getSLocEntryForFile(FileID FID) const {
if (auto *Entry = getSLocEntryOrNull(FID))
if (Entry->isFile())
return Entry;
return nullptr;
}
/// Get the entry with the given unwrapped FileID.
/// Invalid will not be modified for Local IDs.
const SrcMgr::SLocEntry &getSLocEntryByID(int ID,
bool *Invalid = nullptr) const {
assert(ID != -1 && "Using FileID sentinel value");
if (ID < 0)
return getLoadedSLocEntryByID(ID, Invalid);
return getLocalSLocEntry(static_cast<unsigned>(ID));
}
const SrcMgr::SLocEntry &
getLoadedSLocEntryByID(int ID, bool *Invalid = nullptr) const {
return getLoadedSLocEntry(static_cast<unsigned>(-ID - 2), Invalid);
}
/// Implements the common elements of storing an expansion info struct into
/// the SLocEntry table and producing a source location that refers to it.
SourceLocation
createExpansionLocImpl(const SrcMgr::ExpansionInfo &Expansion,
unsigned Length, int LoadedID = 0,
SourceLocation::UIntTy LoadedOffset = 0);
/// Return true if the specified FileID contains the
/// specified SourceLocation offset. This is a very hot method.
inline bool isOffsetInFileID(FileID FID,
SourceLocation::UIntTy SLocOffset) const {
const SrcMgr::SLocEntry &Entry = getSLocEntry(FID);
// If the entry is after the offset, it can't contain it.
if (SLocOffset < Entry.getOffset()) return false;
// If this is the very last entry then it does.
if (FID.ID == -2)
return true;
// If it is the last local entry, then it does if the location is local.
if (FID.ID+1 == static_cast<int>(LocalSLocEntryTable.size()))
return SLocOffset < NextLocalOffset;
// Otherwise, the entry after it has to not include it. This works for both
// local and loaded entries.
return SLocOffset < getSLocEntryByID(FID.ID+1).getOffset();
}
/// Returns the previous in-order FileID or an invalid FileID if there
/// is no previous one.
FileID getPreviousFileID(FileID FID) const;
/// Returns the next in-order FileID or an invalid FileID if there is
/// no next one.
FileID getNextFileID(FileID FID) const;
/// Create a new fileID for the specified ContentCache and
/// include position.
///
/// This works regardless of whether the ContentCache corresponds to a
/// file or some other input source.
FileID createFileIDImpl(SrcMgr::ContentCache &File, StringRef Filename,
SourceLocation IncludePos,
SrcMgr::CharacteristicKind DirCharacter, int LoadedID,
SourceLocation::UIntTy LoadedOffset);
SrcMgr::ContentCache &getOrCreateContentCache(FileEntryRef SourceFile,
bool isSystemFile = false);
/// Create a new ContentCache for the specified memory buffer.
SrcMgr::ContentCache &
createMemBufferContentCache(std::unique_ptr<llvm::MemoryBuffer> Buf);
FileID getFileIDSlow(SourceLocation::UIntTy SLocOffset) const;
FileID getFileIDLocal(SourceLocation::UIntTy SLocOffset) const;
FileID getFileIDLoaded(SourceLocation::UIntTy SLocOffset) const;
SourceLocation getExpansionLocSlowCase(SourceLocation Loc) const;
SourceLocation getSpellingLocSlowCase(SourceLocation Loc) const;
SourceLocation getFileLocSlowCase(SourceLocation Loc) const;
std::pair<FileID, unsigned>
getDecomposedExpansionLocSlowCase(const SrcMgr::SLocEntry *E) const;
std::pair<FileID, unsigned>
getDecomposedSpellingLocSlowCase(const SrcMgr::SLocEntry *E,
unsigned Offset) const;
void computeMacroArgsCache(MacroArgsMap &MacroArgsCache, FileID FID) const;
void associateFileChunkWithMacroArgExp(MacroArgsMap &MacroArgsCache,
FileID FID,
SourceLocation SpellLoc,
SourceLocation ExpansionLoc,
unsigned ExpansionLength) const;
};
/// Comparison function object.
template<typename T>
class BeforeThanCompare;
/// Compare two source locations.
template<>
class BeforeThanCompare<SourceLocation> {
SourceManager &SM;
public:
explicit BeforeThanCompare(SourceManager &SM) : SM(SM) {}
bool operator()(SourceLocation LHS, SourceLocation RHS) const {
return SM.isBeforeInTranslationUnit(LHS, RHS);
}
};
/// Compare two non-overlapping source ranges.
template<>
class BeforeThanCompare<SourceRange> {
SourceManager &SM;
public:
explicit BeforeThanCompare(SourceManager &SM) : SM(SM) {}
bool operator()(SourceRange LHS, SourceRange RHS) const {
return SM.isBeforeInTranslationUnit(LHS.getBegin(), RHS.getBegin());
}
};
/// SourceManager and necessary depdencies (e.g. VFS, FileManager) for a single
/// in-memorty file.
class SourceManagerForFile {
public:
/// Creates SourceManager and necessary depdencies (e.g. VFS, FileManager).
/// The main file in the SourceManager will be \p FileName with \p Content.
SourceManagerForFile(StringRef FileName, StringRef Content);
SourceManager &get() {
assert(SourceMgr);
return *SourceMgr;
}
private:
// The order of these fields are important - they should be in the same order
// as they are created in `createSourceManagerForFile` so that they can be
// deleted in the reverse order as they are created.
std::unique_ptr<FileManager> FileMgr;
std::unique_ptr<DiagnosticsEngine> Diagnostics;
std::unique_ptr<SourceManager> SourceMgr;
};
} // namespace clang
#endif // LLVM_CLANG_BASIC_SOURCEMANAGER_H