summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-diagnostics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-diagnostics.cpp')
-rw-r--r--source/slang/slang-diagnostics.cpp350
1 files changed, 350 insertions, 0 deletions
diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp
new file mode 100644
index 000000000..4aabd3ab9
--- /dev/null
+++ b/source/slang/slang-diagnostics.cpp
@@ -0,0 +1,350 @@
+// slang-diagnostics.cpp
+#include "slang-diagnostics.h"
+
+#include "slang-compiler.h"
+#include "slang-name.h"
+#include "slang-syntax.h"
+
+#include <assert.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+#include <d3dcompiler.h>
+#endif
+
+namespace Slang {
+
+void printDiagnosticArg(StringBuilder& sb, char const* str)
+{
+ sb << str;
+}
+
+void printDiagnosticArg(StringBuilder& sb, int32_t val)
+{
+ sb << val;
+}
+
+void printDiagnosticArg(StringBuilder& sb, uint32_t val)
+{
+ sb << val;
+}
+
+void printDiagnosticArg(StringBuilder& sb, int64_t val)
+{
+ sb << val;
+}
+
+void printDiagnosticArg(StringBuilder& sb, uint64_t val)
+{
+ sb << val;
+}
+
+void printDiagnosticArg(StringBuilder& sb, Slang::String const& str)
+{
+ sb << str;
+}
+
+void printDiagnosticArg(StringBuilder& sb, Slang::UnownedStringSlice const& str)
+{
+ sb.append(str);
+}
+
+
+void printDiagnosticArg(StringBuilder& sb, Name* name)
+{
+ sb << getText(name);
+}
+
+
+void printDiagnosticArg(StringBuilder& sb, Decl* decl)
+{
+ sb << getText(decl->getName());
+}
+
+void printDiagnosticArg(StringBuilder& sb, Type* type)
+{
+ sb << type->ToString();
+}
+
+void printDiagnosticArg(StringBuilder& sb, Val* val)
+{
+ sb << val->ToString();
+}
+
+void printDiagnosticArg(StringBuilder& sb, TypeExp const& type)
+{
+ sb << type.type->ToString();
+}
+
+void printDiagnosticArg(StringBuilder& sb, QualType const& type)
+{
+ if (type.type)
+ sb << type.type->ToString();
+ else
+ sb << "<null>";
+}
+
+void printDiagnosticArg(StringBuilder& sb, TokenType tokenType)
+{
+ sb << TokenTypeToString(tokenType);
+}
+
+void printDiagnosticArg(StringBuilder& sb, Token const& token)
+{
+ sb << token.Content;
+}
+
+void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val)
+{
+ switch( val )
+ {
+ default:
+ sb << "<unknown>";
+ break;
+
+#define CASE(TAG, STR) case CodeGenTarget::TAG: sb << STR; break
+ CASE(GLSL, "glsl");
+ CASE(HLSL, "hlsl");
+ CASE(SPIRV, "spirv");
+ CASE(SPIRVAssembly, "spriv-assembly");
+ CASE(DXBytecode, "dxbc");
+ CASE(DXBytecodeAssembly, "dxbc-assembly");
+ CASE(DXIL, "dxil");
+ CASE(DXILAssembly, "dxil-assembly");
+#undef CASE
+ }
+}
+
+void printDiagnosticArg(StringBuilder& sb, Stage val)
+{
+ sb << getStageName(val);
+}
+
+void printDiagnosticArg(StringBuilder& sb, ProfileVersion val)
+{
+ sb << Profile(val).getName();
+}
+
+
+SourceLoc const& getDiagnosticPos(SyntaxNode const* syntax)
+{
+ return syntax->loc;
+}
+
+SourceLoc const& getDiagnosticPos(Token const& token)
+{
+ return token.loc;
+}
+
+SourceLoc const& getDiagnosticPos(TypeExp const& typeExp)
+{
+ return typeExp.exp->loc;
+}
+
+SourceLoc const& getDiagnosticPos(IRInst* inst)
+{
+ return inst->sourceLoc;
+}
+
+
+// Take the format string for a diagnostic message, along with its arguments, and turn it into a
+static void formatDiagnosticMessage(StringBuilder& sb, char const* format, int argCount, DiagnosticArg const* const* args)
+{
+ char const* spanBegin = format;
+ for(;;)
+ {
+ char const* spanEnd = spanBegin;
+ while (int c = *spanEnd)
+ {
+ if (c == '$')
+ break;
+ spanEnd++;
+ }
+
+ sb.Append(spanBegin, int(spanEnd - spanBegin));
+ if (!*spanEnd)
+ return;
+
+ SLANG_ASSERT(*spanEnd == '$');
+ spanEnd++;
+ int d = *spanEnd++;
+ switch (d)
+ {
+ // A double dollar sign `$$` is used to emit a single `$`
+ case '$':
+ sb.Append('$');
+ break;
+
+ // A single digit means to emit the corresponding argument.
+ // TODO: support more than 10 arguments, and add options
+ // to control formatting, etc.
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int index = d - '0';
+ if (index >= argCount)
+ {
+ // TODO(tfoley): figure out what a good policy will be for "panic" situations like this
+ throw InvalidOperationException("too few arguments for diagnostic message");
+ }
+ else
+ {
+ DiagnosticArg const* arg = args[index];
+ arg->printFunc(sb, arg->data);
+ }
+ }
+ break;
+
+ default:
+ throw InvalidOperationException("invalid diagnostic message format");
+ break;
+ }
+
+ spanBegin = spanEnd;
+ }
+}
+
+static void formatDiagnostic(const HumaneSourceLoc& humaneLoc, Diagnostic const& diagnostic, StringBuilder& outBuilder)
+{
+ outBuilder << humaneLoc.pathInfo.foundPath;
+ outBuilder << "(";
+ outBuilder << Int32(humaneLoc.line);
+ outBuilder << "): ";
+
+ outBuilder << getSeverityName(diagnostic.severity);
+
+ if (diagnostic.ErrorID >= 0)
+ {
+ outBuilder << " ";
+ outBuilder << diagnostic.ErrorID;
+ }
+
+ outBuilder << ": ";
+ outBuilder << diagnostic.Message;
+ outBuilder << "\n";
+}
+
+static void formatDiagnostic(
+ DiagnosticSink* sink,
+ Diagnostic const& diagnostic,
+ StringBuilder& sb)
+{
+ auto sourceManager = sink->sourceManager;
+
+ SourceView* sourceView = nullptr;
+ HumaneSourceLoc humaneLoc;
+ const auto sourceLoc = diagnostic.loc;
+ {
+ sourceView = sourceManager->findSourceViewRecursively(sourceLoc);
+ if (sourceView)
+ {
+ humaneLoc = sourceView->getHumaneLoc(sourceLoc);
+ }
+ formatDiagnostic(humaneLoc, diagnostic, sb);
+ }
+
+ if (sourceView && (sink->flags & DiagnosticSink::Flag::VerbosePath))
+ {
+ auto actualHumaneLoc = sourceView->getHumaneLoc(diagnostic.loc, SourceLocType::Actual);
+
+ // Look up the path verbosely (will get the canonical path if necessary)
+ actualHumaneLoc.pathInfo.foundPath = sourceView->getSourceFile()->calcVerbosePath();
+
+ // Only output if it's actually different
+ if (actualHumaneLoc.pathInfo.foundPath != humaneLoc.pathInfo.foundPath ||
+ actualHumaneLoc.line != humaneLoc.line ||
+ actualHumaneLoc.column != humaneLoc.column)
+ {
+ formatDiagnostic(actualHumaneLoc, diagnostic, sb);
+ }
+ }
+}
+
+void DiagnosticSink::diagnoseImpl(SourceLoc const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args)
+{
+ StringBuilder sb;
+ formatDiagnosticMessage(sb, info.messageFormat, argCount, args);
+
+ Diagnostic diagnostic;
+ diagnostic.ErrorID = info.id;
+ diagnostic.Message = sb.ProduceString();
+ diagnostic.loc = pos;
+ diagnostic.severity = info.severity;
+
+ if (diagnostic.severity >= Severity::Error)
+ {
+ errorCount++;
+ }
+
+ // Did the client supply a callback for us to use?
+ if( writer )
+ {
+ // If so, pass the error string along to them
+ StringBuilder messageBuilder;
+ formatDiagnostic(this, diagnostic, messageBuilder);
+
+ writer->write(messageBuilder.getBuffer(), messageBuilder.getLength());
+ }
+ else
+ {
+ // If the user doesn't have a callback, then just
+ // collect our diagnostic messages into a buffer
+ formatDiagnostic(this, diagnostic, outputBuffer);
+ }
+
+ if (diagnostic.severity >= Severity::Fatal)
+ {
+ // TODO: figure out a better policy for aborting compilation
+ throw AbortCompilationException();
+ }
+}
+
+void DiagnosticSink::diagnoseRaw(
+ Severity severity,
+ char const* message)
+{
+ return diagnoseRaw(severity, UnownedStringSlice(message));
+}
+
+void DiagnosticSink::diagnoseRaw(
+ Severity severity,
+ const UnownedStringSlice& message)
+{
+ if (severity >= Severity::Error)
+ {
+ errorCount++;
+ }
+
+ // Did the client supply a callback for us to use?
+ if(writer)
+ {
+ // If so, pass the error string along to them
+ writer->write(message.begin(), message.size());
+ }
+ else
+ {
+ // If the user doesn't have a callback, then just
+ // collect our diagnostic messages into a buffer
+ outputBuffer.append(message);
+ }
+
+ if (severity >= Severity::Fatal)
+ {
+ // TODO: figure out a better policy for aborting compilation
+ throw InvalidOperationException();
+ }
+}
+
+
+namespace Diagnostics
+{
+#define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat };
+#include "slang-diagnostic-defs.h"
+}
+
+
+} // namespace Slang