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

ArkScript-lang / Ark / 17621217597

10 Sep 2025 05:06PM UTC coverage: 90.868% (+0.02%) from 90.848%
17621217597

push

github

SuperFola
feat(vm, tests): improving the stack overflow error message, and adding proper tests for it

7960 of 8760 relevant lines covered (90.87%)

158460.88 hits per line

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

80.77
/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)
269✔
20
    {
269✔
21
        if (loc.filename != ARK_NO_NAME_FILE)
269✔
22
            fmt::print(os, "In file {}:{}\n", loc.filename, loc.start.line + 1);
269✔
23
    }
269✔
24

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

30
        fmt::print(os, "{}", Printer::GhostLinePrefix);
10✔
31
        fmt::print(
30✔
32
            os,
10✔
33
            "{: <{}}{}\n",
10✔
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
10✔
37
            // underline the parent of the error in red
38
            fmt::styled(
20✔
39
                maybe_context->is_macro_expansion ? "^ macro expansion started here" : "^ expression started here",
10✔
40
                colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
10✔
41
    }
10✔
42

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

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

58
        const bool ctx_same_file = maybe_context && maybe_context->filename == loc.filename;
269✔
59
        const bool ctx_in_window = ctx_same_file && maybe_context && source_printer.coversLine(maybe_context->at.start.line);
269✔
60

61
        if (ctx_same_file && !ctx_in_window)
269✔
62
            source_printer.extendWindow(maybe_context->at.start.line);
3✔
63
        else if (maybe_context && !ctx_same_file && !maybe_context->filename.empty())
266✔
64
        {
65
            // show the location of the parent of our error first
66
            fmt::print(os, "Error originated from file {}:{}\n", maybe_context->filename, maybe_context->at.start.line + 1);
×
67

68
            std::optional<decltype(internal::FilePos::line)> maybe_end_line = std::nullopt;
×
69
            if (maybe_context->at.end)
×
70
                maybe_end_line = maybe_context->at.end->line;
×
71
            Printer printer(maybe_context->filename, maybe_context->at.start.line, maybe_end_line, colorize);
×
72

73
            while (printer.hasContent())
×
74
            {
75
                printer.printLine(os);
×
76
                if (printer.isTargetLine())
×
77
                    hintWithContext(os, maybe_context, colorize);
×
78
            }
79

80
            fmt::print(os, "\n");
×
81
        }
×
82

83
        showFileLocation(os, loc);
269✔
84

85
        while (source_printer.hasContent())
972✔
86
        {
87
            const std::size_t i = source_printer.current();
703✔
88
            const std::string& line = source_printer.currentLine();
703✔
89

90
            source_printer.printLine(os);
703✔
91

92
            // if the error context is in the current file, point to it as the parent of our error
93
            if (ctx_same_file && i == maybe_context->at.start.line && i != loc.start.line)
703✔
94
                hintWithContext(os, maybe_context, colorize);
10✔
95

96
            // show where the error occurred
97
            if (source_printer.isTargetLine() && !line.empty())
703✔
98
            {
99
                if (!loc.wholeLineIsError())
272✔
100
                {
101
                    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");
139✔
102

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

107
                    // ignore the last line that is sometimes erroneous in multiline contexts
108
                    if (i == loc.end->line && loc.end->line != loc.start.line && col_start >= col_end)
139✔
109
                        continue;
3✔
110
                    fmt::print(os, "{}", Printer::GhostLinePrefix);
136✔
111

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

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

170
                    // first non-whitespace character of the line
171
                    // +1 for the leading whitespace after `    |` before the code
172
                    const std::size_t col_start = line.find_first_not_of(" \t\v") + 1;
133✔
173

174
                    // highlight the current line but skip any leading whitespace
175
                    fmt::print(
266✔
176
                        os,
133✔
177
                        "{: <{}}{:~<{}}\n",
133✔
178
                        // padding of spaces
179
                        " ",
180
                        col_start,
181
                        // underline the whole line in red
182
                        fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
133✔
183
                        line.size() - col_start);
133✔
184
                }
133✔
185
            }
269✔
186
        }
703✔
187
    }
269✔
188

189
    void helper(std::ostream& os, const std::string& message, const bool colorize,
142✔
190
                const std::string& filename, const internal::FileSpan& at,
191
                const std::optional<CodeErrorContext>& maybe_context = std::nullopt)
192
    {
142✔
193
        makeContext(
284✔
194
            ErrorLocation {
142✔
195
                .filename = filename,
142✔
196
                .start = at.start,
142✔
197
                .end = at.end },
142✔
198
            os, maybe_context, colorize);
142✔
199

200
        for (const auto& text : Utils::splitString(message, '\n'))
284✔
201
            fmt::print(os, "        {}\n", text);
142✔
202
    }
142✔
203

204
    std::string makeContextWithNode(const std::string& message, const internal::Node& node)
×
205
    {
×
206
        std::stringstream ss;
×
207

208
        helper(
×
209
            ss,
×
210
            message,
×
211
            true,
212
            node.filename(),
×
213
            node.position());
×
214

215
        return ss.str();
×
216
    }
×
217

218
    void generate(const CodeError& e, std::ostream& os, bool colorize)
142✔
219
    {
142✔
220
#ifdef ARK_BUILD_EXE
221
        if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
142✔
222
            colorize = false;
×
223
#endif
224

225
        helper(
426✔
226
            os,
142✔
227
            e.what(),
142✔
228
            colorize,
142✔
229
            e.context.filename,
142✔
230
            e.context.at,
142✔
231
            e.additional_context);
142✔
232
    }
142✔
233
}
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