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

ArkScript-lang / Ark / 17095454925

20 Aug 2025 10:15AM UTC coverage: 87.175% (-0.3%) from 87.426%
17095454925

Pull #571

github

web-flow
Merge 582ae8a8d into 120045f7e
Pull Request #571: Feat/better parser position tracking

594 of 697 new or added lines in 17 files covered. (85.22%)

5 existing lines in 2 files now uncovered.

7545 of 8655 relevant lines covered (87.18%)

129898.3 hits per line

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

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

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

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

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

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

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

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

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

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

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

83
        showFileLocation(os, loc);
238✔
84

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

90
            source_printer.printLine(os);
612✔
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)
612✔
94
                hintWithContext(os, maybe_context, colorize);
9✔
95

96
            // show where the error occurred
97
            if (source_printer.isTargetLine() && !line.empty())
612✔
98
            {
99
                fmt::print(os, "{}", Printer::GhostLinePrefix);
240✔
100

101
                if (!loc.wholeLineIsError())
240✔
102
                {
103
                    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");
116✔
104

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

109
                    // show the error where it's at, using the normal process, if there is no context OR
110
                    // if the context line is different from the error line
111
                    if (!maybe_context || maybe_context->at.start.line != loc.start.line)
116✔
112
                        fmt::print(
318✔
113
                            os,
106✔
114
                            "{: <{}}{:~<{}}\n",
106✔
115
                            // padding of spaces
116
                            " ",
117
                            std::max(1_z, std::min(col_start, col_end)),  // fixing padding when the error is on the first character
106✔
118
                            // underline the error in red
119
                            fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
106✔
120
                            col_start < col_end ? col_end - col_start : 1);
106✔
121
                    // cppcheck-suppress knownConditionTrueFalse ; suppressing error so that the condition is explicit to the reader
122
                    else if (maybe_context && maybe_context->at.start.line == loc.start.line && i == loc.start.line)
10✔
123
                    {
124
                        const auto padding_size = std::max(1_z, maybe_context->at.start.column);
10✔
125

126
                        fmt::print(
20✔
127
                            os,
10✔
128
                            "{: <{}}{}{}{}\n",
10✔
129
                            // padding of spaces
130
                            " ",
131
                            padding_size,
132
                            // indicate where the parent is, with color
133
                            fmt::styled("│", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
10✔
134
                            // yet another padding of spaces between the parent and error column (if need be)
135
                            // -2 to account for the │ and then └
136
                            (loc.start.column - padding_size < 2)
19✔
137
                                ? ""
1✔
138
                                : std::string(loc.start.column - padding_size - 1, ' '),
9✔
139
                            // underline the error in red
140
                            fmt::styled("└─ error", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
10✔
141
                        // new line, some spacing between the error and the parent
142
                        fmt::print(
20✔
143
                            os,
10✔
144
                            "{}{: <{}}{}\n", Printer::GhostLinePrefix,
10✔
145
                            " ",
146
                            padding_size,
147
                            fmt::styled("│", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
10✔
148
                        // new line, now show the "expression started here for the source"
149
                        fmt::print(
20✔
150
                            os,
10✔
151
                            "{}{: <{}}{}\n",
10✔
152
                            Printer::GhostLinePrefix,
153
                            // padding of spaces
154
                            " ",
155
                            padding_size,
156
                            fmt::styled(
20✔
157
                                maybe_context->is_macro_expansion ? "└─ macro expansion started here" : "└─ expression started here",
10✔
158
                                colorize ? fmt::fg(fmt::color::red) : fmt::text_style()));
10✔
159
                    }
10✔
160
                }
116✔
161
                else
162
                {
163
                    // first non-whitespace character of the line
164
                    // +1 for the leading whitespace after `    |` before the code
165
                    const std::size_t col_start = line.find_first_not_of(" \t\v") + 1;
124✔
166

167
                    // highlight the current line but skip any leading whitespace
168
                    fmt::print(
248✔
169
                        os,
124✔
170
                        "{: <{}}{:~<{}}\n",
124✔
171
                        // padding of spaces
172
                        " ",
173
                        col_start,
174
                        // underline the whole line in red
175
                        fmt::styled("^", colorize ? fmt::fg(fmt::color::red) : fmt::text_style()),
124✔
176
                        line.size() - col_start);
124✔
177
                }
124✔
178
            }
240✔
179
        }
612✔
180
    }
238✔
181

182
    void helper(std::ostream& os, const std::string& message, const bool colorize,
120✔
183
                const std::string& filename, const internal::FileSpan& at,
184
                const std::optional<CodeErrorContext>& maybe_context = std::nullopt)
185
    {
120✔
186
        makeContext(
240✔
187
            ErrorLocation {
120✔
188
                .filename = filename,
120✔
189
                .start = at.start,
120✔
190
                .end = at.end },
120✔
191
            os, maybe_context, colorize);
120✔
192

193
        for (const auto& text : Utils::splitString(message, '\n'))
240✔
194
            fmt::print(os, "        {}\n", text);
120✔
195
    }
120✔
196

NEW
197
    std::string makeContextWithNode(const std::string& message, const internal::Node& node)
×
NEW
198
    {
×
NEW
199
        std::stringstream ss;
×
200

NEW
201
        helper(
×
NEW
202
            ss,
×
NEW
203
            message,
×
204
            true,
NEW
205
            node.filename(),
×
NEW
206
            node.position());
×
207

NEW
208
        return ss.str();
×
NEW
209
    }
×
210

211
    void generate(const CodeError& e, std::ostream& os, bool colorize)
120✔
212
    {
120✔
213
#ifdef ARK_BUILD_EXE
214
        if (const char* nocolor = std::getenv("NOCOLOR"); nocolor != nullptr)
120✔
NEW
215
            colorize = false;
×
216
#endif
217

218
        helper(
360✔
219
            os,
120✔
220
            e.what(),
120✔
221
            colorize,
120✔
222
            e.context.filename,
120✔
223
            e.context.at,
120✔
224
            e.additional_context);
120✔
225
    }
120✔
226
}
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