* Copyright (c) 2025-2026 Huawei Device Co., Ltd.
* 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 "diagnostic.h"
#include <iomanip>
#include <memory>
#include <sstream>
#include <vector>
#include "generated/diagnostic.h"
#include "lexer/token/sourceLocation.h"
#include "parser/program/program.h"
#include "checker/types/type.h"
#include "checker/types/signature.h"
namespace ark::es2panda::util {
std::string Format(const DiagnosticMessageElement &elem)
{
if (std::holds_alternative<std::string>(elem)) {
return std::get<std::string>(elem);
}
if (std::holds_alternative<std::string_view>(elem)) {
return std::string(std::get<std::string_view>(elem));
}
if (std::holds_alternative<const char *const>(elem)) {
return std::get<const char *const>(elem);
}
if (std::holds_alternative<StringView>(elem)) {
return std::string(std::get<StringView>(elem));
}
if (std::holds_alternative<size_t>(elem)) {
return std::to_string(std::get<size_t>(elem));
}
if (std::holds_alternative<lexer::TokenType>(elem)) {
return TokenToString(std::get<lexer::TokenType>(elem));
}
if (std::holds_alternative<AsSrc>(elem)) {
std::stringstream ss;
std::get<AsSrc>(elem).GetType()->ToStringAsSrc(ss);
return ss.str();
}
if (std::holds_alternative<const checker::Type *const>(elem)) {
std::stringstream ss;
std::get<const checker::Type *const>(elem)->ToString(ss);
return ss.str();
}
if (std::holds_alternative<const checker::Signature *const>(elem)) {
std::stringstream ss;
std::get<const checker::Signature *const>(elem)->ToString(ss, nullptr, true);
return ss.str();
}
ES2PANDA_UNREACHABLE();
}
std::string Format(const DiagnosticMessageParams &list)
{
std::stringstream ss;
for (const auto &it : list) {
ss << Format(it);
}
return ss.str();
}
std::vector<std::string> FormatParams(const DiagnosticMessageParams &list)
{
std::vector<std::string> params;
params.reserve(list.size());
for (const auto &it : list) {
std::stringstream ss;
ss << Format(it);
params.push_back(ss.str());
}
return params;
}
std::string Format(std::string_view formatString, const std::vector<std::string> ¶ms)
{
if (params.empty()) {
return std::string(formatString);
}
std::string result;
size_t pos = 0;
size_t paramIndex = 0;
while (pos < formatString.size()) {
auto nextPos = formatString.find("{}", pos);
if (nextPos == std::string::npos) {
break;
}
result.append(formatString.substr(pos, nextPos - pos));
ASSERT_PRINT(paramIndex < params.size(),
"Too few params (" + std::to_string(params.size()) + ") given for " + std::string(formatString));
result.append(params.at(paramIndex++));
pos = nextPos + 2U;
}
ASSERT_PRINT(paramIndex == params.size(), "Placeholder and param count mismatch in " + std::string(formatString));
result.append(formatString.substr(pos));
return result;
}
bool DiagnosticBase::operator<(const DiagnosticBase &rhs) const
{
if (File() != rhs.File()) {
return File() < rhs.File();
}
if (Line() != rhs.Line()) {
return Line() < rhs.Line();
}
if (Offset() != rhs.Offset()) {
return Offset() < rhs.Offset();
}
if (Type() != rhs.Type()) {
return Type() < rhs.Type();
}
return Message() < rhs.Message();
}
bool DiagnosticBase::operator==(const DiagnosticBase &rhs) const
{
if (File() != rhs.File()) {
return false;
}
if (Line() != rhs.Line()) {
return false;
}
if (Offset() != rhs.Offset()) {
return false;
}
if (Type() != rhs.Type()) {
return false;
}
return Message() == rhs.Message();
}
DiagnosticType Diagnostic::Type() const
{
return type_;
}
uint32_t Diagnostic::GetId() const
{
return diagnosticKind_->Id();
}
std::string Diagnostic::Message() const
{
return Format(diagnosticKind_->Message(), diagnosticParams_);
}
const char *DiagnosticTypeToString(DiagnosticType type)
{
switch (type) {
case DiagnosticType::FATAL:
return "Fatal error";
case DiagnosticType::SYNTAX:
return "Syntax error";
case DiagnosticType::SEMANTIC:
return "Semantic error";
case DiagnosticType::WARNING:
return "Warning";
case DiagnosticType::PLUGIN_WARNING:
return "Plugin warning";
case DiagnosticType::PLUGIN_ERROR:
return "Plugin error";
case DiagnosticType::DECLGEN_ETS2TS_ERROR:
return "Declgen ets2ts error";
case DiagnosticType::DECLGEN_ETS2TS_WARNING:
return "Declgen ets2ts warning";
case DiagnosticType::ISOLATED_DECLGEN:
return "Isolated declgen error";
case DiagnosticType::ARKTS_CONFIG_ERROR:
return "ArkTS config error";
case DiagnosticType::SUGGESTION:
return "SUGGESTION";
default:
ES2PANDA_UNREACHABLE();
}
}
const char *DiagnosticTypePrefixSymbol(DiagnosticType type)
{
switch (type) {
case DiagnosticType::FATAL:
return "F";
case DiagnosticType::SYNTAX:
return "ESY";
case DiagnosticType::SEMANTIC:
return "ESE";
case DiagnosticType::WARNING:
return "W";
case DiagnosticType::PLUGIN_WARNING:
return "WP";
case DiagnosticType::PLUGIN_ERROR:
return "EP";
case DiagnosticType::DECLGEN_ETS2TS_ERROR:
return "ED";
case DiagnosticType::DECLGEN_ETS2TS_WARNING:
return "WD";
case DiagnosticType::ISOLATED_DECLGEN:
return "EID";
case DiagnosticType::ARKTS_CONFIG_ERROR:
return "EAC";
case DiagnosticType::SUGGESTION:
return "S";
default:
ES2PANDA_UNREACHABLE();
}
}
std::string DiagnosticBase::ToStringUniqueNumber() const
{
const size_t minWidthNumber = 4;
std::stringstream ss;
ss << std::setfill('0') << std::setw(minWidthNumber) << GetId();
return std::string {DiagnosticTypePrefixSymbol(Type())} + ss.str();
}
DiagnosticBase::DiagnosticBase(const lexer::SourcePosition &pos)
{
posData_ = pos;
if (pos.Program() != nullptr) {
file_ = pos.Program()->SourceFilePath().Utf8();
}
}
DiagnosticBase::DiagnosticBase(const lexer::SourceLocation &loc)
{
posData_ = loc;
if (loc.Program() != nullptr) {
file_ = loc.Program()->SourceFilePath().Utf8();
}
}
void DiagnosticBase::EnsureLocation()
{
if (HasPosition()) {
if (Position()->Program() != nullptr) {
file_ = Position()->Program()->SourceFilePath().Utf8();
posData_ = Position()->ToLocation();
} else {
file_ = "";
posData_ = lexer::SourceLocation();
}
}
}
ThrowableDiagnostic::ThrowableDiagnostic(DiagnosticType type, const DiagnosticMessageParams ¶ms,
const lexer::SourceLocation &loc)
: DiagnosticBase(loc), type_(type), message_(Format(params))
{
}
ThrowableDiagnostic::ThrowableDiagnostic(DiagnosticType type, const DiagnosticMessageParams ¶ms,
const lexer::SourcePosition &pos)
: DiagnosticBase(pos), type_(type), message_(Format(params))
{
}
ThrowableDiagnostic::ThrowableDiagnostic(DiagnosticType type, const DiagnosticMessageParams ¶ms,
std::string_view file, size_t line, size_t offset)
: DiagnosticBase(file, line, offset), type_(type), message_(Format(params))
{
}
ThrowableDiagnostic::ThrowableDiagnostic(const DiagnosticType type, const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams,
const lexer::SourcePosition &pos)
: DiagnosticBase(pos), type_(type), message_(Format(diagnosticKind.Message(), FormatParams(diagnosticParams)))
{
}
Suggestion::Suggestion(const diagnostic::DiagnosticKind *kind, std::vector<std::string> ¶ms,
const char *substitutionCode, const char *title, const lexer::SourceRange *range)
: kind_(kind),
substitutionCode_(substitutionCode),
title_(title),
message_(Format(kind->Message(), params)),
range_(range)
{
}
DiagnosticType Suggestion::Type() const
{
return kind_->Type();
}
uint32_t Suggestion::GetId() const
{
return kind_->Id();
}
Diagnostic::Diagnostic(const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos,
std::vector<class Suggestion *> &&suggestions)
: DiagnosticBase(pos),
diagnosticKind_(&diagnosticKind),
type_(diagnosticKind.Type()),
diagnosticParams_(FormatParams(diagnosticParams)),
suggestions_(std::make_unique<std::vector<class Suggestion *>>(std::move(suggestions)))
{
}
Diagnostic::Diagnostic(const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos,
std::initializer_list<class Suggestion *> suggestions)
: DiagnosticBase(pos),
diagnosticKind_(&diagnosticKind),
type_(diagnosticKind.Type()),
diagnosticParams_(FormatParams(diagnosticParams))
{
if (suggestions.size() != 0) {
suggestions_ = std::make_unique<std::vector<class Suggestion *>>();
for (auto suggestion : suggestions) {
suggestions_->emplace_back(suggestion);
}
}
}
Diagnostic::Diagnostic(const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams)
: Diagnostic(diagnosticKind, diagnosticParams, lexer::SourcePosition(), {})
{
}
Diagnostic::Diagnostic(const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos,
class Suggestion *suggestion)
: Diagnostic(diagnosticKind, diagnosticParams, pos, {suggestion})
{
}
Diagnostic::Diagnostic(const diagnostic::DiagnosticKind &diagnosticKind,
const util::DiagnosticMessageParams &diagnosticParams, const lexer::SourcePosition &pos)
: Diagnostic(diagnosticKind, diagnosticParams, pos, {})
{
}
}