Clean up the source manager
This commit is contained in:
parent
91aa0148d6
commit
5ff52c277a
|
@ -45,25 +45,30 @@
|
||||||
|
|
||||||
namespace serene {
|
namespace serene {
|
||||||
class SereneContext;
|
class SereneContext;
|
||||||
class SMDiagnostic;
|
|
||||||
|
|
||||||
|
/// This class is quite similar to the `llvm::SourceMgr` in functionality. We
|
||||||
|
/// even borrowed some of the code from the original implementation but removed
|
||||||
|
/// a lot of code that ar irrelevant to us.
|
||||||
|
///
|
||||||
|
/// SouceMgr is responsible for finding a namespace in the `loadPaths` and read
|
||||||
|
/// the content of the `.srn` (or any of the `DEFAULT_SUFFIX`) into a
|
||||||
|
/// `llvm::MemoryBuffer` embedded in a `SrcBuffer` object as the owner of the
|
||||||
|
/// source files and then it will call the `reader` on the buffer to parse it
|
||||||
|
/// and create the actual `Namespace` object from the parsed AST.
|
||||||
|
///
|
||||||
|
/// Later on, whenever we need to refer to the source file of a namespace for
|
||||||
|
/// diagnosis purposes or any other purpose we can use the functions in this
|
||||||
|
/// class to get hold of a pointer to a specific `reader::Location` of the
|
||||||
|
/// buffer.
|
||||||
|
///
|
||||||
|
/// Note: Unlike the original version, SourceMgr does not handle the diagnostics
|
||||||
|
/// and it uses the Serene's `DiagnosticEngine` for that matter.
|
||||||
class SourceMgr {
|
class SourceMgr {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// TODO: Make it a vector of supported suffixes
|
||||||
std::string DEFAULT_SUFFIX = "srn";
|
std::string DEFAULT_SUFFIX = "srn";
|
||||||
|
|
||||||
enum DiagKind {
|
|
||||||
DK_Error,
|
|
||||||
DK_Warning,
|
|
||||||
DK_Remark,
|
|
||||||
DK_Note,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Clients that want to handle their own diagnostics in a custom way can
|
|
||||||
/// register a function pointer+context as a diagnostic handler.
|
|
||||||
/// It gets called each time PrintMessage is invoked.
|
|
||||||
using DiagHandlerTy = void (*)(const SMDiagnostic &, void *context);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SrcBuffer {
|
struct SrcBuffer {
|
||||||
/// The memory buffer for the file.
|
/// The memory buffer for the file.
|
||||||
|
@ -90,11 +95,13 @@ private:
|
||||||
/// Return a pointer to the first character of the specified line number or
|
/// Return a pointer to the first character of the specified line number or
|
||||||
/// null if the line number is invalid.
|
/// null if the line number is invalid.
|
||||||
const char *getPointerForLineNumber(unsigned lineNo) const;
|
const char *getPointerForLineNumber(unsigned lineNo) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char *getPointerForLineNumberSpecialized(unsigned lineNo) const;
|
const char *getPointerForLineNumberSpecialized(unsigned lineNo) const;
|
||||||
|
|
||||||
/// This is the location of the parent include, or null if at the top level.
|
/// This is the location of the parent import or unknown location if it is
|
||||||
reader::LocationRange includeLoc;
|
/// the main namespace
|
||||||
|
reader::LocationRange importLoc;
|
||||||
|
|
||||||
SrcBuffer() = default;
|
SrcBuffer() = default;
|
||||||
SrcBuffer(SrcBuffer &&);
|
SrcBuffer(SrcBuffer &&);
|
||||||
|
@ -106,18 +113,13 @@ private:
|
||||||
/// This is all of the buffers that we are reading from.
|
/// This is all of the buffers that we are reading from.
|
||||||
std::vector<SrcBuffer> buffers;
|
std::vector<SrcBuffer> buffers;
|
||||||
|
|
||||||
|
/// A hashtable that works as an index from namespace names to the buffer
|
||||||
|
/// position it the `buffer`
|
||||||
llvm::StringMap<unsigned> nsTable;
|
llvm::StringMap<unsigned> nsTable;
|
||||||
|
|
||||||
/// A mapping from the ns name to buffer id. The ns name is a reference to
|
|
||||||
/// the actual name that is stored in the Namespace instance.
|
|
||||||
llvm::DenseMap<llvm::StringRef, unsigned> nsToBufId;
|
|
||||||
|
|
||||||
// This is the list of directories we should search for include files in.
|
// This is the list of directories we should search for include files in.
|
||||||
std::vector<std::string> loadPaths;
|
std::vector<std::string> loadPaths;
|
||||||
|
|
||||||
DiagHandlerTy diagHandler = nullptr;
|
|
||||||
void *diagContext = nullptr;
|
|
||||||
|
|
||||||
bool isValidBufferID(unsigned i) const { return i && i <= buffers.size(); }
|
bool isValidBufferID(unsigned i) const { return i && i <= buffers.size(); }
|
||||||
|
|
||||||
/// Converts the ns name to a partial path by replacing the dots with slashes
|
/// Converts the ns name to a partial path by replacing the dots with slashes
|
||||||
|
@ -131,23 +133,18 @@ public:
|
||||||
SourceMgr &operator=(SourceMgr &&) = default;
|
SourceMgr &operator=(SourceMgr &&) = default;
|
||||||
~SourceMgr() = default;
|
~SourceMgr() = default;
|
||||||
|
|
||||||
|
/// Set the `loadPaths` to the given \p dirs. `loadPaths` is a vector of
|
||||||
|
/// directories that Serene will look in order to find a file that constains a
|
||||||
|
/// namespace which it is looking for.
|
||||||
void setLoadPaths(const std::vector<std::string> &dirs) { loadPaths = dirs; }
|
void setLoadPaths(const std::vector<std::string> &dirs) { loadPaths = dirs; }
|
||||||
|
|
||||||
/// Specify a diagnostic handler to be invoked every time PrintMessage is
|
/// Return a reference to a `SrcBuffer` with the given ID \p i.
|
||||||
/// called. \p Ctx is passed into the handler when it is invoked.
|
|
||||||
void setDiagHandler(DiagHandlerTy dh, void *ctx = nullptr) {
|
|
||||||
diagHandler = dh;
|
|
||||||
diagContext = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
DiagHandlerTy getDiagHandler() const { return diagHandler; }
|
|
||||||
void *getDiagContext() const { return diagContext; }
|
|
||||||
|
|
||||||
const SrcBuffer &getBufferInfo(unsigned i) const {
|
const SrcBuffer &getBufferInfo(unsigned i) const {
|
||||||
assert(isValidBufferID(i));
|
assert(isValidBufferID(i));
|
||||||
return buffers[i - 1];
|
return buffers[i - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a reference to a `SrcBuffer` with the given namspace name \p ns.
|
||||||
const SrcBuffer &getBufferInfo(llvm::StringRef ns) const {
|
const SrcBuffer &getBufferInfo(llvm::StringRef ns) const {
|
||||||
auto bufferId = nsTable.lookup(ns);
|
auto bufferId = nsTable.lookup(ns);
|
||||||
|
|
||||||
|
@ -160,6 +157,8 @@ public:
|
||||||
return buffers[bufferId - 1];
|
return buffers[bufferId - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a pointer to the internal `llvm::MemoryBuffer` of the `SrcBuffer`
|
||||||
|
/// with the given ID \p i.
|
||||||
const llvm::MemoryBuffer *getMemoryBuffer(unsigned i) const {
|
const llvm::MemoryBuffer *getMemoryBuffer(unsigned i) const {
|
||||||
assert(isValidBufferID(i));
|
assert(isValidBufferID(i));
|
||||||
return buffers[i - 1].buffer.get();
|
return buffers[i - 1].buffer.get();
|
||||||
|
@ -167,150 +166,21 @@ public:
|
||||||
|
|
||||||
unsigned getNumBuffers() const { return buffers.size(); }
|
unsigned getNumBuffers() const { return buffers.size(); }
|
||||||
|
|
||||||
unsigned getMainFileID() const {
|
|
||||||
assert(getNumBuffers());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reader::LocationRange getParentIncludeLoc(unsigned i) const {
|
|
||||||
// assert(isValidBufferID(i));
|
|
||||||
// return buffers[i - 1].includeLoc;
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Add a new source buffer to this source manager. This takes ownership of
|
/// Add a new source buffer to this source manager. This takes ownership of
|
||||||
/// the memory buffer.
|
/// the memory buffer.
|
||||||
unsigned AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
|
unsigned AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
|
||||||
reader::LocationRange includeLoc);
|
reader::LocationRange includeLoc);
|
||||||
/// Search for a file with the specified name in the current directory or in
|
|
||||||
/// one of the IncludeDirs.
|
/// Lookup for a file containing the namespace definition of with given
|
||||||
|
/// namespace name \p name and throw an error. In case that the file exists,
|
||||||
|
/// use the parser to read the file and create an AST from it. Then create a
|
||||||
|
/// namespace, set the its AST to the AST that we just read from the file and
|
||||||
|
/// return a shared pointer to the namespace.
|
||||||
///
|
///
|
||||||
/// If no file is found, this returns 0, otherwise it returns the buffer ID
|
/// \p importLoc is a location in the source code where the give namespace is
|
||||||
/// of the stacked file. The full path to the included file can be found in
|
/// imported.
|
||||||
/// \p IncludedFile.
|
|
||||||
// unsigned AddIncludeFile(const std::string &filename, llvm::SMLoc
|
|
||||||
// includeLoc,
|
|
||||||
// std::string &includedFile);
|
|
||||||
|
|
||||||
NSPtr readNamespace(SereneContext &ctx, std::string name,
|
NSPtr readNamespace(SereneContext &ctx, std::string name,
|
||||||
reader::LocationRange importLoc, bool entryNS = false);
|
reader::LocationRange importLoc);
|
||||||
|
|
||||||
// /// Return the ID of the buffer containing the specified location.
|
|
||||||
// ///
|
|
||||||
// /// 0 is returned if the buffer is not found.
|
|
||||||
// unsigned FindBufferContainingLoc(llvm::SMLoc loc) const;
|
|
||||||
|
|
||||||
// /// Find the line number for the specified location in the specified file.
|
|
||||||
// /// This is not a fast method.
|
|
||||||
// unsigned FindLineNumber(llvm::SMLoc loc, unsigned bufferID = 0) const {
|
|
||||||
// return getLineAndColumn(loc, bufferID).first;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Find the line and column number for the specified location in the
|
|
||||||
// /// specified file. This is not a fast method.
|
|
||||||
// std::pair<unsigned, unsigned> getLineAndColumn(llvm::SMLoc loc,
|
|
||||||
// unsigned bufferID = 0)
|
|
||||||
// const;
|
|
||||||
|
|
||||||
// /// Get a string with the \p llvm::SMLoc filename and line number
|
|
||||||
// /// formatted in the standard style.
|
|
||||||
// std::string getFormattedLocationNoOffset(llvm::SMLoc loc,
|
|
||||||
// bool includePath = false) const;
|
|
||||||
|
|
||||||
// /// Given a line and column number in a mapped buffer, turn it into an
|
|
||||||
// /// llvm::SMLoc. This will return a null llvm::SMLoc if the line/column
|
|
||||||
// /// location is invalid.
|
|
||||||
// llvm::SMLoc FindLocForLineAndColumn(unsigned bufferID, unsigned lineNo,
|
|
||||||
// unsigned colNo);
|
|
||||||
|
|
||||||
// /// Emit a message about the specified location with the specified string.
|
|
||||||
// ///
|
|
||||||
// /// \param ShowColors Display colored messages if output is a terminal and
|
|
||||||
// /// the default error handler is used.
|
|
||||||
// void PrintMessage(llvm::raw_ostream &os, llvm::SMLoc loc, DiagKind kind,
|
|
||||||
// const llvm::Twine &msg,
|
|
||||||
// llvm::ArrayRef<llvm::SMRange> ranges = {},
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts = {},
|
|
||||||
// bool showColors = true) const;
|
|
||||||
|
|
||||||
// /// Emits a diagnostic to llvm::errs().
|
|
||||||
// void PrintMessage(llvm::SMLoc loc, DiagKind kind, const llvm::Twine &msg,
|
|
||||||
// llvm::ArrayRef<llvm::SMRange> ranges = {},
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts = {},
|
|
||||||
// bool showColors = true) const;
|
|
||||||
|
|
||||||
// /// Emits a manually-constructed diagnostic to the given output stream.
|
|
||||||
// ///
|
|
||||||
// /// \param ShowColors Display colored messages if output is a terminal and
|
|
||||||
// /// the default error handler is used.
|
|
||||||
// void PrintMessage(llvm::raw_ostream &os, const SMDiagnostic &diagnostic,
|
|
||||||
// bool showColors = true) const;
|
|
||||||
|
|
||||||
// /// Return an SMDiagnostic at the specified location with the specified
|
|
||||||
// /// string.
|
|
||||||
// ///
|
|
||||||
// /// \param Msg If non-null, the kind of message (e.g., "error") which is
|
|
||||||
// /// prefixed to the message.
|
|
||||||
// SMDiagnostic GetMessage(llvm::SMLoc loc, DiagKind kind,
|
|
||||||
// const llvm::Twine &msg,
|
|
||||||
// llvm::ArrayRef<llvm::SMRange> ranges = {},
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts = {}) const;
|
|
||||||
|
|
||||||
// /// Prints the names of included files and the line of the file they were
|
|
||||||
// /// included from. A diagnostic handler can use this before printing its
|
|
||||||
// /// custom formatted message.
|
|
||||||
// ///
|
|
||||||
// /// \param IncludeLoc The location of the include.
|
|
||||||
// /// \param OS the raw_ostream to print on.
|
|
||||||
// void PrintIncludeStack(llvm::SMLoc includeLoc, llvm::raw_ostream &os)
|
|
||||||
// const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Instances of this class encapsulate one diagnostic report, allowing
|
|
||||||
/// printing to a raw_ostream as a caret diagnostic.
|
|
||||||
class SMDiagnostic {
|
|
||||||
const SourceMgr *sm = nullptr;
|
|
||||||
llvm::SMLoc loc;
|
|
||||||
std::string filename;
|
|
||||||
int lineNo = 0;
|
|
||||||
int columnNo = 0;
|
|
||||||
SourceMgr::DiagKind kind = SourceMgr::DK_Error;
|
|
||||||
std::string message, lineContents;
|
|
||||||
std::vector<std::pair<unsigned, unsigned>> ranges;
|
|
||||||
llvm::SmallVector<llvm::SMFixIt, 4> fixIts;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Null diagnostic.
|
|
||||||
SMDiagnostic() = default;
|
|
||||||
// Diagnostic with no location (e.g. file not found, command line arg error).
|
|
||||||
SMDiagnostic(llvm::StringRef filename, SourceMgr::DiagKind knd,
|
|
||||||
llvm::StringRef msg)
|
|
||||||
: filename(filename), lineNo(-1), columnNo(-1), kind(knd), message(msg) {}
|
|
||||||
|
|
||||||
// Diagnostic with a location.
|
|
||||||
SMDiagnostic(const SourceMgr &sm, llvm::SMLoc l, llvm::StringRef fn, int line,
|
|
||||||
int col, SourceMgr::DiagKind kind, llvm::StringRef msg,
|
|
||||||
llvm::StringRef lineStr,
|
|
||||||
llvm::ArrayRef<std::pair<unsigned, unsigned>> ranges,
|
|
||||||
llvm::ArrayRef<llvm::SMFixIt> fixIts = {});
|
|
||||||
|
|
||||||
const SourceMgr *getSourceMgr() const { return sm; }
|
|
||||||
llvm::SMLoc getLoc() const { return loc; }
|
|
||||||
llvm::StringRef getFilename() const { return filename; }
|
|
||||||
int getLineNo() const { return lineNo; }
|
|
||||||
int getColumnNo() const { return columnNo; }
|
|
||||||
SourceMgr::DiagKind getKind() const { return kind; }
|
|
||||||
llvm::StringRef getMessage() const { return message; }
|
|
||||||
llvm::StringRef getLineContents() const { return lineContents; }
|
|
||||||
llvm::ArrayRef<std::pair<unsigned, unsigned>> getRanges() const {
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFixIt(const llvm::SMFixIt &hint) { fixIts.push_back(hint); }
|
|
||||||
|
|
||||||
llvm::ArrayRef<llvm::SMFixIt> getFixIts() const { return fixIts; }
|
|
||||||
|
|
||||||
void print(const char *progName, llvm::raw_ostream &s, bool showColors = true,
|
|
||||||
bool showKindLabel = true) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace serene
|
}; // namespace serene
|
||||||
|
|
|
@ -24,22 +24,20 @@
|
||||||
|
|
||||||
#include "serene/source_mgr.h"
|
#include "serene/source_mgr.h"
|
||||||
|
|
||||||
#include "mlir/Support/LogicalResult.h"
|
|
||||||
#include "serene/errors/constants.h"
|
#include "serene/errors/constants.h"
|
||||||
#include "serene/namespace.h"
|
#include "serene/namespace.h"
|
||||||
#include "serene/reader/location.h"
|
#include "serene/reader/location.h"
|
||||||
#include "serene/reader/reader.h"
|
#include "serene/reader/reader.h"
|
||||||
#include "serene/utils.h"
|
#include "serene/utils.h"
|
||||||
|
|
||||||
#include "llvm/Support/MemoryBufferRef.h"
|
|
||||||
|
|
||||||
#include <llvm/Support/FormatVariadic.h>
|
#include <llvm/Support/FormatVariadic.h>
|
||||||
#include <llvm/Support/Locale.h>
|
#include <llvm/Support/Locale.h>
|
||||||
|
#include <llvm/Support/MemoryBufferRef.h>
|
||||||
#include <llvm/Support/Path.h>
|
#include <llvm/Support/Path.h>
|
||||||
|
#include <mlir/Support/LogicalResult.h>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
namespace serene {
|
namespace serene {
|
||||||
static const size_t tabStop = 8;
|
|
||||||
|
|
||||||
std::string inline SourceMgr::convertNamespaceToPath(std::string ns_name) {
|
std::string inline SourceMgr::convertNamespaceToPath(std::string ns_name) {
|
||||||
std::replace(ns_name.begin(), ns_name.end(), '.', '/');
|
std::replace(ns_name.begin(), ns_name.end(), '.', '/');
|
||||||
|
@ -52,23 +50,24 @@ std::string inline SourceMgr::convertNamespaceToPath(std::string ns_name) {
|
||||||
};
|
};
|
||||||
|
|
||||||
NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
||||||
reader::LocationRange importLoc, bool entryNS) {
|
reader::LocationRange importLoc) {
|
||||||
std::string includedFile;
|
std::string importedFile;
|
||||||
auto path = convertNamespaceToPath(name);
|
auto path = convertNamespaceToPath(name);
|
||||||
|
|
||||||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> newBufOrErr(
|
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> newBufOrErr(
|
||||||
std::make_error_code(std::errc::no_such_file_or_directory));
|
std::make_error_code(std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
SMGR_LOG("Attempt to load namespace: " + name);
|
SMGR_LOG("Attempt to load namespace: " + name);
|
||||||
|
|
||||||
// If the file didn't exist directly, see if it's in an include path.
|
// If the file didn't exist directly, see if it's in an include path.
|
||||||
for (unsigned i = 0, e = loadPaths.size(); i != e && !newBufOrErr; ++i) {
|
for (unsigned i = 0, e = loadPaths.size(); i != e && !newBufOrErr; ++i) {
|
||||||
|
|
||||||
// TODO: Ugh, Udgly, fix this using llvm::sys::path functions
|
// TODO: Ugh, Udgly, fix this using llvm::sys::path functions
|
||||||
includedFile = loadPaths[i] + llvm::sys::path::get_separator().data() +
|
importedFile = loadPaths[i] + llvm::sys::path::get_separator().data() +
|
||||||
path + "." + DEFAULT_SUFFIX;
|
path + "." + DEFAULT_SUFFIX;
|
||||||
|
|
||||||
SMGR_LOG("Try to load the ns from: " + includedFile);
|
SMGR_LOG("Try to load the ns from: " + importedFile);
|
||||||
newBufOrErr = llvm::MemoryBuffer::getFile(includedFile);
|
newBufOrErr = llvm::MemoryBuffer::getFile(importedFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newBufOrErr) {
|
if (!newBufOrErr) {
|
||||||
|
@ -96,7 +95,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
||||||
|
|
||||||
// Read the content of the buffer by passing it the reader
|
// Read the content of the buffer by passing it the reader
|
||||||
auto maybeAst = reader::read(ctx, buf->getBuffer(), name,
|
auto maybeAst = reader::read(ctx, buf->getBuffer(), name,
|
||||||
llvm::Optional(llvm::StringRef(includedFile)));
|
llvm::Optional(llvm::StringRef(importedFile)));
|
||||||
|
|
||||||
if (!maybeAst) {
|
if (!maybeAst) {
|
||||||
SMGR_LOG("Couldn't Read namespace: " + name)
|
SMGR_LOG("Couldn't Read namespace: " + name)
|
||||||
|
@ -105,7 +104,7 @@ NSPtr SourceMgr::readNamespace(SereneContext &ctx, std::string name,
|
||||||
|
|
||||||
// Create the NS and set the AST
|
// Create the NS and set the AST
|
||||||
auto ns =
|
auto ns =
|
||||||
makeNamespace(ctx, name, llvm::Optional(llvm::StringRef(includedFile)));
|
makeNamespace(ctx, name, llvm::Optional(llvm::StringRef(importedFile)));
|
||||||
|
|
||||||
if (mlir::failed(ns->setTree(maybeAst.getValue()))) {
|
if (mlir::failed(ns->setTree(maybeAst.getValue()))) {
|
||||||
SMGR_LOG("Couldn't set the AST for namespace: " + name)
|
SMGR_LOG("Couldn't set the AST for namespace: " + name)
|
||||||
|
@ -119,41 +118,11 @@ unsigned SourceMgr::AddNewSourceBuffer(std::unique_ptr<llvm::MemoryBuffer> f,
|
||||||
reader::LocationRange includeLoc) {
|
reader::LocationRange includeLoc) {
|
||||||
SrcBuffer nb;
|
SrcBuffer nb;
|
||||||
nb.buffer = std::move(f);
|
nb.buffer = std::move(f);
|
||||||
nb.includeLoc = includeLoc;
|
nb.importLoc = includeLoc;
|
||||||
buffers.push_back(std::move(nb));
|
buffers.push_back(std::move(nb));
|
||||||
return buffers.size();
|
return buffers.size();
|
||||||
};
|
};
|
||||||
|
|
||||||
// unsigned SourceMgr::AddIncludeFile(const std::string &filename,
|
|
||||||
// llvm::SMLoc includeLoc,
|
|
||||||
// std::string &includedFile) {
|
|
||||||
// includedFile = filename;
|
|
||||||
// llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> NewBufOrErr =
|
|
||||||
// llvm::MemoryBuffer::getFile(includedFile);
|
|
||||||
|
|
||||||
// // If the file didn't exist directly, see if it's in an include path.
|
|
||||||
// for (unsigned i = 0, e = loadPaths.size(); i != e && !NewBufOrErr; ++i) {
|
|
||||||
// includedFile =
|
|
||||||
// loadPaths[i] + llvm::sys::path::get_separator().data() + filename;
|
|
||||||
// NewBufOrErr = llvm::MemoryBuffer::getFile(includedFile);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!NewBufOrErr)
|
|
||||||
// return 0;
|
|
||||||
|
|
||||||
// return AddNewSourceBuffer(std::move(*NewBufOrErr), includeLoc);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// unsigned SourceMgr::FindBufferContainingLoc(llvm::SMLoc loc) const {
|
|
||||||
// for (unsigned i = 0, e = buffers.size(); i != e; ++i)
|
|
||||||
// if (loc.getPointer() >= buffers[i].buffer->getBufferStart() &&
|
|
||||||
// // Use <= here so that a pointer to the null at the end of the buffer
|
|
||||||
// // is included as part of the buffer.
|
|
||||||
// loc.getPointer() <= buffers[i].buffer->getBufferEnd())
|
|
||||||
// return i + 1;
|
|
||||||
// return 0;
|
|
||||||
// }
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
|
static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
|
||||||
llvm::MemoryBuffer *buffer) {
|
llvm::MemoryBuffer *buffer) {
|
||||||
|
@ -163,7 +132,10 @@ static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
|
||||||
// Lazily fill in the offset cache.
|
// Lazily fill in the offset cache.
|
||||||
auto *offsets = new std::vector<T>();
|
auto *offsets = new std::vector<T>();
|
||||||
size_t sz = buffer->getBufferSize();
|
size_t sz = buffer->getBufferSize();
|
||||||
|
|
||||||
|
// TODO: Replace this assert with a realtime check
|
||||||
assert(sz <= std::numeric_limits<T>::max());
|
assert(sz <= std::numeric_limits<T>::max());
|
||||||
|
|
||||||
llvm::StringRef s = buffer->getBuffer();
|
llvm::StringRef s = buffer->getBuffer();
|
||||||
for (size_t n = 0; n < sz; ++n) {
|
for (size_t n = 0; n < sz; ++n) {
|
||||||
if (s[n] == '\n')
|
if (s[n] == '\n')
|
||||||
|
@ -174,38 +146,6 @@ static std::vector<T> &GetOrCreateOffsetCache(void *&offsetCache,
|
||||||
return *offsets;
|
return *offsets;
|
||||||
}
|
}
|
||||||
|
|
||||||
// template <typename T>
|
|
||||||
// unsigned SourceMgr::SrcBuffer::getLineNumberSpecialized(const char *ptr)
|
|
||||||
// const {
|
|
||||||
// std::vector<T> &offsets =
|
|
||||||
// GetOrCreateOffsetCache<T>(offsetCache, buffer.get());
|
|
||||||
|
|
||||||
// const char *bufStart = buffer->getBufferStart();
|
|
||||||
// assert(ptr >= bufStart && ptr <= buffer->getBufferEnd());
|
|
||||||
// ptrdiff_t ptrDiff = ptr - bufStart;
|
|
||||||
// assert(ptrDiff >= 0 &&
|
|
||||||
// static_cast<size_t>(ptrDiff) <= std::numeric_limits<T>::max());
|
|
||||||
// T ptrOffset = static_cast<T>(ptrDiff);
|
|
||||||
|
|
||||||
// // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get
|
|
||||||
// // the line number.
|
|
||||||
// return llvm::lower_bound(offsets, ptrOffset) - offsets.begin() + 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Look up a given \p Ptr in in the buffer, determining which line it came
|
|
||||||
// /// from.
|
|
||||||
// unsigned SourceMgr::SrcBuffer::getLineNumber(const char *ptr) const {
|
|
||||||
// size_t sz = buffer->getBufferSize();
|
|
||||||
// if (sz <= std::numeric_limits<uint8_t>::max())
|
|
||||||
// return getLineNumberSpecialized<uint8_t>(ptr);
|
|
||||||
// else if (sz <= std::numeric_limits<uint16_t>::max())
|
|
||||||
// return getLineNumberSpecialized<uint16_t>(ptr);
|
|
||||||
// else if (sz <= std::numeric_limits<uint32_t>::max())
|
|
||||||
// return getLineNumberSpecialized<uint32_t>(ptr);
|
|
||||||
// else
|
|
||||||
// return getLineNumberSpecialized<uint64_t>(ptr);
|
|
||||||
// }
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
|
const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized(
|
||||||
unsigned lineNo) const {
|
unsigned lineNo) const {
|
||||||
|
@ -244,7 +184,7 @@ SourceMgr::SrcBuffer::getPointerForLineNumber(unsigned lineNo) const {
|
||||||
|
|
||||||
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&other)
|
SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&other)
|
||||||
: buffer(std::move(other.buffer)), offsetCache(other.offsetCache),
|
: buffer(std::move(other.buffer)), offsetCache(other.offsetCache),
|
||||||
includeLoc(other.includeLoc) {
|
importLoc(other.importLoc) {
|
||||||
other.offsetCache = nullptr;
|
other.offsetCache = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,443 +203,4 @@ SourceMgr::SrcBuffer::~SrcBuffer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::pair<unsigned, unsigned>
|
|
||||||
// SourceMgr::getLineAndColumn(llvm::SMLoc loc, unsigned bufferID) const {
|
|
||||||
// if (!bufferID)
|
|
||||||
// bufferID = FindBufferContainingLoc(loc);
|
|
||||||
// assert(bufferID && "Invalid location!");
|
|
||||||
|
|
||||||
// auto &sb = getBufferInfo(bufferID);
|
|
||||||
// const char *ptr = loc.getPointer();
|
|
||||||
|
|
||||||
// unsigned lineNo = sb.getLineNumber(ptr);
|
|
||||||
// const char *bufStart = sb.buffer->getBufferStart();
|
|
||||||
// size_t newlineOffs =
|
|
||||||
// llvm::StringRef(bufStart, ptr - bufStart).find_last_of("\n\r");
|
|
||||||
// if (newlineOffs == llvm::StringRef::npos)
|
|
||||||
// newlineOffs = ~(size_t)0;
|
|
||||||
// return std::make_pair(lineNo, ptr - bufStart - newlineOffs);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // FIXME: Note that the formatting of source locations is spread between
|
|
||||||
// // multiple functions, some in SourceMgr and some in SMDiagnostic. A better
|
|
||||||
// // solution would be a general-purpose source location formatter
|
|
||||||
// // in one of those two classes, or possibly in llvm::SMLoc.
|
|
||||||
|
|
||||||
// /// Get a string with the source location formatted in the standard
|
|
||||||
// /// style, but without the line offset. If \p IncludePath is true, the path
|
|
||||||
// /// is included. If false, only the file name and extension are included.
|
|
||||||
// std::string SourceMgr::getFormattedLocationNoOffset(llvm::SMLoc loc,
|
|
||||||
// bool includePath) const {
|
|
||||||
// auto bufferID = FindBufferContainingLoc(loc);
|
|
||||||
// assert(bufferID && "Invalid location!");
|
|
||||||
// auto fileSpec = getBufferInfo(bufferID).buffer->getBufferIdentifier();
|
|
||||||
|
|
||||||
// if (includePath) {
|
|
||||||
// return fileSpec.str() + ":" + std::to_string(FindLineNumber(loc,
|
|
||||||
// bufferID));
|
|
||||||
// } else {
|
|
||||||
// auto I = fileSpec.find_last_of("/\\");
|
|
||||||
// I = (I == fileSpec.size()) ? 0 : (I + 1);
|
|
||||||
// return fileSpec.substr(I).str() + ":" +
|
|
||||||
// std::to_string(FindLineNumber(loc, bufferID));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// /// Given a line and column number in a mapped buffer, turn it into an
|
|
||||||
// /// llvm::SMLoc. This will return a null llvm::SMLoc if the line/column
|
|
||||||
// location
|
|
||||||
// /// is invalid.
|
|
||||||
// llvm::SMLoc SourceMgr::FindLocForLineAndColumn(unsigned bufferID,
|
|
||||||
// unsigned lineNo,
|
|
||||||
// unsigned colNo) {
|
|
||||||
// auto &sb = getBufferInfo(bufferID);
|
|
||||||
// const char *ptr = sb.getPointerForLineNumber(lineNo);
|
|
||||||
// if (!ptr)
|
|
||||||
// return llvm::SMLoc();
|
|
||||||
|
|
||||||
// // We start counting line and column numbers from 1.
|
|
||||||
// if (colNo != 0)
|
|
||||||
// --colNo;
|
|
||||||
|
|
||||||
// // If we have a column number, validate it.
|
|
||||||
// if (colNo) {
|
|
||||||
// // Make sure the location is within the current line.
|
|
||||||
// if (ptr + colNo > sb.buffer->getBufferEnd())
|
|
||||||
// return llvm::SMLoc();
|
|
||||||
|
|
||||||
// // Make sure there is no newline in the way.
|
|
||||||
// if (llvm::StringRef(ptr, colNo).find_first_of("\n\r") !=
|
|
||||||
// llvm::StringRef::npos)
|
|
||||||
// return llvm::SMLoc();
|
|
||||||
|
|
||||||
// ptr += colNo;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return llvm::SMLoc::getFromPointer(ptr);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void SourceMgr::PrintIncludeStack(llvm::SMLoc includeLoc,
|
|
||||||
// llvm::raw_ostream &os) const {
|
|
||||||
// if (includeLoc == llvm::SMLoc())
|
|
||||||
// return; // Top of stack.
|
|
||||||
|
|
||||||
// unsigned curBuf = FindBufferContainingLoc(includeLoc);
|
|
||||||
// assert(curBuf && "Invalid or unspecified location!");
|
|
||||||
|
|
||||||
// PrintIncludeStack(getBufferInfo(curBuf).includeLoc, os);
|
|
||||||
|
|
||||||
// os << "Included from " <<
|
|
||||||
// getBufferInfo(curBuf).buffer->getBufferIdentifier()
|
|
||||||
// << ":" << FindLineNumber(includeLoc, curBuf) << ":\n";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// SMDiagnostic SourceMgr::GetMessage(llvm::SMLoc loc, SourceMgr::DiagKind kind,
|
|
||||||
// const llvm::Twine &msg,
|
|
||||||
// llvm::ArrayRef<llvm::SMRange> ranges,
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts)
|
|
||||||
// const {
|
|
||||||
// // First thing to do: find the current buffer containing the specified
|
|
||||||
// // location to pull out the source line.
|
|
||||||
// llvm::SmallVector<std::pair<unsigned, unsigned>, 4> colRanges;
|
|
||||||
// std::pair<unsigned, unsigned> lineAndCol;
|
|
||||||
// llvm::StringRef bufferID = "<unknown>";
|
|
||||||
// llvm::StringRef lineStr;
|
|
||||||
|
|
||||||
// if (loc.isValid()) {
|
|
||||||
// unsigned curBuf = FindBufferContainingLoc(loc);
|
|
||||||
// assert(curBuf && "Invalid or unspecified location!");
|
|
||||||
|
|
||||||
// const llvm::MemoryBuffer *curMB = getMemoryBuffer(curBuf);
|
|
||||||
// bufferID = curMB->getBufferIdentifier();
|
|
||||||
|
|
||||||
// // Scan backward to find the start of the line.
|
|
||||||
// const char *lineStart = loc.getPointer();
|
|
||||||
// const char *bufStart = curMB->getBufferStart();
|
|
||||||
// while (lineStart != bufStart && lineStart[-1] != '\n' &&
|
|
||||||
// lineStart[-1] != '\r')
|
|
||||||
// --lineStart;
|
|
||||||
|
|
||||||
// // Get the end of the line.
|
|
||||||
// const char *lineEnd = loc.getPointer();
|
|
||||||
// const char *bufEnd = curMB->getBufferEnd();
|
|
||||||
// while (lineEnd != bufEnd && lineEnd[0] != '\n' && lineEnd[0] != '\r')
|
|
||||||
// ++lineEnd;
|
|
||||||
// lineStr = llvm::StringRef(lineStart, lineEnd - lineStart);
|
|
||||||
|
|
||||||
// // Convert any ranges to column ranges that only intersect the line of
|
|
||||||
// the
|
|
||||||
// // location.
|
|
||||||
// for (unsigned i = 0, e = ranges.size(); i != e; ++i) {
|
|
||||||
// llvm::SMRange r = ranges[i];
|
|
||||||
// if (!r.isValid())
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// // If the line doesn't contain any part of the range, then ignore it.
|
|
||||||
// if (r.Start.getPointer() > lineEnd || r.End.getPointer() < lineStart)
|
|
||||||
// continue;
|
|
||||||
|
|
||||||
// // Ignore pieces of the range that go onto other lines.
|
|
||||||
// if (r.Start.getPointer() < lineStart)
|
|
||||||
// r.Start = llvm::SMLoc::getFromPointer(lineStart);
|
|
||||||
// if (r.End.getPointer() > lineEnd)
|
|
||||||
// r.End = llvm::SMLoc::getFromPointer(lineEnd);
|
|
||||||
|
|
||||||
// // Translate from llvm::SMLoc ranges to column ranges.
|
|
||||||
// // FIXME: Handle multibyte characters.
|
|
||||||
// colRanges.push_back(std::make_pair(r.Start.getPointer() - lineStart,
|
|
||||||
// r.End.getPointer() - lineStart));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// lineAndCol = getLineAndColumn(loc, curBuf);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return SMDiagnostic(*this, loc, bufferID, lineAndCol.first,
|
|
||||||
// lineAndCol.second - 1, kind, msg.str(), lineStr,
|
|
||||||
// colRanges, fixIts);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void SourceMgr::PrintMessage(llvm::raw_ostream &os,
|
|
||||||
// const SMDiagnostic &diagnostic,
|
|
||||||
// bool showColors) const {
|
|
||||||
// // Report the message with the diagnostic handler if present.
|
|
||||||
// if (diagHandler) {
|
|
||||||
// diagHandler(diagnostic, diagContext);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (diagnostic.getLoc().isValid()) {
|
|
||||||
// unsigned CurBuf = FindBufferContainingLoc(diagnostic.getLoc());
|
|
||||||
// assert(CurBuf && "Invalid or unspecified location!");
|
|
||||||
// PrintIncludeStack(getBufferInfo(CurBuf).includeLoc, os);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// diagnostic.print(nullptr, os, showColors);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void SourceMgr::PrintMessage(llvm::raw_ostream &os, llvm::SMLoc loc,
|
|
||||||
// SourceMgr::DiagKind kind, const llvm::Twine
|
|
||||||
// &msg, llvm::ArrayRef<llvm::SMRange> ranges,
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts,
|
|
||||||
// bool showColors) const {
|
|
||||||
// PrintMessage(os, GetMessage(loc, kind, msg, ranges, fixIts), showColors);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void SourceMgr::PrintMessage(llvm::SMLoc loc, SourceMgr::DiagKind kind,
|
|
||||||
// const llvm::Twine &msg,
|
|
||||||
// llvm::ArrayRef<llvm::SMRange> ranges,
|
|
||||||
// llvm::ArrayRef<llvm::SMFixIt> fixIts,
|
|
||||||
// bool showColors) const {
|
|
||||||
// PrintMessage(llvm::errs(), loc, kind, msg, ranges, fixIts, showColors);
|
|
||||||
// }
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// SMDiagnostic Implementation
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
SMDiagnostic::SMDiagnostic(const SourceMgr &sm, llvm::SMLoc l,
|
|
||||||
llvm::StringRef fn, int line, int col,
|
|
||||||
SourceMgr::DiagKind kind, llvm::StringRef msg,
|
|
||||||
llvm::StringRef lineStr,
|
|
||||||
llvm::ArrayRef<std::pair<unsigned, unsigned>> ranges,
|
|
||||||
llvm::ArrayRef<llvm::SMFixIt> hints)
|
|
||||||
: sm(&sm), loc(l), filename(std::string(fn)), lineNo(line), columnNo(col),
|
|
||||||
kind(kind), message(msg), lineContents(lineStr), ranges(ranges.vec()),
|
|
||||||
fixIts(hints.begin(), hints.end()) {
|
|
||||||
llvm::sort(fixIts);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void buildFixItLine(std::string &caretLine, std::string &fixItLine,
|
|
||||||
llvm::ArrayRef<llvm::SMFixIt> fixIts,
|
|
||||||
llvm::ArrayRef<char> sourceLine) {
|
|
||||||
if (fixIts.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const char *lineStart = sourceLine.begin();
|
|
||||||
const char *lineEnd = sourceLine.end();
|
|
||||||
|
|
||||||
size_t prevHintEndCol = 0;
|
|
||||||
|
|
||||||
for (const llvm::SMFixIt &fixit : fixIts) {
|
|
||||||
// If the fixit contains a newline or tab, ignore it.
|
|
||||||
if (fixit.getText().find_first_of("\n\r\t") != llvm::StringRef::npos)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
llvm::SMRange r = fixit.getRange();
|
|
||||||
|
|
||||||
// If the line doesn't contain any part of the range, then ignore it.
|
|
||||||
if (r.Start.getPointer() > lineEnd || r.End.getPointer() < lineStart)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Translate from llvm::SMLoc to column.
|
|
||||||
// Ignore pieces of the range that go onto other lines.
|
|
||||||
// FIXME: Handle multibyte characters in the source line.
|
|
||||||
unsigned firstCol;
|
|
||||||
if (r.Start.getPointer() < lineStart)
|
|
||||||
firstCol = 0;
|
|
||||||
else
|
|
||||||
firstCol = r.Start.getPointer() - lineStart;
|
|
||||||
|
|
||||||
// If we inserted a long previous hint, push this one forwards, and add
|
|
||||||
// an extra space to show that this is not part of the previous
|
|
||||||
// completion. This is sort of the best we can do when two hints appear
|
|
||||||
// to overlap.
|
|
||||||
//
|
|
||||||
// Note that if this hint is located immediately after the previous
|
|
||||||
// hint, no space will be added, since the location is more important.
|
|
||||||
unsigned hintCol = firstCol;
|
|
||||||
if (hintCol < prevHintEndCol)
|
|
||||||
hintCol = prevHintEndCol + 1;
|
|
||||||
|
|
||||||
// FIXME: This assertion is intended to catch unintended use of multibyte
|
|
||||||
// characters in fixits. If we decide to do this, we'll have to track
|
|
||||||
// separate byte widths for the source and fixit lines.
|
|
||||||
assert((size_t)llvm::sys::locale::columnWidth(fixit.getText()) ==
|
|
||||||
fixit.getText().size());
|
|
||||||
|
|
||||||
// This relies on one byte per column in our fixit hints.
|
|
||||||
unsigned lastColumnModified = hintCol + fixit.getText().size();
|
|
||||||
if (lastColumnModified > fixItLine.size())
|
|
||||||
fixItLine.resize(lastColumnModified, ' ');
|
|
||||||
|
|
||||||
llvm::copy(fixit.getText(), fixItLine.begin() + hintCol);
|
|
||||||
|
|
||||||
prevHintEndCol = lastColumnModified;
|
|
||||||
|
|
||||||
// For replacements, mark the removal range with '~'.
|
|
||||||
// FIXME: Handle multibyte characters in the source line.
|
|
||||||
unsigned lastCol;
|
|
||||||
if (r.End.getPointer() >= lineEnd)
|
|
||||||
lastCol = lineEnd - lineStart;
|
|
||||||
else
|
|
||||||
lastCol = r.End.getPointer() - lineStart;
|
|
||||||
|
|
||||||
std::fill(&caretLine[firstCol], &caretLine[lastCol], '~');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void printSourceLine(llvm::raw_ostream &s,
|
|
||||||
llvm::StringRef lineContents) {
|
|
||||||
// Print out the source line one character at a time, so we can expand tabs.
|
|
||||||
for (unsigned i = 0, e = lineContents.size(), outCol = 0; i != e; ++i) {
|
|
||||||
size_t nextTab = lineContents.find('\t', i);
|
|
||||||
// If there were no tabs left, print the rest, we are done.
|
|
||||||
if (nextTab == llvm::StringRef::npos) {
|
|
||||||
s << lineContents.drop_front(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, print from i to NextTab.
|
|
||||||
s << lineContents.slice(i, nextTab);
|
|
||||||
outCol += nextTab - i;
|
|
||||||
i = nextTab;
|
|
||||||
|
|
||||||
// If we have a tab, emit at least one space, then round up to 8 columns.
|
|
||||||
do {
|
|
||||||
s << ' ';
|
|
||||||
++outCol;
|
|
||||||
} while ((outCol % tabStop) != 0);
|
|
||||||
}
|
|
||||||
s << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isNonASCII(char c) { return c & 0x80; }
|
|
||||||
|
|
||||||
void SMDiagnostic::print(const char *progName, llvm::raw_ostream &os,
|
|
||||||
bool showColors, bool showKindLabel) const {
|
|
||||||
llvm::ColorMode mode =
|
|
||||||
showColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
|
|
||||||
|
|
||||||
{
|
|
||||||
llvm::WithColor s(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode);
|
|
||||||
|
|
||||||
if (progName && progName[0])
|
|
||||||
s << progName << ": ";
|
|
||||||
|
|
||||||
if (!filename.empty()) {
|
|
||||||
if (filename == "-")
|
|
||||||
s << "<stdin>";
|
|
||||||
else
|
|
||||||
s << filename;
|
|
||||||
|
|
||||||
if (lineNo != -1) {
|
|
||||||
s << ':' << lineNo;
|
|
||||||
if (columnNo != -1)
|
|
||||||
s << ':' << (columnNo + 1);
|
|
||||||
}
|
|
||||||
s << ": ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showKindLabel) {
|
|
||||||
switch (kind) {
|
|
||||||
case SourceMgr::DK_Error:
|
|
||||||
llvm::WithColor::error(os, "", !showColors);
|
|
||||||
break;
|
|
||||||
case SourceMgr::DK_Warning:
|
|
||||||
llvm::WithColor::warning(os, "", !showColors);
|
|
||||||
break;
|
|
||||||
case SourceMgr::DK_Note:
|
|
||||||
llvm::WithColor::note(os, "", !showColors);
|
|
||||||
break;
|
|
||||||
case SourceMgr::DK_Remark:
|
|
||||||
llvm::WithColor::remark(os, "", !showColors);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::WithColor(os, llvm::raw_ostream::SAVEDCOLOR, true, false, mode)
|
|
||||||
<< message << '\n';
|
|
||||||
|
|
||||||
if (lineNo == -1 || columnNo == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// FIXME: If there are multibyte or multi-column characters in the source, all
|
|
||||||
// our ranges will be wrong. To do this properly, we'll need a byte-to-column
|
|
||||||
// map like Clang's TextDiagnostic. For now, we'll just handle tabs by
|
|
||||||
// expanding them later, and bail out rather than show incorrect ranges and
|
|
||||||
// misaligned fixits for any other odd characters.
|
|
||||||
if (llvm::any_of(lineContents, isNonASCII)) {
|
|
||||||
printSourceLine(os, lineContents);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size_t numColumns = lineContents.size();
|
|
||||||
|
|
||||||
// Build the line with the caret and ranges.
|
|
||||||
std::string caretLine(numColumns + 1, ' ');
|
|
||||||
|
|
||||||
// Expand any ranges.
|
|
||||||
for (const std::pair<unsigned, unsigned> &r : ranges)
|
|
||||||
std::fill(&caretLine[r.first],
|
|
||||||
&caretLine[std::min((size_t)r.second, caretLine.size())], '~');
|
|
||||||
|
|
||||||
// Add any fix-its.
|
|
||||||
// FIXME: Find the beginning of the line properly for multibyte characters.
|
|
||||||
std::string fixItInsertionLine;
|
|
||||||
buildFixItLine(
|
|
||||||
caretLine, fixItInsertionLine, fixIts,
|
|
||||||
llvm::makeArrayRef(loc.getPointer() - columnNo, lineContents.size()));
|
|
||||||
|
|
||||||
// Finally, plop on the caret.
|
|
||||||
if (unsigned(columnNo) <= numColumns)
|
|
||||||
caretLine[columnNo] = '^';
|
|
||||||
else
|
|
||||||
caretLine[numColumns] = '^';
|
|
||||||
|
|
||||||
// ... and remove trailing whitespace so the output doesn't wrap for it. We
|
|
||||||
// know that the line isn't completely empty because it has the caret in it at
|
|
||||||
// least.
|
|
||||||
caretLine.erase(caretLine.find_last_not_of(' ') + 1);
|
|
||||||
|
|
||||||
printSourceLine(os, lineContents);
|
|
||||||
|
|
||||||
{
|
|
||||||
llvm::ColorMode mode =
|
|
||||||
showColors ? llvm::ColorMode::Auto : llvm::ColorMode::Disable;
|
|
||||||
llvm::WithColor s(os, llvm::raw_ostream::GREEN, true, false, mode);
|
|
||||||
|
|
||||||
// Print out the caret line, matching tabs in the source line.
|
|
||||||
for (unsigned i = 0, e = caretLine.size(), outCol = 0; i != e; ++i) {
|
|
||||||
if (i >= lineContents.size() || lineContents[i] != '\t') {
|
|
||||||
s << caretLine[i];
|
|
||||||
++outCol;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, we have a tab. Insert the appropriate number of characters.
|
|
||||||
do {
|
|
||||||
s << caretLine[i];
|
|
||||||
++outCol;
|
|
||||||
} while ((outCol % tabStop) != 0);
|
|
||||||
}
|
|
||||||
s << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print out the replacement line, matching tabs in the source line.
|
|
||||||
if (fixItInsertionLine.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (size_t i = 0, e = fixItInsertionLine.size(), outCol = 0; i < e; ++i) {
|
|
||||||
if (i >= lineContents.size() || lineContents[i] != '\t') {
|
|
||||||
os << fixItInsertionLine[i];
|
|
||||||
++outCol;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Okay, we have a tab. Insert the appropriate number of characters.
|
|
||||||
do {
|
|
||||||
os << fixItInsertionLine[i];
|
|
||||||
// FIXME: This is trying not to break up replacements, but then to re-sync
|
|
||||||
// with the tabs between replacements. This will fail, though, if two
|
|
||||||
// fix-it replacements are exactly adjacent, or if a fix-it contains a
|
|
||||||
// space. Really we should be precomputing column widths, which we'll
|
|
||||||
// need anyway for multibyte chars.
|
|
||||||
if (fixItInsertionLine[i] != ' ')
|
|
||||||
++i;
|
|
||||||
++outCol;
|
|
||||||
} while (((outCol % tabStop) != 0) && i != e);
|
|
||||||
}
|
|
||||||
os << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // namespace serene
|
}; // namespace serene
|
||||||
|
|
Loading…
Reference in New Issue