• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ArkScript-lang / Ark / 21488144923

29 Jan 2026 05:26PM UTC coverage: 93.408% (-0.009%) from 93.417%
21488144923

push

github

SuperFola
chore: update credits

8828 of 9451 relevant lines covered (93.41%)

274416.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.07
/src/arkreactor/Error/Diagnostics.cpp
1
#include <Ark/Error/Diagnostics.hpp>
2

3
#include <cassert>
4
#include <sstream>
5
#include <algorithm>
6
#include <fmt/color.h>
7
#include <fmt/ostream.h>
8

9
#include <Ark/Constants.hpp>
10
#include <Ark/Utils/Utils.hpp>
11
#include <Ark/Utils/Literals.hpp>
12
#include <Ark/Compiler/AST/Node.hpp>
13
#include <Ark/Error/PrettyPrinting.hpp>
14

15
namespace Ark::Diagnostics
16
{
17
    using namespace Ark::literals;
18

19
    void showFileLocation(std::ostream& os, const ErrorLocation& loc)
297✔
20
    {
297✔
21
        if (loc.filename != ARK_NO_NAME_FILE)
297✔
22
            fmt::print(os, "In file {}:{}\n", loc.filename, loc.start.line + 1);
296✔
23
    }
297✔
24

25
    void hintWithContext(std::ostream& os, const std::optional<CodeErrorContext>& maybe_context, const bool colorize, const bool start)
13✔
26
    {
13✔
27
        if (!maybe_context)
13✔
28
            return;
×
29

30
        fmt::print(os, "{}", Printer::GhostLinePrefix);
13✔
31
        fmt::print(
39✔
32
            os,
13✔
33
            "{: <{}}{}\n",
13✔
34
            // padding os spaces
35
            " ",
36
            std::max(1_z, maybe_context->at.start.column),  // fixing padding when the error is on the first character
13✔
37
            // underline the parent of the error in red
38
            fmt::styled(
26✔
39
                start
25✔
40
                    ? (maybe_context->is_macro_expansion ? "┌─ macro expansion started here" : "┌─ expression started here")
12✔
41
                    : "└─ ... and ended here",
42
                colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
13✔
43
    }
13✔
44

45
    void makeContext(
297✔
46
        const ErrorLocation& loc,
47
        std::ostream& os,
48
        const std::optional<CodeErrorContext>& maybe_context,  // can not be populated at runtime, only compile time
49
        const bool colorize)
50
    {
297✔
51
        assert(!(maybe_context && loc.wholeLineIsError()) && "Can not create error context when a context is given AND the whole line has to be underlined");
297✔
52

53
        Printer source_printer(loc.filename, loc.start.line, loc.maybeEndLine(), colorize, loc.maybe_content);
297✔
54
        if (!source_printer.hasContent())
297✔
55
        {
56
            showFileLocation(os, loc);
×
57
            return;
×
58
        }
59

60
        const bool ctx_same_file = maybe_context && maybe_context->filename == loc.filename;
297✔
61
        const bool ctx_in_window = ctx_same_file && maybe_context && source_printer.coversLine(maybe_context->at.start.line);
297✔
62

63
        if (ctx_same_file && !ctx_in_window)
297✔
64
            source_printer.extendWindow(maybe_context->at.start.line);
3✔
65
        else if (maybe_context && !ctx_same_file && !maybe_context->filename.empty())
294✔
66
        {
67
            // show the location of the parent of our error first
68
            std::string uniformised_filename;
1✔
69
            std::ranges::replace_copy(maybe_context->filename, std::back_inserter(uniformised_filename), '\\', '/');
1✔
70
            fmt::print(os, "Error originated from file {}:{}\n", uniformised_filename, maybe_context->at.start.line + 1);
1✔
71

72
            std::optional<decltype(internal::FilePos::line)> maybe_end_line = std::nullopt;
1✔
73
            if (maybe_context->at.end)
1✔
74
                maybe_end_line = maybe_context->at.end->line;
1✔
75
            Printer printer(maybe_context->filename, maybe_context->at.start.line, maybe_end_line, colorize);
1✔
76

77
            while (printer.hasContent())
5✔
78
            {
79
                if (printer.isNextLineTheFirstLineOfTarget())
4✔
80
                    hintWithContext(os, maybe_context, colorize, /* start= */ true);
1✔
81
                printer.printLine(os);
4✔
82
                if (printer.isLastLineOfTarget())
4✔
83
                    hintWithContext(os, maybe_context, colorize, /* start= */ false);
1✔
84
            }
85

86
            fmt::print(os, "\n");
1✔
87
        }
1✔
88

89
        showFileLocation(os, loc);
297✔
90

91
        while (source_printer.hasContent())
1,111✔
92
        {
93
            const std::size_t i = source_printer.current();
814✔
94
            const std::string& line = source_printer.currentLine();
814✔
95

96
            // if the error context is in the current file, point to it as the parent of our error
97
            if (ctx_same_file && i == maybe_context->at.start.line && i < loc.start.line)
814✔
98
                hintWithContext(os, maybe_context, colorize, /* start= */ true);
11✔
99
            source_printer.printLine(os);
814✔
100

101
            // show where the error occurred
102
            if (source_printer.isTargetLine() && !line.empty())
814✔
103
            {
104
                if (!loc.wholeLineIsError())
300✔
105
                {
106
                    const std::size_t line_first_char = (line.find_first_not_of(" \t\v") == std::string::npos) ? 0 : line.find_first_not_of(" \t\v");
144✔
107

108
                    const std::size_t col_start = (i == loc.start.line) ? loc.start.column : line_first_char + 1;
144✔
109
                    // due to the `!loc.wholeLineIsError()` check, we are guaranteed to have a value in loc.end
110
                    const std::size_t col_end = (i == loc.end->line) ? loc.end->column : line.size();
144✔
111

112
                    // ignore the last line that is sometimes erroneous in multiline contexts
113
                    if (i == loc.end->line && loc.end->line != loc.start.line && col_start >= col_end)
144✔
114
                        continue;
3✔
115
                    fmt::print(os, "{}", Printer::GhostLinePrefix);
141✔
116

117
                    // show the error where it's at, using the normal process, if there is no context OR
118
                    // if the context line is different from the error line
119
                    if (!maybe_context || maybe_context->at.start.line != loc.start.line)
141✔
120
                        fmt::print(
381✔
121
                            os,
127✔
122
                            "{: <{}}{:~<{}}\n",
127✔
123
                            // padding of spaces
124
                            " ",
125
                            std::max(1_z, std::min(col_start, col_end)),  // fixing padding when the error is on the first character
127✔
126
                            // underline the error in red
127
                            fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
127✔
128
                            col_start < col_end ? col_end - col_start : 1);
127✔
129
                    // cppcheck-suppress knownConditionTrueFalse ; suppressing error so that the condition is explicit to the reader
130
                    else if (maybe_context && maybe_context->at.start.line == loc.start.line && i == loc.start.line)
14✔
131
                    {
132
                        const auto padding_size = std::max(1_z, maybe_context->at.start.column);
14✔
133
                        const std::string inner_padding =
14✔
134
                            // -2 to account for the │ and then └
135
                            (loc.start.column < padding_size || loc.start.column - padding_size < 2)
26✔
136
                            ? ""
2✔
137
                            : std::string(std::max(1_z, loc.start.column - padding_size - 1), ' ');
12✔
138

139
                        fmt::print(
28✔
140
                            os,
14✔
141
                            "{: <{}}{}{}{}\n",
14✔
142
                            // padding of spaces
143
                            " ",
144
                            padding_size,
145
                            // indicate where the parent is, with color
146
                            fmt::styled("│", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
14✔
147
                            // yet another padding of spaces between the parent and error column (if need be)
148
                            inner_padding,
149
                            // underline the error in red
150
                            fmt::styled("└─ error", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
14✔
151
                        // new line, some spacing between the error and the parent
152
                        fmt::print(
28✔
153
                            os,
14✔
154
                            "{}{: <{}}{}\n", Printer::GhostLinePrefix,
14✔
155
                            " ",
156
                            padding_size,
157
                            fmt::styled("│", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
14✔
158
                        // new line, now show the "expression started here for the source"
159
                        fmt::print(
28✔
160
                            os,
14✔
161
                            "{}{: <{}}{}\n",
14✔
162
                            Printer::GhostLinePrefix,
163
                            // padding of spaces
164
                            " ",
165
                            padding_size,
166
                            fmt::styled(
28✔
167
                                maybe_context->is_macro_expansion ? "└─ macro expansion started here" : "└─ expression started here",
14✔
168
                                colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
14✔
169
                    }
14✔
170
                }
144✔
171
                else
172
                {
173
                    fmt::print(os, "{}", Printer::GhostLinePrefix);
156✔
174

175
                    // first non-whitespace character of the line
176
                    // +1 for the leading whitespace after `    |` before the code
177
                    const std::size_t col_start = line.find_first_not_of(" \t\v") + 1;
156✔
178

179
                    // highlight the current line but skip any leading whitespace
180
                    fmt::print(
312✔
181
                        os,
156✔
182
                        "{: <{}}{:~<{}}\n",
156✔
183
                        // padding of spaces
184
                        " ",
185
                        col_start,
186
                        // underline the whole line in red
187
                        fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
156✔
188
                        line.size() - col_start);
156✔
189
                }
156✔
190
            }
297✔
191
        }
814✔
192
    }
297✔
193

194
    void helper(std::ostream& os, const std::string& message, const bool colorize,
147✔
195
                const std::string& filename, const internal::FileSpan& at,
196
                const std::optional<CodeErrorContext>& maybe_context = std::nullopt,
197
                const std::optional<std::string>& maybe_file_content = std::nullopt)
198
    {
147✔
199
        std::string uniformised_filename;
147✔
200
        std::ranges::replace_copy(filename, std::back_inserter(uniformised_filename), '\\', '/');
147✔
201
        makeContext(
294✔
202
            ErrorLocation {
147✔
203
                .filename = uniformised_filename,
147✔
204
                .start = at.start,
147✔
205
                .end = at.end,
147✔
206
                .maybe_content = maybe_file_content },
147✔
207
            os, maybe_context, colorize);
147✔
208

209
        for (const auto& text : Utils::splitString(message, '\n'))
294✔
210
            fmt::print(os, "        {}\n", text);
147✔
211
    }
147✔
212

213
    std::string makeContextWithNode(const std::string& message, const internal::Node& node)
×
214
    {
×
215
        std::stringstream ss;
×
216

217
        helper(
×
218
            ss,
×
219
            message,
×
220
            true,
221
            node.filename(),
×
222
            node.position());
×
223

224
        return ss.str();
×
225
    }
×
226

227
    void generateWithCode(const CodeError& e, const std::string& code, std::ostream& os, bool colorize)
1✔
228
    {
1✔
229
#ifdef ARK_BUILD_EXE
230
        if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
1✔
231
            colorize = false;
×
232
#endif
233

234
        helper(
4✔
235
            os,
1✔
236
            e.what(),
1✔
237
            colorize,
1✔
238
            e.context.filename,
1✔
239
            e.context.at,
1✔
240
            e.additional_context,
1✔
241
            code);
1✔
242
    }
1✔
243

244
    void generate(const CodeError& e, std::ostream& os, bool colorize)
146✔
245
    {
146✔
246
#ifdef ARK_BUILD_EXE
247
        if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
146✔
248
            colorize = false;
×
249
#endif
250

251
        helper(
438✔
252
            os,
146✔
253
            e.what(),
146✔
254
            colorize,
146✔
255
            e.context.filename,
146✔
256
            e.context.at,
146✔
257
            e.additional_context);
146✔
258
    }
146✔
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc