Files
UnrealEngine/Engine/Source/Runtime/VerseCompiler/Private/uLang/Diagnostics/Glitch.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

272 lines
8.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "uLang/Diagnostics/Glitch.h"
#include "uLang/Common/Text/UTF8String.h"
#include "uLang/Syntax/VstNode.h"
#include "uLang/Semantics/Expression.h"
#include "uLang/SourceProject/IndexedSourceText.h"
namespace uLang
{
namespace
{
constexpr SDiagnosticInfo DiagnosticInfos[] =
{
#define VISIT_DIAGNOSTIC(Code, Severity, EnumName, Description) {Code, EDiagnosticSeverity::Severity, Description},
VERSE_ENUM_DIAGNOSTICS(VISIT_DIAGNOSTIC)
#undef VISIT_DIAGNOSTIC
};
bool VerifyDiagnostics()
{
// Verify that the diagnostics are enumerated in ascending order by code, and that no code is duplicated.
int32_t PreviousDiagnosticCode = INT32_MIN;
for (const SDiagnosticInfo& DiagnosticInfo : DiagnosticInfos)
{
if (DiagnosticInfo.ReferenceCode <= PreviousDiagnosticCode)
{
ULANG_ERRORF(
"Diagnostic with code %i occurs following diagnostic with same or higher code %i. "
"Diagnostics must be in ascending order by code.",
DiagnosticInfo.ReferenceCode,
PreviousDiagnosticCode);
return false;
}
PreviousDiagnosticCode = DiagnosticInfo.ReferenceCode;
}
return true;
}
}
const SDiagnosticInfo& GetDiagnosticInfo(EDiagnostic ResultId)
{
if(size_t(ResultId) >= ULANG_COUNTOF(DiagnosticInfos))
{
ULANG_ERRORF("Invalid diagnostic enum %zu", ResultId);
ULANG_UNREACHABLE();
}
else
{
return DiagnosticInfos[size_t(ResultId)];
}
}
EDiagnostic GetDiagnosticFromReferenceCode(uint16_t ReferenceCode)
{
static const bool bVerifiedDiagnosticInfos = VerifyDiagnostics();
ULANG_ENSUREF(bVerifiedDiagnosticInfos, "Diagnostics failed verification.");
// The DiagnosticInfos array is sorted by reference code (see VerifyDiagnostics), so we can
// look up the index in the array by binary search.
uint32_t MinIndex = 0;
uint32_t MaxIndex = ULANG_COUNTOF(DiagnosticInfos) - 1;
while (MaxIndex >= MinIndex)
{
const uint32_t MidIndex = (MinIndex + MaxIndex) / 2;
if (DiagnosticInfos[MidIndex].ReferenceCode < ReferenceCode)
{
MinIndex = MidIndex + 1;
}
else if (DiagnosticInfos[MidIndex].ReferenceCode > ReferenceCode)
{
MaxIndex = MidIndex - 1;
}
else
{
return EDiagnostic(MidIndex);
}
};
ULANG_ERRORF("Failed to find diagnostic matching reference code %u", ReferenceCode);
ULANG_UNREACHABLE();
}
SGlitchResult::SGlitchResult(EDiagnostic ResultId)
: _Id(ResultId)
, _Message(GetDiagnosticInfo(ResultId).Description)
{
}
SGlitchLocus::SGlitchLocus(const Verse::Vst::Node* VstNode)
: _SnippetPath(VstNode ? VstNode->GetSnippetPath() : CUTF8String::GetEmpty())
, _Range(VstNode ? VstNode->Whence() : STextRange())
, _ResultPos(_Range.GetEnd())
, _VstIdentifier(uintptr_t(VstNode))
{
}
SGlitchLocus::SGlitchLocus(const CAstNode* AstNode)
: SGlitchLocus(AstNode ? AstNode->GetMappedVstNode() : (const Verse::Vst::Node*)nullptr)
{
// TODO: Fill in _MemberInfo
}
CUTF8String SGlitchLocus::AsFormattedString() const
{
return CUTF8String(
"%s(%d,%d, %d,%d)",
_SnippetPath.AsCString(),
_Range.BeginRow() + 1, _Range.BeginColumn() + 1,
_Range.EndRow() + 1, _Range.EndColumn() + 1);
}
CUTF8String SGlitch::FormattedString(const char* Message, const char* Path, const STextRange& Range, EDiagnosticSeverity Severity, EDiagnostic Diagnostic)
{
const char* Category = "Verse compiler";
bool bUseRefCode = Diagnostic != EDiagnostic::Ok;
switch (Severity)
{
case EDiagnosticSeverity::Info: Category = "Verse compiler info"; break;
case EDiagnosticSeverity::Warning: Category = "Verse compiler warning"; break;
case EDiagnosticSeverity::Error: Category = "Verse compiler error"; break;
case EDiagnosticSeverity::Ok: bUseRefCode = false; break;
default: ULANG_UNREACHABLE();
};
return bUseRefCode
? CUTF8String(
"%s(%d,%d, %d,%d): %s V%d: %s",
Path,
Range.BeginRow() + 1, Range.BeginColumn() + 1,
Range.EndRow() + 1, Range.EndColumn() + 1,
Category,
GetDiagnosticInfo(Diagnostic).ReferenceCode,
Message)
: CUTF8String(
"%s(%d,%d, %d,%d): %s: %s",
Path,
Range.BeginRow() + 1, Range.BeginColumn() + 1,
Range.EndRow() + 1, Range.EndColumn() + 1,
Category,
Message);
}
TOptional<int32_t> ScanToRowCol(CUTF8StringView const& Source, const STextPosition& Position)
{
// Scan to row, col
uint32_t CurRow = 0;
uint32_t CurCol = 0;
const UTF8Char* Ch = Source._Begin;
while (Ch < Source._End && (CurRow < Position._Row || (CurRow == Position._Row && CurCol < Position._Column)))
{
if (*Ch == '\n')
{
++Ch;
++CurRow;
CurCol = 0;
}
else if (*Ch == '\r')
{
++Ch;
++CurRow;
CurCol = 0;
if (*Ch == '\n')
{
++Ch;
}
}
else
{
++Ch;
++CurCol;
}
}
// TODO: Account for wider characters
if (CurRow == Position._Row && CurCol == Position._Column)
{
return int32_t(Ch - Source._Begin);
}
else
{
return EResult::Unspecified;
}
}
TOptional<int32_t> ScanToRowCol(const SIndexedSourceText& SourceText, const STextPosition& Position)
{
if (TOptional<int64_t> LineOffset = SourceText.CalculateOffsetForLine(Position._Row))
{
const CUTF8StringView Source = SourceText._SourceText;
// Scan to row, col
uint32_t CurRow = Position._Row;
uint32_t CurCol = 0;
const UTF8Char* Ch = Source._Begin + *LineOffset;
// We shouldn't iterate here very much
while (Ch < Source._End && (CurRow < Position._Row || (CurRow == Position._Row && CurCol < Position._Column)))
{
if (*Ch == '\n')
{
++Ch;
++CurRow;
CurCol = 0;
}
else if (*Ch == '\r')
{
++Ch;
++CurRow;
CurCol = 0;
if (*Ch == '\n')
{
++Ch;
}
}
else
{
++Ch;
++CurCol;
}
}
// TODO: Account for wider characters
if (CurRow == Position._Row && CurCol == Position._Column)
{
return int32_t(Ch - Source._Begin);
}
else
{
return EResult::Unspecified;
}
}
return EResult::Error;
}
CUTF8StringView TextRangeToStringView(CUTF8StringView const& Source, STextRange const& Range)
{
ULANG_ASSERT(Range.IsOrdered());
TOptional<int32_t> BeginOffset = ScanToRowCol(Source, Range.GetBegin());
TOptional<int32_t> EndOffset = ScanToRowCol(Source, Range.GetEnd());
if (BeginOffset && EndOffset)
{
ULANG_ASSERT(BeginOffset.GetValue() <= EndOffset.GetValue());
return Source.SubView(BeginOffset.GetValue(), EndOffset.GetValue() - BeginOffset.GetValue());
}
else
{
return CUTF8StringView();
}
}
CUTF8StringView TextRangeToStringView(const uLang::SIndexedSourceText& SourceText, STextRange const& Range)
{
ULANG_ASSERT(Range.IsOrdered());
TOptional<int32_t> BeginOffset = ScanToRowCol(SourceText, Range.GetBegin());
TOptional<int32_t> EndOffset = ScanToRowCol(SourceText, Range.GetEnd());
if (BeginOffset && EndOffset)
{
ULANG_ASSERT(BeginOffset.GetValue() <= EndOffset.GetValue());
return SourceText._SourceText.ToStringView().SubView(BeginOffset.GetValue(), EndOffset.GetValue() - BeginOffset.GetValue());
}
return CUTF8StringView();
}
} // namespace uLang