summaryrefslogtreecommitdiffstats
path: root/tools/slang-test/parse-diagnostic-util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/slang-test/parse-diagnostic-util.cpp')
-rw-r--r--tools/slang-test/parse-diagnostic-util.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/tools/slang-test/parse-diagnostic-util.cpp b/tools/slang-test/parse-diagnostic-util.cpp
new file mode 100644
index 000000000..af2ef2a05
--- /dev/null
+++ b/tools/slang-test/parse-diagnostic-util.cpp
@@ -0,0 +1,324 @@
+// parse-diagnostic-util.cpp
+
+#include "parse-diagnostic-util.h"
+
+#include "../../source/core/slang-hex-dump-util.h"
+#include "../../source/core/slang-type-text-util.h"
+
+#include "../../slang-com-helper.h"
+
+#include "../../source/core/slang-string-util.h"
+#include "../../source/core/slang-byte-encode-util.h"
+#include "../../source/core/slang-char-util.h"
+
+#include "../../source/core/slang-downstream-compiler.h"
+
+using namespace Slang;
+
+/* static */SlangResult ParseDiagnosticUtil::splitPathLocation(const UnownedStringSlice& pathLocation, DownstreamDiagnostic& outDiagnostic)
+{
+ const Index lineStartIndex = pathLocation.lastIndexOf('(');
+ if (lineStartIndex >= 0)
+ {
+ outDiagnostic.filePath = UnownedStringSlice(pathLocation.head(lineStartIndex).trim());
+
+ const UnownedStringSlice tail = pathLocation.tail(lineStartIndex + 1);
+ const Index lineEndIndex = tail.indexOf(')');
+
+ if (lineEndIndex >= 0)
+ {
+ // Extract the location info
+ UnownedStringSlice locationSlice(tail.begin(), tail.begin() + lineEndIndex);
+ List<UnownedStringSlice> locationSlices;
+ StringUtil::split(locationSlice, ',', locationSlices);
+
+ Int locationLine = 0;
+ Int locationCol = 0;
+
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(locationSlices[0], locationLine));
+ if (locationSlices.getCount() > 1)
+ {
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(locationSlices[1], locationCol));
+ }
+
+ outDiagnostic.fileLine = locationLine;
+ }
+ }
+ else
+ {
+ outDiagnostic.filePath = pathLocation;
+ }
+ return SLANG_OK;
+}
+
+/* static */SlangResult ParseDiagnosticUtil::parseFXCLine(const UnownedStringSlice& line, List<UnownedStringSlice>& lineSlices, DownstreamDiagnostic& outDiagnostic)
+{
+ SLANG_RETURN_ON_FAIL(splitPathLocation(lineSlices[1], outDiagnostic));
+
+ if (lineSlices.getCount() > 2)
+ {
+ UnownedStringSlice errorSlice = lineSlices[2].trim();
+ Index spaceIndex = errorSlice.indexOf(' ');
+ if (spaceIndex >= 0)
+ {
+ UnownedStringSlice diagnosticType = errorSlice.head(spaceIndex);
+ UnownedStringSlice code = errorSlice.tail(spaceIndex + 1).trim();
+
+ if (diagnosticType == "warning")
+ {
+ outDiagnostic.type = DownstreamDiagnostic::Type::Warning;
+ }
+
+ outDiagnostic.code = code;
+ }
+ else
+ {
+ outDiagnostic.code = errorSlice;
+ }
+
+ if (lineSlices.getCount() > 3)
+ {
+ outDiagnostic.text = UnownedStringSlice(lineSlices[3].begin(), line.end());
+ }
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult ParseDiagnosticUtil::parseDXCLine(const UnownedStringSlice& line, List<UnownedStringSlice>& lineSlices, DownstreamDiagnostic& outDiagnostic)
+{
+ /* dxc: tests/diagnostics/syntax-error-intrinsic.slang:14:2: error: expected expression */
+
+ outDiagnostic.filePath = lineSlices[1];
+
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[2], outDiagnostic.fileLine));
+
+ Int lineCol;
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[3], lineCol));
+
+ UnownedStringSlice typeSlice = lineSlices[4].trim();
+
+ if (typeSlice == UnownedStringSlice::fromLiteral("warning"))
+ {
+ outDiagnostic.type = DownstreamDiagnostic::Type::Warning;
+ }
+
+ // The rest of the line
+ outDiagnostic.text = UnownedStringSlice(lineSlices[5].begin(), line.end());
+ return SLANG_OK;
+}
+
+/* static */ SlangResult ParseDiagnosticUtil::parseGlslangLine(const UnownedStringSlice& line, List<UnownedStringSlice>& lineSlices, DownstreamDiagnostic& outDiagnostic)
+{
+ if (lineSlices.getCount() < 5)
+ {
+ return SLANG_FAIL;
+ }
+
+ /* glslang: ERROR: tests/diagnostics/syntax-error-intrinsic.slang:13: '@' : unexpected token
+ */
+
+ UnownedStringSlice typeSlice = lineSlices[1].trim();
+ if (typeSlice.caseInsensitiveEquals(UnownedStringSlice::fromLiteral("warning")))
+ {
+ outDiagnostic.type = DownstreamDiagnostic::Type::Warning;
+ }
+
+ outDiagnostic.filePath = lineSlices[2];
+
+ SLANG_RETURN_ON_FAIL(StringUtil::parseInt(lineSlices[3], outDiagnostic.fileLine));
+ outDiagnostic.text = UnownedStringSlice(lineSlices[4].begin(), line.end());
+ return SLANG_OK;
+}
+
+/* static */SlangResult ParseDiagnosticUtil::parseGenericLine(const UnownedStringSlice& line, List<UnownedStringSlice>& lineSlices, DownstreamDiagnostic& outDiagnostic)
+{
+ /* Visual Studio 14.0: e:\git\somewhere\tests\diagnostics\syntax-error-intrinsic.slang(13): error C2018: unknown character '0x40' */
+
+ UnownedStringSlice errorSlice = lineSlices[2].trim();
+ UnownedStringSlice typeSlice = StringUtil::getAtInSplit(errorSlice, ' ', 0);
+ if (typeSlice == UnownedStringSlice::fromLiteral("warning"))
+ {
+ outDiagnostic.type = DownstreamDiagnostic::Type::Warning;
+ }
+ else if (typeSlice == UnownedStringSlice::fromLiteral("info"))
+ {
+ outDiagnostic.type = DownstreamDiagnostic::Type::Info;
+ }
+
+ // Get the code
+ outDiagnostic.code = StringUtil::getAtInSplit(errorSlice, ' ', 1);
+
+ // Get the location info
+ SLANG_RETURN_ON_FAIL(splitPathLocation(lineSlices[1], outDiagnostic));
+
+ outDiagnostic.text = UnownedStringSlice(lineSlices[3].begin(), line.end());
+
+ return SLANG_OK;
+}
+
+/* static */ SlangResult ParseDiagnosticUtil::splitCompilerDiagnosticLine(SlangPassThrough downstreamCompiler, const UnownedStringSlice& line, UnownedStringSlice& linePrefix, List<UnownedStringSlice>& outSlices)
+{
+ /*
+ glslang: ERROR: tests/diagnostics/syntax-error-intrinsic.slang:13: '@' : unexpected token
+ dxc: tests/diagnostics/syntax-error-intrinsic.slang:14:2: error: expected expression
+ fxc: tests/diagnostics/syntax-error-intrinsic.slang(14,2): error X3000: syntax error: unexpected token '@'
+ Visual Studio 14.0: e:\git\somewhere\tests\diagnostics\syntax-error-intrinsic.slang(13): error C2018: unknown character '0x40'
+ NVRTC 11.0: tests/diagnostics/syntax-error-intrinsic.slang(13): error : unrecognized token
+ */
+
+ Int pathIndex = 1;
+
+ StringUtil::split(line, ':', outSlices);
+
+ if (downstreamCompiler == SLANG_PASS_THROUGH_GLSLANG)
+ {
+ // If we don't have the prefix then add it
+ if (!outSlices[0].trim().startsWith(linePrefix))
+ {
+ outSlices.insert(0, linePrefix);
+ }
+ pathIndex = 2;
+ }
+
+ // Make sure this seems plausible
+ if (outSlices.getCount() > pathIndex + 1)
+ {
+ UnownedStringSlice pathStart = outSlices[pathIndex].trim();
+
+ if (pathStart.getLength() == 1 && CharUtil::isAlpha(pathStart[0]))
+ {
+ // Splice back together
+ outSlices[pathIndex] = UnownedStringSlice(outSlices[pathIndex].begin(), outSlices[pathIndex + 1].end());
+ outSlices.removeAt(pathIndex + 1);
+ }
+ }
+
+ return SLANG_OK;
+}
+
+static void _addDiagnosticNote(const UnownedStringSlice& in, List<DownstreamDiagnostic>& outDiagnostics)
+{
+ // Don't bother adding an empty line
+ if (in.trim().getLength() == 0)
+ {
+ return;
+ }
+
+ // Make it a note on the output
+ DownstreamDiagnostic diagnostic;
+ diagnostic.reset();
+ diagnostic.type = DownstreamDiagnostic::Type::Info;
+ diagnostic.text = in;
+ outDiagnostics.add(diagnostic);
+}
+
+static SlangResult _findDownstreamCompiler(const UnownedStringSlice& slice, SlangPassThrough& outDownstreamCompiler)
+{
+ for (Index i = SLANG_PASS_THROUGH_NONE + 1; i < SLANG_PASS_THROUGH_COUNT_OF; ++i)
+ {
+ const SlangPassThrough downstreamCompiler = SlangPassThrough(i);
+ UnownedStringSlice name = TypeTextUtil::getPassThroughAsHumanText(downstreamCompiler);
+
+ if (slice.startsWith(name))
+ {
+ outDownstreamCompiler = downstreamCompiler;
+ return SLANG_OK;
+ }
+ }
+ return SLANG_FAIL;
+}
+
+/* static */SlangResult ParseDiagnosticUtil::parseDiagnostics(const UnownedStringSlice& inText, List<DownstreamDiagnostic>& outDiagnostics)
+{
+ // TODO(JS):
+ // As it stands output of downstream compilers isn't standardized. This should be improved upon, and perhaps
+ // we should have a function that will parse the standardized output
+ // Currently dxc/fxc/glslang, use a different downstream path
+
+ // We look for the first l
+
+ SlangPassThrough downstreamCompiler = SLANG_PASS_THROUGH_NONE;
+ UnownedStringSlice linePrefix;
+
+ List<UnownedStringSlice> splitLine;
+
+ UnownedStringSlice text(inText), line;
+ while (StringUtil::extractLine(text, line))
+ {
+ UnownedStringSlice initial = StringUtil::getAtInSplit(line, ':', 0);
+
+ if (downstreamCompiler == SLANG_PASS_THROUGH_NONE)
+ {
+ // First entry that begins with a numeral indicates the version number
+ if (SLANG_FAILED(_findDownstreamCompiler(initial, downstreamCompiler)))
+ {
+ continue;
+ }
+
+ linePrefix = TypeTextUtil::getPassThroughAsHumanText(downstreamCompiler);
+ }
+
+ if (line.indexOf(':') < 0 )
+ {
+ _addDiagnosticNote(line, outDiagnostics);
+ continue;
+ }
+
+ if (SLANG_FAILED(splitCompilerDiagnosticLine(downstreamCompiler, line, linePrefix, splitLine)))
+ {
+ _addDiagnosticNote(line, outDiagnostics);
+ continue;
+ }
+
+ // If doesn't have prefix, just add as note
+ if (!splitLine[0].trim().startsWith(linePrefix))
+ {
+ _addDiagnosticNote(line, outDiagnostics);
+ continue;
+ }
+
+ DownstreamDiagnostic diagnostic;
+ diagnostic.type = DownstreamDiagnostic::Type::Error;
+ diagnostic.stage = DownstreamDiagnostic::Stage::Compile;
+ diagnostic.fileLine = 0;
+
+ SlangResult parseRes;
+
+ switch (downstreamCompiler)
+ {
+ case SLANG_PASS_THROUGH_FXC:
+ {
+ parseRes = parseFXCLine(line, splitLine, diagnostic);
+ break;
+ }
+ case SLANG_PASS_THROUGH_DXC:
+ {
+ parseRes = parseDXCLine(line, splitLine, diagnostic);
+ break;
+ }
+ case SLANG_PASS_THROUGH_GLSLANG:
+ {
+ parseRes = parseGlslangLine(line, splitLine, diagnostic);
+ break;
+ }
+ default:
+ {
+ parseRes = parseGenericLine(line, splitLine, diagnostic);
+ break;
+ }
+ }
+
+ // If couldn't parse, just add as a note
+ if (SLANG_FAILED(parseRes))
+ {
+ _addDiagnosticNote(line, outDiagnostics);
+ }
+ else
+ {
+ outDiagnostics.add(diagnostic);
+ }
+ }
+
+ return SLANG_OK;
+}