1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
// SPDX-License-Identifier: GPL-3.0-or-later
#include "rcc/Support/TextDiagnostic.h"
#include <algorithm>
#include <iterator>
#include <sstream>
#include <utility>
namespace rcc {
std::string_view getDiagnosticSeverityName(DiagnosticSeverity Severity) noexcept {
switch (Severity) {
case DiagnosticSeverity::Note:
return "note";
case DiagnosticSeverity::Warning:
return "warning";
case DiagnosticSeverity::Error:
return "error";
}
return "unknown";
}
TextSource::TextSource(std::string Name, std::string Text)
: Name(std::move(Name)), Text(std::move(Text)), LineOffsets{0} {
for (std::size_t Index = 0; Index < this->Text.size(); ++Index) {
if (this->Text[Index] == '\n')
LineOffsets.push_back(Index + 1);
}
}
TextPosition TextSource::positionAt(std::size_t Offset) const noexcept {
Offset = std::min(Offset, Text.size());
const auto LineIterator = std::upper_bound(LineOffsets.begin(), LineOffsets.end(), Offset);
const std::size_t LineIndex =
static_cast<std::size_t>(std::distance(LineOffsets.begin(), LineIterator) - 1);
return TextPosition{
.Offset = Offset,
.Line = LineIndex + 1,
.Column = Offset - LineOffsets[LineIndex] + 1,
};
}
TextRange TextSource::range(std::size_t Begin, std::size_t End) const noexcept {
Begin = std::min(Begin, Text.size());
End = std::clamp(End, Begin, Text.size());
return TextRange{.Begin = positionAt(Begin), .End = positionAt(End)};
}
std::string_view TextSource::line(std::size_t OneBasedLine) const noexcept {
if (OneBasedLine == 0 || OneBasedLine > LineOffsets.size())
return {};
const std::size_t Begin = LineOffsets[OneBasedLine - 1];
std::size_t End = OneBasedLine < LineOffsets.size() ? LineOffsets[OneBasedLine] - 1 : Text.size();
if (End > Begin && Text[End - 1] == '\r')
--End;
return std::string_view(Text).substr(Begin, End - Begin);
}
std::string formatTextDiagnostic(const TextDiagnostic &Diagnostic, const TextSource &Source) {
std::ostringstream Output;
const TextPosition Position = Diagnostic.Range.Begin;
const std::string_view SourceName =
Diagnostic.SourceName.empty() ? Source.name() : std::string_view(Diagnostic.SourceName);
Output << SourceName << ':' << Position.Line << ':' << Position.Column << ": "
<< getDiagnosticSeverityName(Diagnostic.Severity);
if (!Diagnostic.Category.empty())
Output << '[' << Diagnostic.Category << ']';
Output << ": " << Diagnostic.Message;
const std::string_view SourceLine = Source.line(Position.Line);
if (SourceLine.empty())
return Output.str();
Output << '\n' << SourceLine << '\n';
const std::size_t PrefixWidth = Position.Column > 0 ? Position.Column - 1 : 0;
for (std::size_t Index = 0; Index < PrefixWidth && Index < SourceLine.size(); ++Index)
Output << (SourceLine[Index] == '\t' ? '\t' : ' ');
Output << '^';
if (Diagnostic.Range.End.Line == Position.Line &&
Diagnostic.Range.End.Column > Position.Column + 1) {
Output << std::string(Diagnostic.Range.End.Column - Position.Column - 1, '~');
}
return Output.str();
}
} // namespace rcc
|