// Copyright 2021 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn_native/CompilationMessages.h" #include "common/Assert.h" #include "dawn_native/dawn_platform.h" #include namespace dawn::native { namespace { WGPUCompilationMessageType tintSeverityToMessageType(tint::diag::Severity severity) { switch (severity) { case tint::diag::Severity::Note: return WGPUCompilationMessageType_Info; case tint::diag::Severity::Warning: return WGPUCompilationMessageType_Warning; default: return WGPUCompilationMessageType_Error; } } } // anonymous namespace OwnedCompilationMessages::OwnedCompilationMessages() { mCompilationInfo.nextInChain = 0; mCompilationInfo.messageCount = 0; mCompilationInfo.messages = nullptr; } void OwnedCompilationMessages::AddMessageForTesting(std::string message, wgpu::CompilationMessageType type, uint64_t lineNum, uint64_t linePos, uint64_t offset, uint64_t length) { // Cannot add messages after GetCompilationInfo has been called. ASSERT(mCompilationInfo.messages == nullptr); mMessageStrings.push_back(message); mMessages.push_back({nullptr, nullptr, static_cast(type), lineNum, linePos, offset, length}); } void OwnedCompilationMessages::AddMessage(const tint::diag::Diagnostic& diagnostic) { // Cannot add messages after GetCompilationInfo has been called. ASSERT(mCompilationInfo.messages == nullptr); // Tint line and column values are 1-based. uint64_t lineNum = diagnostic.source.range.begin.line; uint64_t linePos = diagnostic.source.range.begin.column; // The offset is 0-based. uint64_t offset = 0; uint64_t length = 0; if (lineNum && linePos && diagnostic.source.file_content) { const std::vector& lines = diagnostic.source.file_content->lines; size_t i = 0; // To find the offset of the message position, loop through each of the first lineNum-1 // lines and add it's length (+1 to account for the line break) to the offset. for (; i < lineNum - 1; ++i) { offset += lines[i].length() + 1; } // If the end line is on a different line from the beginning line, add the length of the // lines in between to the ending offset. uint64_t endLineNum = diagnostic.source.range.end.line; uint64_t endLinePos = diagnostic.source.range.end.column; // If the range has a valid start but the end it not specified, clamp it to the start. if (endLineNum == 0 || endLinePos == 0) { endLineNum = lineNum; endLinePos = linePos; } // Negative ranges aren't allowed ASSERT(endLineNum >= lineNum); uint64_t endOffset = offset; for (; i < endLineNum - 1; ++i) { endOffset += lines[i].length() + 1; } // Add the line positions to the offset and endOffset to get their final positions // within the code string. offset += linePos - 1; endOffset += endLinePos - 1; // Negative ranges aren't allowed ASSERT(endOffset >= offset); // The length of the message is the difference between the starting offset and the // ending offset. length = endOffset - offset; } if (diagnostic.code) { mMessageStrings.push_back(std::string(diagnostic.code) + ": " + diagnostic.message); } else { mMessageStrings.push_back(diagnostic.message); } mMessages.push_back({nullptr, nullptr, tintSeverityToMessageType(diagnostic.severity), lineNum, linePos, offset, length}); } void OwnedCompilationMessages::AddMessages(const tint::diag::List& diagnostics) { // Cannot add messages after GetCompilationInfo has been called. ASSERT(mCompilationInfo.messages == nullptr); for (const auto& diag : diagnostics) { AddMessage(diag); } AddFormattedTintMessages(diagnostics); } void OwnedCompilationMessages::ClearMessages() { // Cannot clear messages after GetCompilationInfo has been called. ASSERT(mCompilationInfo.messages == nullptr); mMessageStrings.clear(); mMessages.clear(); } const WGPUCompilationInfo* OwnedCompilationMessages::GetCompilationInfo() { mCompilationInfo.messageCount = mMessages.size(); mCompilationInfo.messages = mMessages.data(); // Ensure every message points at the correct message string. Cannot do this earlier, since // vector reallocations may move the pointers around. for (size_t i = 0; i < mCompilationInfo.messageCount; ++i) { WGPUCompilationMessage& message = mMessages[i]; std::string& messageString = mMessageStrings[i]; message.message = messageString.c_str(); } return &mCompilationInfo; } const std::vector& OwnedCompilationMessages::GetFormattedTintMessages() { return mFormattedTintMessages; } void OwnedCompilationMessages::AddFormattedTintMessages(const tint::diag::List& diagnostics) { tint::diag::List messageList; size_t warningCount = 0; size_t errorCount = 0; for (auto& diag : diagnostics) { switch (diag.severity) { case (tint::diag::Severity::Fatal): case (tint::diag::Severity::Error): case (tint::diag::Severity::InternalCompilerError): { errorCount++; messageList.add(tint::diag::Diagnostic(diag)); break; } case (tint::diag::Severity::Warning): { warningCount++; messageList.add(tint::diag::Diagnostic(diag)); break; } default: break; } } if (errorCount == 0 && warningCount == 0) { return; } tint::diag::Formatter::Style style; style.print_newline_at_end = false; std::ostringstream t; if (errorCount > 0) { t << errorCount << " error(s) "; if (warningCount > 0) { t << "and "; } } if (warningCount > 0) { t << warningCount << " warning(s) "; } t << "generated while compiling the shader:" << std::endl << tint::diag::Formatter{style}.format(messageList); mFormattedTintMessages.push_back(t.str()); } } // namespace dawn::native