summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-string.cpp12
-rw-r--r--source/core/slang-string.h7
-rw-r--r--source/slang/slang-check-decl.cpp4
-rw-r--r--source/slang/slang-diagnostics.cpp164
-rw-r--r--source/slang/slang-diagnostics.h28
-rw-r--r--source/slang/slang-lexer.cpp36
-rw-r--r--source/slang/slang-lexer.h11
-rw-r--r--source/slang/slang-parser.cpp6
-rw-r--r--source/slang/slang-reflection-api.cpp5
-rw-r--r--source/slang/slang.cpp20
10 files changed, 273 insertions, 20 deletions
diff --git a/source/core/slang-string.cpp b/source/core/slang-string.cpp
index 4b1ec4c84..a7374d8ba 100644
--- a/source/core/slang-string.cpp
+++ b/source/core/slang-string.cpp
@@ -362,6 +362,18 @@ namespace Slang
}
}
+ void String::appendRepeatedChar(char chr, Index count)
+ {
+ SLANG_ASSERT(count >= 0);
+ if (count > 0)
+ {
+ char* chars = prepareForAppend(count);
+ // Set all space to repeated chr.
+ ::memset(chars, chr, sizeof(char) * count);
+ appendInPlace(chars, count);
+ }
+ }
+
void String::appendChar(char c)
{
const auto oldLength = getLength();
diff --git a/source/core/slang-string.h b/source/core/slang-string.h
index e57718d40..3fb184ac7 100644
--- a/source/core/slang-string.h
+++ b/source/core/slang-string.h
@@ -100,6 +100,10 @@ namespace Slang
{
return slice.m_begin >= m_begin && slice.m_end <= m_end;
}
+ bool isMemoryContained(const char* pos) const
+ {
+ return pos >= m_begin && pos <= m_end;
+ }
Index getLength() const
{
@@ -439,6 +443,9 @@ namespace Slang
/// Append a character (to remove ambiguity with other integral types)
void appendChar(char chr);
+ /// Append the specified char count times
+ void appendRepeatedChar(char chr, Index count);
+
String(const char* str)
{
append(str);
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 8c11e538d..a1c8369aa 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -1888,7 +1888,7 @@ namespace Slang
// a temporary one.
//
DiagnosticSink* savedSink = m_shared->m_sink;
- DiagnosticSink tempSink(savedSink->getSourceManager());
+ DiagnosticSink tempSink(savedSink->getSourceManager(), nullptr);
m_shared->m_sink = &tempSink;
// With our temporary diagnostic sink soaking up any messages
@@ -2177,7 +2177,7 @@ namespace Slang
// of diagnostics more easily.
//
DiagnosticSink* savedSink = m_shared->m_sink;
- DiagnosticSink tempSink(savedSink->getSourceManager());
+ DiagnosticSink tempSink(savedSink->getSourceManager(), nullptr);
m_shared->m_sink = &tempSink;
// We start by constructing an expression that represents
diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp
index 04ada6fc4..4b450c9a9 100644
--- a/source/slang/slang-diagnostics.cpp
+++ b/source/slang/slang-diagnostics.cpp
@@ -6,6 +6,7 @@
#include "../core/slang-memory-arena.h"
#include "../core/slang-dictionary.h"
#include "../core/slang-string-util.h"
+#include "../core/slang-char-util.h"
#include "../core/slang-name-convention-util.h"
#include <assert.h>
@@ -160,6 +161,160 @@ static void formatDiagnostic(const HumaneSourceLoc& humaneLoc, Diagnostic const&
outBuilder << "\n";
}
+static void _replaceTabWithSpaces(const UnownedStringSlice& slice, Int tabSize, StringBuilder& out)
+{
+ const char* start = slice.begin();
+ const char*const end = slice.end();
+
+ const Index startLength = out.getLength();
+
+ for (const char* cur = start; cur < end; cur++)
+ {
+ if (*cur == '\t')
+ {
+ if (start < cur)
+ {
+ out.append(start, cur);
+ }
+
+ // The amount of spaces we add depends on the current position.
+ const Index lastPosition = out.getLength() - startLength;
+ Index tabPosition = lastPosition;
+
+ // Strip the tabPosition so it's back to the tab stop
+ // Special case if tabSize is a power of 2
+ if ((tabSize & (tabSize - 1)) == 0)
+ {
+ tabPosition = tabPosition & ~Index(tabSize - 1);
+ }
+ else
+ {
+ tabPosition -= tabPosition % tabSize;
+ }
+
+ // Move to next tab
+ tabPosition += tabSize;
+
+ // The amount of spaces to simulate the tab
+ const Index spacesCount = tabPosition - lastPosition;
+
+ // Add the spaces
+ out.appendRepeatedChar(' ', spacesCount);
+
+ // Set the start at the first character past
+ start = cur + 1;
+ }
+ }
+
+ if (start < end)
+ {
+ out.append(start, end);
+ }
+}
+
+// Given multi-line text, and a position within the text (as a pointer into the memory of text)
+// extract the line that contains pos
+static UnownedStringSlice _extractLineContainingPosition(const UnownedStringSlice& text, const char* pos)
+{
+ SLANG_ASSERT(text.isMemoryContained(pos));
+
+ const char*const contentStart = text.begin();
+ const char*const contentEnd = text.end();
+
+ // We want to determine the start of the line, and the end of the line
+ const char* start = pos;
+ for (; start > contentStart; --start)
+ {
+ const char c = *start;
+ if (c == '\n' || c == '\r')
+ {
+ // We want the character after, but we can only do this if not already at pos
+ start += int(start < pos);
+ break;
+ }
+ }
+ const char* end = pos;
+ for (; end < contentEnd; ++end)
+ {
+ const char c = *end;
+ if (c == '\n' || c == '\r')
+ {
+ break;
+ }
+ }
+
+ return UnownedStringSlice(start, end);
+}
+
+static void _sourceLocationNoteDiagnostic(SourceView* sourceView, SourceLoc sourceLoc, DiagnosticSink::SourceLocationLexer lexer, StringBuilder& sb)
+{
+ SourceFile* sourceFile = sourceView->getSourceFile();
+ if (!sourceFile)
+ {
+ return;
+ }
+
+ UnownedStringSlice content = sourceFile->getContent();
+
+ // Make sure the offset is within content.
+ // This is important because it's possible to have a 'SourceFile' that doesn't contain any content
+ // (for example when reconstructed via serialization with just line offsets, the actual source text 'content' isn't available).
+ const int offset = sourceView->getRange().getOffset(sourceLoc);
+ if (offset < 0 || offset >= content.getLength())
+ {
+ return;
+ }
+
+ // Work out the position of the SourceLoc in the source
+ const char*const pos = content.begin() + offset;
+
+ UnownedStringSlice line = _extractLineContainingPosition(content, pos);
+
+ // Trim any trailing white space
+ line = UnownedStringSlice(line.begin(), line.trim().end());
+
+ // TODO(JS): The tab size should ideally be configurable from command line.
+ // For now just go with 4.
+ const Index tabSize = 4;
+
+ StringBuilder sourceLine;
+ StringBuilder caretLine;
+
+ // First work out the sourceLine
+ _replaceTabWithSpaces(line, tabSize, sourceLine);
+
+ // Now the caretLine which appears underneath the sourceLine
+ {
+ // Produce the text up to the caret position (at pos), taking into account tabs
+ _replaceTabWithSpaces(UnownedStringSlice(line.begin(), pos), tabSize, caretLine);
+
+ // Now make all spaces
+ const Index length = caretLine.getLength();
+ caretLine.Clear();
+ caretLine.appendRepeatedChar(' ', length);
+
+ // Add caret
+ caretLine << "^";
+
+ if (lexer)
+ {
+ UnownedStringSlice token = lexer(UnownedStringSlice(pos, line.end()));
+
+ if (token.getLength() > 1)
+ {
+ caretLine.appendRepeatedChar('~', token.getLength() - 1);
+ }
+ }
+ }
+
+ // We could have handling here for if the line is too long, that we surround the important section
+ // will ellipsis for example.
+ // For now we just output.
+
+ sb << sourceLine << "\n";
+ sb << caretLine << "\n";
+}
+
static void formatDiagnostic(
DiagnosticSink* sink,
Diagnostic const& diagnostic,
@@ -214,7 +369,14 @@ static void formatDiagnostic(
}
}
}
-
+
+ // We don't don't output source line information if this is a 'note' as a note is extra information for one
+ // of the other main severity types, and so the information should already be output on the initial line
+ if (sourceView && sink->isFlagSet(DiagnosticSink::Flag::SourceLocationLine) && diagnostic.severity != Severity::Note)
+ {
+ _sourceLocationNoteDiagnostic(sourceView, sourceLoc, sink->getSourceLocationLexer(), sb);
+ }
+
if (sourceView && sink->isFlagSet(DiagnosticSink::Flag::VerbosePath))
{
auto actualHumaneLoc = sourceView->getHumaneLoc(diagnostic.loc, SourceLocType::Actual);
diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h
index deec57b7c..05080989a 100644
--- a/source/slang/slang-diagnostics.h
+++ b/source/slang/slang-diagnostics.h
@@ -131,6 +131,7 @@ namespace Slang
{}
};
+
class DiagnosticSink
{
public:
@@ -140,10 +141,15 @@ namespace Slang
{
enum Enum: Flags
{
- VerbosePath = 0x1, ///< Will display a more verbose path (if available) - such as a canonical or absolute path
+ VerbosePath = 0x1, ///< Will display a more verbose path (if available) - such as a canonical or absolute path
+ SourceLocationLine = 0x2, ///< If set will display the location line if source is available
};
};
+ /// Used by diagnostic sink to be able to underline tokens. If not defined on the DiagnosticSink,
+ /// will only display a caret at the SourceLoc
+ typedef UnownedStringSlice(*SourceLocationLexer)(const UnownedStringSlice& text);
+
/// Get the total amount of errors that have taken place on this DiagnosticSink
SLANG_FORCE_INLINE int getErrorCount() { return m_errorCount; }
@@ -209,10 +215,22 @@ namespace Slang
/// Test if flag is set
bool isFlagSet(Flag::Enum flag) { return (m_flags & Flags(flag)) != 0; }
+ /// Get the (optional) diagnostic sink lexer. This is used to
+ /// improve quality of highlighting a locations token. If not set, will just have a single
+ /// character caret at location
+ SourceLocationLexer getSourceLocationLexer() const { return m_sourceLocationLexer; }
+
/// Ctor
- DiagnosticSink(SourceManager* sourceManager)
- : m_sourceManager(sourceManager)
- {}
+ DiagnosticSink(SourceManager* sourceManager, SourceLocationLexer sourceLocationLexer)
+ : m_sourceManager(sourceManager),
+ m_sourceLocationLexer(sourceLocationLexer)
+ {
+ // If we have a source location lexer, we'll by default enable source location output
+ if (sourceLocationLexer)
+ {
+ setFlag(Flag::SourceLocationLine);
+ }
+ }
// Public members
@@ -231,6 +249,8 @@ namespace Slang
// The source manager to use when mapping source locations to file+line info
SourceManager* m_sourceManager = nullptr;
+
+ SourceLocationLexer m_sourceLocationLexer;
};
/// An `ISlangWriter` that writes directly to a diagnostic sink.
diff --git a/source/slang/slang-lexer.cpp b/source/slang/slang-lexer.cpp
index fe268223d..b0146c5b0 100644
--- a/source/slang/slang-lexer.cpp
+++ b/source/slang/slang-lexer.cpp
@@ -1339,4 +1339,40 @@ namespace Slang
return tokenList;
}
}
+
+ /* static */UnownedStringSlice Lexer::sourceLocationLexer(const UnownedStringSlice& in)
+ {
+ Lexer lexer;
+
+ SourceManager sourceManager;
+ sourceManager.initialize(nullptr, nullptr);
+
+ auto sourceFile = sourceManager.createSourceFileWithString(PathInfo::makeUnknown(), in);
+ auto sourceView = sourceManager.createSourceView(sourceFile, nullptr, SourceLoc::fromRaw(0));
+
+ DiagnosticSink sink(&sourceManager, nullptr);
+
+ MemoryArena arena;
+
+ RootNamePool rootNamePool;
+ NamePool namePool;
+ namePool.setRootNamePool(&rootNamePool);
+
+ lexer.initialize(sourceView, &sink, &namePool, &arena);
+
+ Token tok = lexer.lexToken();
+
+ if (tok.type == TokenType::Invalid)
+ {
+ return UnownedStringSlice();
+ }
+
+ const int offset = sourceView->getRange().getOffset(tok.loc);
+
+ SLANG_ASSERT(offset >= 0 && offset <= in.getLength());
+ SLANG_ASSERT(Index(offset + tok.charsCount) <= in.getLength());
+
+ return UnownedStringSlice(in.begin() + offset, in.begin() + offset + tok.charsCount);
+ }
+
}
diff --git a/source/slang/slang-lexer.h b/source/slang/slang-lexer.h
index d404296ff..957d05fec 100644
--- a/source/slang/slang-lexer.h
+++ b/source/slang/slang-lexer.h
@@ -106,6 +106,16 @@ namespace Slang
~Lexer();
+ /// Runs the lexer to try and extract a single token, which is returned.
+ /// This can be used by the DiagnosticSink to be able to display more appropriate
+ /// information when displaying a source location - such as underscoring the
+ /// token at that location.
+ ///
+ /// NOTE! This function is relatively slow, and is designed for use around this specific
+ /// purpose. It does not return a token or a token type, because that information is
+ /// not needed by the DiagnosticSink.
+ static UnownedStringSlice sourceLocationLexer(const UnownedStringSlice& in);
+
Token lexToken(LexerFlags extraFlags = 0);
TokenList lexAllTokens();
@@ -128,6 +138,7 @@ namespace Slang
MemoryArena* m_memoryArena;
};
+
// Helper routines for extracting values from tokens
String getStringLiteralTokenValue(Token const& token);
String getFileNameTokenValue(Token const& token);
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index b15d215da..8ea51645a 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -1762,7 +1762,11 @@ namespace Slang
TokenSpan tokenSpan;
tokenSpan.m_begin = parser->tokenReader.m_cursor;
tokenSpan.m_end = parser->tokenReader.m_end;
- DiagnosticSink newSink(parser->sink->getSourceManager());
+
+ // Setup without diagnostic lexer, or SourceLocationLine output
+ // as this sink is just to *try* generic application
+ DiagnosticSink newSink(parser->sink->getSourceManager(), nullptr);
+
Parser newParser(*parser);
newParser.sink = &newSink;
diff --git a/source/slang/slang-reflection-api.cpp b/source/slang/slang-reflection-api.cpp
index 2a3667295..dcfed07b0 100644
--- a/source/slang/slang-reflection-api.cpp
+++ b/source/slang/slang-reflection-api.cpp
@@ -718,7 +718,8 @@ SLANG_API SlangReflectionType * spReflection_FindTypeByName(SlangReflection * re
// when type lookup fails.
//
Slang::DiagnosticSink sink(
- programLayout->getTargetReq()->getLinkage()->getSourceManager());
+ programLayout->getTargetReq()->getLinkage()->getSourceManager(),
+ Lexer::sourceLocationLexer);
try
{
@@ -2560,7 +2561,7 @@ SLANG_API SlangReflectionType* spReflection_specializeType(
auto linkage = programLayout->getProgram()->getLinkage();
- DiagnosticSink sink(linkage->getSourceManager());
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
auto specializedType = linkage->specializeType(unspecializedType, specializationArgCount, (Type* const*) specializationArgs, &sink);
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index b711a5327..14f30c632 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -773,7 +773,7 @@ SLANG_NO_THROW slang::IModule* SLANG_MCALL Linkage::loadModule(
{
auto name = getNamePool()->getName(moduleName);
- DiagnosticSink sink(getSourceManager());
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
auto module = findOrImportModule(name, SourceLoc(), &sink);
sink.getBlobIfNeeded(outDiagnostics);
@@ -797,7 +797,7 @@ SLANG_NO_THROW SlangResult SLANG_MCALL Linkage::createCompositeComponentType(
return SLANG_OK;
}
- DiagnosticSink sink(getSourceManager());
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
List<RefPtr<ComponentType>> childComponents;
for( Int cc = 0; cc < componentTypeCount; ++cc )
@@ -834,7 +834,7 @@ SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL Linkage::specializeType(
typeArgs.add(asInternal(arg.type));
}
- DiagnosticSink sink(getSourceManager());
+ DiagnosticSink sink(getSourceManager(), Lexer::sourceLocationLexer);
auto specializedType = specializeType(unspecializedType, typeArgs.getCount(), typeArgs.getBuffer(), &sink);
sink.getBlobIfNeeded(outDiagnostics);
@@ -1185,7 +1185,7 @@ Expr* Linkage::parseTermString(String typeStr, RefPtr<Scope> scope)
Slang::SourceFile* srcFile = localSourceManager.createSourceFileWithString(PathInfo::makeTypeParse(), typeStr);
// We'll use a temporary diagnostic sink
- DiagnosticSink sink(&localSourceManager);
+ DiagnosticSink sink(&localSourceManager, nullptr);
// RAII type to make make sure current SourceManager is restored after parse.
// Use RAII - to make sure everything is reset even if an exception is thrown.
@@ -1845,7 +1845,7 @@ BackEndCompileRequest::BackEndCompileRequest(
EndToEndCompileRequest::EndToEndCompileRequest(
Session* session)
: m_session(session)
- , m_sink(nullptr)
+ , m_sink(nullptr, Lexer::sourceLocationLexer)
{
RefPtr<ASTBuilder> astBuilder(new ASTBuilder(session->m_sharedASTBuilder, "EndToEnd::Linkage::astBuilder"));
m_linkage = new Linkage(session, astBuilder, session->getBuiltinLinkage());
@@ -1856,7 +1856,7 @@ EndToEndCompileRequest::EndToEndCompileRequest(
Linkage* linkage)
: m_session(linkage->getSessionImpl())
, m_linkage(linkage)
- , m_sink(nullptr)
+ , m_sink(nullptr, Lexer::sourceLocationLexer)
{
init();
}
@@ -2627,7 +2627,7 @@ SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL ComponentType::getLayout(
return nullptr;
auto target = linkage->targets[targetIndex];
- DiagnosticSink sink(linkage->getSourceManager());
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
auto programLayout = getTargetProgram(target)->getOrCreateLayout(&sink);
sink.getBlobIfNeeded(outDiagnostics);
@@ -2647,7 +2647,7 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::getEntryPointCode(
auto targetProgram = getTargetProgram(target);
- DiagnosticSink sink(linkage->getSourceManager());
+ DiagnosticSink sink(linkage->getSourceManager(), Lexer::sourceLocationLexer);
auto& entryPointResult = targetProgram->getOrCreateEntryPointResult(entryPointIndex, &sink);
sink.getBlobIfNeeded(outDiagnostics);
@@ -2698,7 +2698,7 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize(
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics)
{
- DiagnosticSink sink(getLinkage()->getSourceManager());
+ DiagnosticSink sink(getLinkage()->getSourceManager(), Lexer::sourceLocationLexer);
// First let's check if the number of arguments given matches
// the number of parameters that are present on this component type.
@@ -3575,7 +3575,7 @@ void Session::addBuiltinSource(
{
SourceManager* sourceManager = getBuiltinSourceManager();
- DiagnosticSink sink(sourceManager);
+ DiagnosticSink sink(sourceManager, Lexer::sourceLocationLexer);
RefPtr<FrontEndCompileRequest> compileRequest = new FrontEndCompileRequest(
m_builtinLinkage,
&sink);