diff options
| -rw-r--r-- | build/visual-studio/slang-test/slang-test.vcxproj | 2 | ||||
| -rw-r--r-- | build/visual-studio/slang-test/slang-test.vcxproj.filters | 6 | ||||
| -rw-r--r-- | source/core/slang-string-util.cpp | 6 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.cpp | 30 | ||||
| -rw-r--r-- | source/core/slang-type-text-util.h | 2 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 11 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang | 20 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang.1.expected | 1 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang.2.expected | 1 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang.3.expected | 1 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang.4.expected | 1 | ||||
| -rw-r--r-- | tests/diagnostics/syntax-error-intrinsic.slang.expected | 1 | ||||
| -rw-r--r-- | tools/slang-test/parse-diagnostic-util.cpp | 324 | ||||
| -rw-r--r-- | tools/slang-test/parse-diagnostic-util.h | 30 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 83 |
15 files changed, 505 insertions, 14 deletions
diff --git a/build/visual-studio/slang-test/slang-test.vcxproj b/build/visual-studio/slang-test/slang-test.vcxproj index 1626b6cca..27c64c7cb 100644 --- a/build/visual-studio/slang-test/slang-test.vcxproj +++ b/build/visual-studio/slang-test/slang-test.vcxproj @@ -164,6 +164,7 @@ <ItemGroup> <ClInclude Include="..\..\..\tools\slang-test\directory-util.h" /> <ClInclude Include="..\..\..\tools\slang-test\options.h" /> + <ClInclude Include="..\..\..\tools\slang-test\parse-diagnostic-util.h" /> <ClInclude Include="..\..\..\tools\slang-test\slangc-tool.h" /> <ClInclude Include="..\..\..\tools\slang-test\test-context.h" /> <ClInclude Include="..\..\..\tools\slang-test\test-reporter.h" /> @@ -171,6 +172,7 @@ <ItemGroup> <ClCompile Include="..\..\..\tools\slang-test\directory-util.cpp" /> <ClCompile Include="..\..\..\tools\slang-test\options.cpp" /> + <ClCompile Include="..\..\..\tools\slang-test\parse-diagnostic-util.cpp" /> <ClCompile Include="..\..\..\tools\slang-test\slang-test-main.cpp" /> <ClCompile Include="..\..\..\tools\slang-test\slangc-tool.cpp" /> <ClCompile Include="..\..\..\tools\slang-test\test-context.cpp" /> diff --git a/build/visual-studio/slang-test/slang-test.vcxproj.filters b/build/visual-studio/slang-test/slang-test.vcxproj.filters index 8718a4d86..41a6237ba 100644 --- a/build/visual-studio/slang-test/slang-test.vcxproj.filters +++ b/build/visual-studio/slang-test/slang-test.vcxproj.filters @@ -15,6 +15,9 @@ <ClInclude Include="..\..\..\tools\slang-test\options.h"> <Filter>Header Files</Filter> </ClInclude> + <ClInclude Include="..\..\..\tools\slang-test\parse-diagnostic-util.h"> + <Filter>Header Files</Filter> + </ClInclude> <ClInclude Include="..\..\..\tools\slang-test\slangc-tool.h"> <Filter>Header Files</Filter> </ClInclude> @@ -32,6 +35,9 @@ <ClCompile Include="..\..\..\tools\slang-test\options.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\..\..\tools\slang-test\parse-diagnostic-util.cpp"> + <Filter>Source Files</Filter> + </ClCompile> <ClCompile Include="..\..\..\tools\slang-test\slang-test-main.cpp"> <Filter>Source Files</Filter> </ClCompile> diff --git a/source/core/slang-string-util.cpp b/source/core/slang-string-util.cpp index 326bc3191..a859c6945 100644 --- a/source/core/slang-string-util.cpp +++ b/source/core/slang-string-util.cpp @@ -32,9 +32,9 @@ namespace Slang { return areAllEqual(slicesA, slicesB, equalFn); } -/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& slicesOut) +/* static */void StringUtil::split(const UnownedStringSlice& in, char splitChar, List<UnownedStringSlice>& outSlices) { - slicesOut.clear(); + outSlices.clear(); const char* start = in.begin(); const char* end = in.end(); @@ -49,7 +49,7 @@ namespace Slang { } // Add to output - slicesOut.add(UnownedStringSlice(start, cur)); + outSlices.add(UnownedStringSlice(start, cur)); // Skip the split character, if at end we are okay anyway start = cur + 1; diff --git a/source/core/slang-type-text-util.cpp b/source/core/slang-type-text-util.cpp index 80ef0027f..89cd4504b 100644 --- a/source/core/slang-type-text-util.cpp +++ b/source/core/slang-type-text-util.cpp @@ -128,22 +128,34 @@ static const ArchiveTypeInfo s_archiveTypeInfos[] = return slang::TypeReflection::ScalarType::None; } +#define SLANG_PASS_THROUGH_HUMAN_TEXT(x) \ + x(NONE, "Unknown") \ + x(VISUAL_STUDIO, "Visual Studio") \ + x(GCC, "GCC") \ + x(CLANG, "Clang") \ + x(NVRTC, "NVRTC") \ + x(FXC, "fxc") \ + x(DXC, "dxc") \ + x(GLSLANG, "glslang") + /* static */UnownedStringSlice TypeTextUtil::getPassThroughAsHumanText(SlangPassThrough type) { +#define SLANG_PASS_THROUGH_HUMAN_CASE(value, text) case SLANG_PASS_THROUGH_##value: return UnownedStringSlice::fromLiteral(text); + switch (type) { - default: - case SLANG_PASS_THROUGH_NONE: return UnownedStringSlice::fromLiteral("Unknown"); - case SLANG_PASS_THROUGH_VISUAL_STUDIO: return UnownedStringSlice::fromLiteral("Visual Studio"); - case SLANG_PASS_THROUGH_GCC: return UnownedStringSlice::fromLiteral("GCC"); - case SLANG_PASS_THROUGH_CLANG: return UnownedStringSlice::fromLiteral("Clang"); - case SLANG_PASS_THROUGH_NVRTC: return UnownedStringSlice::fromLiteral("NVRTC"); - case SLANG_PASS_THROUGH_FXC: return UnownedStringSlice::fromLiteral("fxc"); - case SLANG_PASS_THROUGH_DXC: return UnownedStringSlice::fromLiteral("dxc"); - case SLANG_PASS_THROUGH_GLSLANG: return UnownedStringSlice::fromLiteral("glslang"); + default: /* fall-through to none */ + SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_CASE) } } +/* static */SlangResult TypeTextUtil::findPassThroughFromHumanText(const UnownedStringSlice& inText, SlangPassThrough& outPassThrough) +{ + #define SLANG_PASS_THROUGH_HUMAN_IF(value, text) if (inText == UnownedStringSlice::fromLiteral(text)) { outPassThrough = SLANG_PASS_THROUGH_##value; return SLANG_OK; } else + SLANG_PASS_THROUGH_HUMAN_TEXT(SLANG_PASS_THROUGH_HUMAN_IF) + return SLANG_FAIL; +} + /* static */SlangSourceLanguage TypeTextUtil::findSourceLanguage(const UnownedStringSlice& text) { if (text == "c" || text == "C") diff --git a/source/core/slang-type-text-util.h b/source/core/slang-type-text-util.h index 07426246e..f5534ec72 100644 --- a/source/core/slang-type-text-util.h +++ b/source/core/slang-type-text-util.h @@ -20,6 +20,8 @@ struct TypeTextUtil /// As human readable text static UnownedStringSlice getPassThroughAsHumanText(SlangPassThrough type); + /// Gets pass through from human text (as from getPassThroughAsHumanText) + static SlangResult findPassThroughFromHumanText(const UnownedStringSlice& text, SlangPassThrough& outPassThrough); /// Given a source language name returns a source language. Name here is distinct from extension static SlangSourceLanguage findSourceLanguage(const UnownedStringSlice& text); diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 9fecc7661..a60da422c 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -1914,12 +1914,19 @@ ${{{{ }}}} - // Specialized function __intrinsic_op int getStringHash(String string); +// Use will produce a syntax error in downstream compiler +// Useful for testing diagnostics around compilation errors of downstream compiler +__target_intrinsic(hlsl, " @ ") +__target_intrinsic(glsl, " @ ") +__target_intrinsic(cuda, " @ ") +__target_intrinsic(cpp, " @ ") +void __SyntaxError(); + // Operators to apply to `enum` types __generic<E : __EnumType> @@ -2084,4 +2091,4 @@ __attributeTarget(DeclBase) attribute_syntax [__requiresNVAPI] : RequiresNVAPIAttribute; __attributeTarget(FunctionDeclBase) -attribute_syntax [noinline] : NoInlineAttribute;
\ No newline at end of file +attribute_syntax [noinline] : NoInlineAttribute; diff --git a/tests/diagnostics/syntax-error-intrinsic.slang b/tests/diagnostics/syntax-error-intrinsic.slang new file mode 100644 index 000000000..dde54b2ad --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang @@ -0,0 +1,20 @@ +// syntax-error-intrinsic.slang + +// NOTE! That although this is a 'diagnostic' like test, it tests using downstream compiler +// the downstream compiler being present is a requirement, so we mark as a 'TEST' so that +// those tests are made. + +//TEST:SIMPLE_LINE:-entry computeMain -target spirv +//TEST:SIMPLE_LINE:-entry computeMain -target dxil -profile cs_6_0 +//TEST:SIMPLE_LINE:-entry computeMain -target dxbc +//TEST:SIMPLE_LINE:-entry computeMain -target dll +//TEST:SIMPLE_LINE:-entry computeMain -target ptx + +[shader("compute")] +[numthreads(4, 1, 1)] +void computeMain() +{ + // Will output what downstream compilers will output as a syntax + // error. + __SyntaxError(); +}
\ No newline at end of file diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.1.expected b/tests/diagnostics/syntax-error-intrinsic.slang.1.expected new file mode 100644 index 000000000..3c032078a --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang.1.expected @@ -0,0 +1 @@ +18 diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.2.expected b/tests/diagnostics/syntax-error-intrinsic.slang.2.expected new file mode 100644 index 000000000..3c032078a --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang.2.expected @@ -0,0 +1 @@ +18 diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.3.expected b/tests/diagnostics/syntax-error-intrinsic.slang.3.expected new file mode 100644 index 000000000..98d9bcb75 --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang.3.expected @@ -0,0 +1 @@ +17 diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.4.expected b/tests/diagnostics/syntax-error-intrinsic.slang.4.expected new file mode 100644 index 000000000..98d9bcb75 --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang.4.expected @@ -0,0 +1 @@ +17 diff --git a/tests/diagnostics/syntax-error-intrinsic.slang.expected b/tests/diagnostics/syntax-error-intrinsic.slang.expected new file mode 100644 index 000000000..98d9bcb75 --- /dev/null +++ b/tests/diagnostics/syntax-error-intrinsic.slang.expected @@ -0,0 +1 @@ +17 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; +} diff --git a/tools/slang-test/parse-diagnostic-util.h b/tools/slang-test/parse-diagnostic-util.h new file mode 100644 index 000000000..26349b779 --- /dev/null +++ b/tools/slang-test/parse-diagnostic-util.h @@ -0,0 +1,30 @@ +// parse-diagnostic-util.h + +#ifndef PARSE_DIAGNOSTIC_UTIL_H +#define PARSE_DIAGNOSTIC_UTIL_H + +#include "../../source/core/slang-string-util.h" +#include "../../source/core/slang-downstream-compiler.h" +#include "../../source/core/slang-string.h" + +#include "../../slang-com-ptr.h" + +struct ParseDiagnosticUtil +{ + static SlangResult splitPathLocation(const Slang::UnownedStringSlice& pathLocation, Slang::DownstreamDiagnostic& outDiagnostic); + + static SlangResult parseFXCLine(const Slang::UnownedStringSlice& line, Slang::List<Slang::UnownedStringSlice>& lineSlices, Slang::DownstreamDiagnostic& outDiagnostic); + + static SlangResult parseDXCLine(const Slang::UnownedStringSlice& line, Slang::List<Slang::UnownedStringSlice>& lineSlices, Slang::DownstreamDiagnostic& outDiagnostic); + + static SlangResult parseGlslangLine(const Slang::UnownedStringSlice& line, Slang::List<Slang::UnownedStringSlice>& lineSlices, Slang::DownstreamDiagnostic& outDiagnostic); + + static SlangResult parseGenericLine(const Slang::UnownedStringSlice& line, Slang::List<Slang::UnownedStringSlice>& lineSlices, Slang::DownstreamDiagnostic& outDiagnostic); + + static SlangResult splitCompilerDiagnosticLine(SlangPassThrough downstreamCompiler, const Slang::UnownedStringSlice& line, Slang::UnownedStringSlice& linePrefix, Slang::List<Slang::UnownedStringSlice>& outSlices); + + static SlangResult parseDiagnostics(const Slang::UnownedStringSlice& inText, Slang::List<Slang::DownstreamDiagnostic>& outDiagnostics); + +}; + +#endif // PARSE_DIAGNOSTIC_UTIL_H diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp index 3a15c6e61..fcb609c34 100644 --- a/tools/slang-test/slang-test-main.cpp +++ b/tools/slang-test/slang-test-main.cpp @@ -10,6 +10,7 @@ #include "../../source/core/slang-string-util.h" #include "../../source/core/slang-byte-encode-util.h" +#include "../../source/core/slang-char-util.h" using namespace Slang; @@ -19,6 +20,7 @@ using namespace Slang; #include "test-reporter.h" #include "options.h" #include "slangc-tool.h" +#include "parse-diagnostic-util.h" #include "../../source/core/slang-downstream-compiler.h" @@ -1244,6 +1246,86 @@ TestResult runSimpleTest(TestContext* context, TestInput& input) return result; } +TestResult runSimpleLineTest(TestContext* context, TestInput& input) +{ + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + auto outputStem = input.outputStem; + + CommandLine cmdLine; + _initSlangCompiler(context, cmdLine); + + cmdLine.addArg(input.filePath); + + for (auto arg : input.testOptions->args) + { + cmdLine.addArg(arg); + } + + ExecuteResult exeRes; + TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes)); + + if (context->isCollectingRequirements()) + { + return TestResult::Pass; + } + + // Parse all the diagnostics so we can extract line numbers + List<DownstreamDiagnostic> diagnostics; + if (SLANG_FAILED(ParseDiagnosticUtil::parseDiagnostics(exeRes.standardError.getUnownedSlice(), diagnostics)) || diagnostics.getCount() <= 0) + { + // Write out the diagnostics which couldn't be parsed. + + String actualOutputPath = outputStem + ".actual"; + Slang::File::writeAllText(actualOutputPath, exeRes.standardError); + + return TestResult::Fail; + } + + StringBuilder actualOutput; + + if (diagnostics.getCount() > 0) + { + actualOutput << diagnostics[0].fileLine << "\n"; + } + else + { + actualOutput << "No output diagnostics\n"; + } + + + String expectedOutputPath = outputStem + ".expected"; + String expectedOutput; + try + { + expectedOutput = Slang::File::readAllText(expectedOutputPath); + } + catch (const Slang::IOException&) + { + } + + TestResult result = TestResult::Pass; + + // Otherwise we compare to the expected output + if (!_areResultsEqual(input.testOptions->type, expectedOutput, actualOutput)) + { + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); + result = TestResult::Fail; + } + + // If the test failed, then we write the actual output to a file + // so that we can easily diff it from the command line and + // diagnose the problem. + if (result == TestResult::Fail) + { + String actualOutputPath = outputStem + ".actual"; + Slang::File::writeAllText(actualOutputPath, actualOutput); + + context->reporter->dumpOutputDifference(expectedOutput, actualOutput); + } + + return result; +} + TestResult runCompile(TestContext* context, TestInput& input) { auto outputStem = input.outputStem; @@ -2688,6 +2770,7 @@ static const TestCommandInfo s_testCommandInfos[] = { { "SIMPLE", &runSimpleTest, 0 }, { "SIMPLE_EX", &runSimpleTest, 0 }, + { "SIMPLE_LINE", &runSimpleLineTest, 0 }, { "REFLECTION", &runReflectionTest, 0 }, { "CPU_REFLECTION", &runReflectionTest, 0 }, { "COMMAND_LINE_SIMPLE", &runSimpleCompareCommandLineTest, 0 }, |
