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

ArkScript-lang / Ark / 20146161189

11 Dec 2025 08:16PM UTC coverage: 90.726% (+0.002%) from 90.724%
20146161189

Pull #616

github

web-flow
Merge e792b2c18 into 63d746f20
Pull Request #616: Trying to understand why Windows CI tests are broken

6 of 6 new or added lines in 1 file covered. (100.0%)

6 existing lines in 1 file now uncovered.

8100 of 8928 relevant lines covered (90.73%)

180062.24 hits per line

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

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

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

30
        fmt::print(os, "{}", Printer::GhostLinePrefix);
12✔
31
        fmt::print(
36✔
32
            os,
12✔
33
            "{: <{}}{}\n",
12✔
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
12✔
37
            // underline the parent of the error in red
38
            fmt::styled(
24✔
39
                start
23✔
40
                    ? (maybe_context->is_macro_expansion ? "┌─ macro expansion started here" : "┌─ expression started here")
11✔
41
                    : "└─ ... and ended here",
42
                colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
12✔
43
    }
12✔
44

45
    void makeContext(
273✔
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
    {
273✔
51
        assert(!(maybe_context && loc.wholeLineIsError()) && "Can not create error context when a context is given AND the whole line has to be underlined");
273✔
52

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

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

63
        if (ctx_same_file && !ctx_in_window)
273✔
64
            source_printer.extendWindow(maybe_context->at.start.line);
3✔
65
        else if (maybe_context && !ctx_same_file && !maybe_context->filename.empty())
270✔
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);
273✔
90

91
        while (source_printer.hasContent())
986✔
92
        {
93
            const std::size_t i = source_printer.current();
713✔
94
            const std::string& line = source_printer.currentLine();
713✔
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)
713✔
98
                hintWithContext(os, maybe_context, colorize, /* start= */ true);
10✔
99
            source_printer.printLine(os);
713✔
100

101
            // show where the error occurred
102
            if (source_printer.isTargetLine() && !line.empty())
713✔
103
            {
104
                if (!loc.wholeLineIsError())
276✔
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");
141✔
107

108
                    const std::size_t col_start = (i == loc.start.line) ? loc.start.column : line_first_char + 1;
141✔
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();
141✔
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)
141✔
114
                        continue;
3✔
115
                    fmt::print(os, "{}", Printer::GhostLinePrefix);
138✔
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)
138✔
120
                        fmt::print(
372✔
121
                            os,
124✔
122
                            "{: <{}}{:~<{}}\n",
124✔
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
124✔
126
                            // underline the error in red
127
                            fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
124✔
128
                            col_start < col_end ? col_end - col_start : 1);
124✔
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
                }
141✔
171
                else
172
                {
173
                    fmt::print(os, "{}", Printer::GhostLinePrefix);
135✔
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;
135✔
178

179
                    // highlight the current line but skip any leading whitespace
180
                    fmt::print(
270✔
181
                        os,
135✔
182
                        "{: <{}}{:~<{}}\n",
135✔
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()),
135✔
188
                        line.size() - col_start);
135✔
189
                }
135✔
190
            }
273✔
191
        }
713✔
192
    }
273✔
193

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

207
        for (const auto& text : Utils::splitString(message, '\n'))
288✔
208
            fmt::print(os, "        {}\n", text);
144✔
209
    }
144✔
210

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

215
        helper(
×
UNCOV
216
            ss,
×
217
            message,
×
218
            true,
219
            node.filename(),
×
UNCOV
220
            node.position());
×
221

222
        return ss.str();
×
UNCOV
223
    }
×
224

225
    void generate(const CodeError& e, std::ostream& os, bool colorize)
144✔
226
    {
144✔
227
#ifdef ARK_BUILD_EXE
228
        if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
144✔
UNCOV
229
            colorize = false;
×
230
#endif
231

232
        helper(
432✔
233
            os,
144✔
234
            e.what(),
144✔
235
            colorize,
144✔
236
            e.context.filename,
144✔
237
            e.context.at,
144✔
238
            e.additional_context);
144✔
239
    }
144✔
240
}
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