mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This rare crash happens when 1. A diagnostic is reported when building a Clang module. 2. The diagnostic is mapped to a Swift diagnostic by mirroring the Clang source buffer as a Swift source buffer (via non-owning reference). 3. The Clang CompilerInstance used specifically to build the module is destroyed. 4. Some /new/ buffer is allocated in the same memory spot as the old buffer. 5. Some new Clang diagnostic occurs in the new buffer. 6. The Swift source manager asserts when trying to set up a virtual name for the diagnostic in the second imported buffer, because there's already a name for that region. The fix, because we don't expect diagnostics from modules to appear very often, is to keep any clang::SourceManagers alive if diagnostics are emitted in their buffers. We can revisit this if/when Swift's source manager (currently built on llvm::SourceMgr) has the ability to remove buffers. Many thanks to Greg for noticing the problem, tracking it down, and providing a diff to make it fail reproducibly under GuardMalloc. I've tried to preserve the spirit of that diff in the new logic in ~SourceManager, which will also fail reliably with GuardMalloc (and probably ASan). rdar://problem/18285805 Swift SVN r21958
228 lines
7.6 KiB
C++
228 lines
7.6 KiB
C++
//===--- SourceManager.h - Manager for Source Buffers -----------*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SOURCEMANAGER_H
|
|
#define SWIFT_SOURCEMANAGER_H
|
|
|
|
#include "swift/Basic/SourceLoc.h"
|
|
#include "swift/Basic/Optional.h"
|
|
#include "llvm/ADT/StringMap.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include <map>
|
|
|
|
namespace swift {
|
|
|
|
/// \brief This class manages and owns source buffers.
|
|
class SourceManager {
|
|
llvm::SourceMgr LLVMSourceMgr;
|
|
|
|
unsigned CodeCompletionBufferID = 0U;
|
|
unsigned CodeCompletionOffset;
|
|
|
|
/// \brief The buffer ID where a hashbang line #! is allowed.
|
|
unsigned HashbangBufferID = 0U;
|
|
|
|
/// Associates buffer identifiers to buffer IDs.
|
|
llvm::StringMap<unsigned> BufIdentIDMap;
|
|
|
|
// #line directive handling.
|
|
struct VirtualFile {
|
|
CharSourceRange Range;
|
|
std::string Name;
|
|
int LineOffset;
|
|
};
|
|
std::map<const char *, VirtualFile> VirtualFiles;
|
|
mutable std::pair<const char *, const VirtualFile*> CachedVFile = {};
|
|
|
|
public:
|
|
~SourceManager();
|
|
|
|
llvm::SourceMgr &getLLVMSourceMgr() {
|
|
return LLVMSourceMgr;
|
|
}
|
|
const llvm::SourceMgr &getLLVMSourceMgr() const {
|
|
return LLVMSourceMgr;
|
|
}
|
|
|
|
void setCodeCompletionPoint(unsigned BufferID, unsigned Offset) {
|
|
assert(BufferID != 0U && "Buffer should be valid");
|
|
|
|
CodeCompletionBufferID = BufferID;
|
|
CodeCompletionOffset = Offset;
|
|
}
|
|
|
|
unsigned getCodeCompletionBufferID() const {
|
|
return CodeCompletionBufferID;
|
|
}
|
|
|
|
unsigned getCodeCompletionOffset() const {
|
|
return CodeCompletionOffset;
|
|
}
|
|
|
|
SourceLoc getCodeCompletionLoc() const;
|
|
|
|
void setHashbangBufferID(unsigned BufferID) {
|
|
assert(HashbangBufferID == 0U && "Hashbang buffer ID already set");
|
|
HashbangBufferID = BufferID;
|
|
}
|
|
|
|
unsigned getHashbangBufferID() const {
|
|
return HashbangBufferID;
|
|
}
|
|
|
|
/// Returns true if \c LHS is before \c RHS in the source buffer.
|
|
bool isBeforeInBuffer(SourceLoc LHS, SourceLoc RHS) const {
|
|
return LHS.Value.getPointer() < RHS.Value.getPointer();
|
|
}
|
|
|
|
/// Returns true if range \c R contains the location \c Loc. The location
|
|
/// \c Loc should point at the beginning of the token.
|
|
bool rangeContainsTokenLoc(SourceRange R, SourceLoc Loc) const {
|
|
return Loc == R.Start || Loc == R.End ||
|
|
(isBeforeInBuffer(R.Start, Loc) && isBeforeInBuffer(Loc, R.End));
|
|
}
|
|
|
|
/// Returns true if range \c Enclosing contains the range \c Inner.
|
|
bool rangeContains(SourceRange Enclosing, SourceRange Inner) const {
|
|
return rangeContainsTokenLoc(Enclosing, Inner.Start) &&
|
|
rangeContainsTokenLoc(Enclosing, Inner.End);
|
|
}
|
|
|
|
/// Returns the buffer ID for the specified *valid* location.
|
|
///
|
|
/// Because a valid source location always corresponds to a source buffer,
|
|
/// this routine always returns a valid buffer ID.
|
|
unsigned findBufferContainingLoc(SourceLoc Loc) const;
|
|
|
|
/// Adds a memory buffer to the SourceManager, taking ownership of it.
|
|
unsigned addNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer);
|
|
|
|
/// Add a #line-defined virtual file region.
|
|
///
|
|
/// By default, this region continues to the end of the buffer.
|
|
///
|
|
/// \returns True if the new file was added, false if the file already exists.
|
|
/// The name and line offset must match exactly in that case.
|
|
///
|
|
/// \sa closeVirtualFile.
|
|
bool openVirtualFile(SourceLoc loc, StringRef name, int lineOffset);
|
|
|
|
/// Close a #line-defined virtual file region.
|
|
void closeVirtualFile(SourceLoc end);
|
|
|
|
/// Creates a copy of a \c MemoryBuffer and adds it to the \c SourceManager,
|
|
/// taking ownership of the copy.
|
|
unsigned addMemBufferCopy(llvm::MemoryBuffer *Buffer);
|
|
|
|
/// Creates and adds a memory buffer to the \c SourceManager, taking
|
|
/// ownership of the newly created copy.
|
|
///
|
|
/// \p InputData and \p BufIdentifier are copied, so that this memory can go
|
|
/// away as soon as this function returns.
|
|
unsigned addMemBufferCopy(StringRef InputData, StringRef BufIdentifier = "");
|
|
|
|
/// Returns a buffer ID for a previously added buffer with the given
|
|
/// buffer identifier, or Nothing if there is no such buffer.
|
|
Optional<unsigned> getIDForBufferIdentifier(StringRef BufIdentifier);
|
|
|
|
/// Returns the identifier for the buffer with the given ID.
|
|
///
|
|
/// \p BufferID must be a valid buffer ID.
|
|
const char *getIdentifierForBuffer(unsigned BufferID) const;
|
|
|
|
/// \brief Returns a SourceRange covering the entire specified buffer.
|
|
///
|
|
/// Note that the start location might not point at the first token: it
|
|
/// might point at whitespace or a comment.
|
|
CharSourceRange getRangeForBuffer(unsigned BufferID) const;
|
|
|
|
/// Returns the SourceLoc for the beginning of the specified buffer
|
|
/// (at offset zero).
|
|
///
|
|
/// Note that the resulting location might not point at the first token: it
|
|
/// might point at whitespace or a comment.
|
|
SourceLoc getLocForBufferStart(unsigned BufferID) const {
|
|
return getRangeForBuffer(BufferID).getStart();
|
|
}
|
|
|
|
/// \brief Returns the offset in bytes for the given valid source location.
|
|
unsigned getLocOffsetInBuffer(SourceLoc Loc, unsigned BufferID) const;
|
|
|
|
/// \brief Returns the distance in bytes between the given valid source
|
|
/// locations.
|
|
unsigned getByteDistance(SourceLoc Start, SourceLoc End) const;
|
|
|
|
/// Returns the SourceLoc for the byte offset in the specified buffer.
|
|
SourceLoc getLocForOffset(unsigned BufferID, unsigned Offset) const {
|
|
return getLocForBufferStart(BufferID).getAdvancedLoc(Offset);
|
|
}
|
|
|
|
/// Returns the identifier string for the buffer containing the given source
|
|
/// location.
|
|
///
|
|
/// This respects #line directives.
|
|
const char *getBufferIdentifierForLoc(SourceLoc Loc) const {
|
|
if (auto VFile = getVirtualFile(Loc))
|
|
return VFile->Name.c_str();
|
|
else
|
|
return getIdentifierForBuffer(findBufferContainingLoc(Loc));
|
|
}
|
|
|
|
/// Returns the line and column represented by the given source location.
|
|
///
|
|
/// If \p BufferID is provided, \p Loc must come from that source buffer.
|
|
///
|
|
/// This respects #line directives.
|
|
std::pair<unsigned, unsigned>
|
|
getLineAndColumn(SourceLoc Loc, unsigned BufferID = 0) const {
|
|
assert(Loc.isValid());
|
|
int LineOffset = getLineOffset(Loc);
|
|
int l, c;
|
|
std::tie(l, c) = LLVMSourceMgr.getLineAndColumn(Loc.Value, BufferID);
|
|
assert(LineOffset+l > 0 && "bogus line offset");
|
|
return { LineOffset + l, c };
|
|
}
|
|
|
|
/// Returns the real line number for a source location.
|
|
///
|
|
/// If \p BufferID is provided, \p Loc must come from that source buffer.
|
|
///
|
|
/// This does not respect #line directives.
|
|
unsigned getLineNumber(SourceLoc Loc, unsigned BufferID = 0) const {
|
|
assert(Loc.isValid());
|
|
return LLVMSourceMgr.FindLineNumber(Loc.Value, BufferID);
|
|
}
|
|
|
|
StringRef extractText(CharSourceRange Range,
|
|
Optional<unsigned> BufferID = {}) const;
|
|
|
|
llvm::SMDiagnostic GetMessage(SourceLoc Loc, llvm::SourceMgr::DiagKind Kind,
|
|
const Twine &Msg,
|
|
ArrayRef<llvm::SMRange> Ranges,
|
|
ArrayRef<llvm::SMFixIt> FixIts) const;
|
|
|
|
private:
|
|
const VirtualFile *getVirtualFile(SourceLoc Loc) const;
|
|
|
|
int getLineOffset(SourceLoc Loc) const {
|
|
if (auto VFile = getVirtualFile(Loc))
|
|
return VFile->LineOffset;
|
|
else
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
} // namespace swift
|
|
|
|
#endif // LLVM_SWIFT_SOURCEMANAGER_H
|
|
|