diff --git a/include/Nazara/Shader/ShaderLangErrors.hpp b/include/Nazara/Shader/ShaderLangErrors.hpp index 20bcafc01..24aeaa557 100644 --- a/include/Nazara/Shader/ShaderLangErrors.hpp +++ b/include/Nazara/Shader/ShaderLangErrors.hpp @@ -40,7 +40,7 @@ namespace Nz::ShaderLang { public: inline Error(SourceLocation sourceLocation, ErrorCategory errorCategory, ErrorType errorType) noexcept; - Error(const Error&) = delete; + Error(const Error&) = default; Error(Error&&) noexcept = default; ~Error() = default; @@ -51,7 +51,7 @@ namespace Nz::ShaderLang const char* what() const noexcept override; - Error& operator=(const Error&) = delete; + Error& operator=(const Error&) = default; Error& operator=(Error&&) noexcept = default; protected: diff --git a/src/Nazara/Shader/ShaderLangErrors.cpp b/src/Nazara/Shader/ShaderLangErrors.cpp index 43ea7bef1..905755bf9 100644 --- a/src/Nazara/Shader/ShaderLangErrors.cpp +++ b/src/Nazara/Shader/ShaderLangErrors.cpp @@ -118,14 +118,14 @@ namespace Nz::ShaderLang sourceFile = *m_sourceLocation.file; if (m_sourceLocation.startLine != m_sourceLocation.endLine) - m_errorMessage = fmt::format("{}({} -> {},{} -> {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorCategory, m_errorType, BuildErrorMessage()); + m_errorMessage = fmt::format("{}({} -> {},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.endLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage()); else if (m_sourceLocation.startColumn != m_sourceLocation.endColumn) - m_errorMessage = fmt::format("{}({},{} -> {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorCategory, m_errorType, BuildErrorMessage()); + m_errorMessage = fmt::format("{}({},{} -> {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_sourceLocation.endColumn, m_errorType, BuildErrorMessage()); else - m_errorMessage = fmt::format("{}({}, {}): {} error {}: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorCategory, m_errorType, BuildErrorMessage()); + m_errorMessage = fmt::format("{}({}, {}): {} error: {}", sourceFile, m_sourceLocation.startLine, m_sourceLocation.startColumn, m_errorType, BuildErrorMessage()); } else - m_errorMessage = fmt::format("?: {} error {}: {}", m_errorCategory, m_errorType, BuildErrorMessage()); + m_errorMessage = fmt::format("?: {} error: {}", m_errorType, BuildErrorMessage()); } return m_errorMessage; diff --git a/src/Nazara/Shader/ShaderLangLexer.cpp b/src/Nazara/Shader/ShaderLangLexer.cpp index 60d107f66..df762ef97 100644 --- a/src/Nazara/Shader/ShaderLangLexer.cpp +++ b/src/Nazara/Shader/ShaderLangLexer.cpp @@ -224,6 +224,7 @@ namespace Nz::ShaderLang } token.location.endColumn = SafeCast(currentPos - lastLineFeed) + 1; + token.location.endLine = currentLine; if (floatingPoint) { @@ -244,17 +245,18 @@ namespace Nz::ShaderLang { tokenType = TokenType::IntegerValue; + // avoid std::string_view operator[] assertions (if &str[currentPos + 1] is out of the string) + const char* first = &str[start]; + const char* last = first + (currentPos - start + 1); + long long value; - std::from_chars_result r = std::from_chars(&str[start], &str[currentPos + 1], value); - if (r.ptr != &str[currentPos + 1]) - { - if (r.ec == std::errc::result_out_of_range) - throw LexerNumberOutOfRangeError{ token.location }; - + std::from_chars_result r = std::from_chars(first, last, value); + if (r.ptr == last && r.ec == std::errc{}) + token.data = value; + else if (r.ec == std::errc::result_out_of_range) + throw LexerNumberOutOfRangeError{ token.location }; + else throw LexerBadNumberError{ token.location }; - } - - token.data = value; } break; @@ -414,12 +416,13 @@ namespace Nz::ShaderLang case '\n': case '\r': token.location.endColumn = SafeCast(currentPos - lastLineFeed) + 1; + token.location.endLine = currentLine; throw LexerUnfinishedStringError{ token.location }; case '\\': { currentPos++; - char next = Peek(); + char next = Peek(0); switch (next) { case 'n': character = '\n'; break; @@ -429,6 +432,7 @@ namespace Nz::ShaderLang case '\\': character = '\\'; break; default: token.location.endColumn = SafeCast(currentPos - lastLineFeed) + 1; + token.location.endLine = currentLine; throw LexerUnrecognizedCharError{ token.location }; } break; @@ -469,7 +473,11 @@ namespace Nz::ShaderLang break; } else + { + token.location.endColumn = SafeCast(currentPos - lastLineFeed) + 1; + token.location.endLine = currentLine; throw LexerUnrecognizedTokenError{ token.location }; + } } } diff --git a/src/Nazara/Shader/ShaderLangParser.cpp b/src/Nazara/Shader/ShaderLangParser.cpp index 8348e9057..d1f1a3496 100644 --- a/src/Nazara/Shader/ShaderLangParser.cpp +++ b/src/Nazara/Shader/ShaderLangParser.cpp @@ -285,7 +285,7 @@ namespace Nz::ShaderLang } if (!moduleVersion.has_value()) - throw ParserMissingAttributeError{ moduleToken.location }; + throw ParserMissingAttributeError{ moduleToken.location, ShaderAst::AttributeType::LangVersion }; if (!moduleId) moduleId = Uuid::Generate(); diff --git a/tests/Engine/Shader/ErrorsTest.cpp b/tests/Engine/Shader/ErrorsTest.cpp new file mode 100644 index 000000000..6e184ae3f --- /dev/null +++ b/tests/Engine/Shader/ErrorsTest.cpp @@ -0,0 +1,24 @@ +#include +#include +#include + +TEST_CASE("errors", "[Shader]") +{ + SECTION("Checking lexer errors") + { + CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("1x42"), "(1,1 -> 4): LBadNumber error: bad number"); + CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("123456789876543210123456789"), "(1,1 -> 27): LNumberOutOfRange error: number is out of range"); + CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("\"Hello world"), "(1,1 -> 13): LUnfinishedString error: unfinished string"); + CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize(R"("hello \p")"), "(1,1 -> 9): LUnrecognizedChar error: unrecognized character"); + CHECK_THROWS_WITH(Nz::ShaderLang::Tokenize("$"), "(1, 1): LUnrecognizedToken error: unrecognized token"); + } + + SECTION("Checking parser errors") + { + CHECK_THROWS_WITH(Nz::ShaderLang::Parse("nazara"), "(1,1 -> 6): PUnexpectedToken error: unexpected token Identifier"); + CHECK_THROWS_WITH(Nz::ShaderLang::Parse("module;"), "(1,1 -> 6): PMissingAttribute error: missing attribute nzsl_version"); + CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version] module;"), "(1,2 -> 13): PAttributeMissingParameter error: attribute nzsl_version requires a parameter"); + CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), nzsl_version(\"1.0\")] module;"), "(1,23 -> 41): PAttributeMultipleUnique error: attribute nzsl_version can only be present once"); + CHECK_THROWS_WITH(Nz::ShaderLang::Parse("[nzsl_version(\"1.0\"), uuid(\"Nazara\")] module;"), "(1,23 -> 36): PInvalidUuid error: \"Nazara\" is not a valid UUID"); + } +}