3#include "prism/internal/allocator.h"
4#include "prism/internal/buffer.h"
5#include "prism/internal/diagnostic.h"
6#include "prism/internal/encoding.h"
7#include "prism/internal/line_offset_list.h"
8#include "prism/internal/parser.h"
25 const size_t start_line = (size_t) pm_line_offset_list_line_column(line_offsets, location->
start, 1).
line;
26 const size_t end_line = (size_t) pm_line_offset_list_line_column(line_offsets, location->
start + location->
length, 1).
line;
28 const uint8_t *cursor = parser->start + line_offsets->
offsets[start_line - 1];
29 const uint8_t *end = (end_line == line_offsets->
size) ? parser->end : (parser->start + line_offsets->offsets[end_line]);
31 while (cursor < end) {
32 size_t width = pm_encoding_utf_8_char_width(cursor, end - cursor);
33 if (width == 0)
return false;
44 uint32_t column_start;
50pm_rich_error_compare(
const void *a,
const void *b) {
54 if (left->line != right->line) {
55 return (left->line < right->line) ? -1 : 1;
58 if (left->column_start != right->column_start) {
59 return (left->column_start < right->column_start) ? -1 : 1;
62 if (left->idx != right->idx) {
63 return (left->idx < right->idx) ? 1 : -1;
69#define COLOR_BOLD "\033[1m"
70#define COLOR_GRAY "\033[2m"
71#define COLOR_RED "\033[1;31m"
72#define COLOR_RESET "\033[m"
76 const char *number_prefix;
77 const char *blank_prefix;
79 size_t blank_prefix_length;
80 size_t divider_length;
92 if (first_line < 0) first_line = (-first_line) * 10;
93 if (last_line < 0) last_line = (-last_line) * 10;
94 int32_t max_line = first_line > last_line ? first_line : last_line;
99 .number_prefix = COLOR_GRAY
"%1" PRIi32
" | " COLOR_RESET,
100 .blank_prefix = COLOR_GRAY
" | " COLOR_RESET,
101 .divider = COLOR_GRAY
" ~~~~~" COLOR_RESET
"\n"
105 .number_prefix =
"%1" PRIi32
" | ",
106 .blank_prefix =
" | ",
107 .divider =
" ~~~~~\n"
110 }
else if (max_line < 100) {
113 .number_prefix = COLOR_GRAY
"%2" PRIi32
" | " COLOR_RESET,
114 .blank_prefix = COLOR_GRAY
" | " COLOR_RESET,
115 .divider = COLOR_GRAY
" ~~~~~~" COLOR_RESET
"\n"
119 .number_prefix =
"%2" PRIi32
" | ",
120 .blank_prefix =
" | ",
121 .divider =
" ~~~~~~\n"
124 }
else if (max_line < 1000) {
127 .number_prefix = COLOR_GRAY
"%3" PRIi32
" | " COLOR_RESET,
128 .blank_prefix = COLOR_GRAY
" | " COLOR_RESET,
129 .divider = COLOR_GRAY
" ~~~~~~~" COLOR_RESET
"\n"
133 .number_prefix =
"%3" PRIi32
" | ",
134 .blank_prefix =
" | ",
135 .divider =
" ~~~~~~~\n"
138 }
else if (max_line < 10000) {
141 .number_prefix = COLOR_GRAY
"%4" PRIi32
" | " COLOR_RESET,
142 .blank_prefix = COLOR_GRAY
" | " COLOR_RESET,
143 .divider = COLOR_GRAY
" ~~~~~~~~" COLOR_RESET
"\n"
147 .number_prefix =
"%4" PRIi32
" | ",
148 .blank_prefix =
" | ",
149 .divider =
" ~~~~~~~~\n"
155 .number_prefix = COLOR_GRAY
"%5" PRIi32
" | " COLOR_RESET,
156 .blank_prefix = COLOR_GRAY
" | " COLOR_RESET,
157 .divider = COLOR_GRAY
" ~~~~~~~~" COLOR_RESET
"\n"
161 .number_prefix =
"%5" PRIi32
" | ",
162 .blank_prefix =
" | ",
163 .divider =
" ~~~~~~~~\n"
168 format->blank_prefix_length = strlen(format->blank_prefix);
169 format->divider_length = strlen(format->divider);
173pm_error_format_line(
const pm_parser_t *parser,
pm_buffer_t *buffer,
const char *number_prefix, int32_t line, uint32_t column_start, uint32_t column_end) {
174 int32_t line_delta = line - parser->start_line;
175 assert(line_delta >= 0);
177 size_t index = (size_t) line_delta;
178 assert(index < parser->line_offsets.
size);
180 const uint8_t *start = parser->start + parser->line_offsets.
offsets[index];
183 if (index >= parser->line_offsets.
size - 1) {
186 end = parser->start + parser->line_offsets.
offsets[index + 1];
189 pm_buffer_append_format(buffer, number_prefix, line);
195 bool truncate_end =
false;
196 if ((column_end != 0) && ((end - start) - ((ptrdiff_t) column_end) >= TRUNCATE)) {
197 const uint8_t *end_candidate = start + column_end + TRUNCATE;
199 for (
const uint8_t *cursor = start; cursor < end_candidate;) {
200 size_t char_width = pm_parser_encoding_char_width(parser, cursor, parser->end - cursor);
204 if (char_width == 0)
break;
208 if (cursor + char_width > end_candidate) {
209 end_candidate = cursor;
213 cursor += char_width;
221 if (column_start >= TRUNCATE) {
222 pm_buffer_append_string(buffer,
"... ", 4);
223 start += column_start;
226 pm_buffer_append_string(buffer, (
const char *) start, (
size_t) (end - start));
229 pm_buffer_append_string(buffer,
" ...\n", 5);
230 }
else if (end == parser->end && end > parser->start && end[-1] !=
'\n') {
231 pm_buffer_append_byte(buffer,
'\n');
238 pm_error_line_format_init(&format, format_type, rich_errors[0].line, rich_errors[nerrors - 1].line);
245 int32_t last_line = parser->start_line - 1;
246 uint32_t last_column_start = 0;
248 for (
size_t idx = 0; idx < nerrors; idx++) {
253 if (rich_error->line - last_line > 1) {
254 if (rich_error->line - last_line > 2) {
255 if ((idx != 0) && (rich_error->line - last_line > 3)) {
256 pm_buffer_append_string(buffer, format.divider, format.divider_length);
259 pm_buffer_append_string(buffer,
" ", 2);
260 pm_error_format_line(parser, buffer, format.number_prefix, rich_error->line - 2, 0, 0);
263 pm_buffer_append_string(buffer,
" ", 2);
264 pm_error_format_line(parser, buffer, format.number_prefix, rich_error->line - 1, 0, 0);
269 if ((idx == 0) || (rich_error->line != last_line)) {
270 switch (format_type) {
272 pm_buffer_append_string(buffer,
"> ", 2);
275 pm_buffer_append_format(buffer,
"%s", COLOR_BOLD
"> " COLOR_RESET);
278 pm_buffer_append_format(buffer,
"%s", COLOR_RED
"> " COLOR_RESET);
282 last_column_start = rich_error->column_start;
285 uint32_t column_end = rich_error->column_end;
286 for (
size_t next_idx = idx + 1; next_idx < nerrors; next_idx++) {
287 if (rich_errors[next_idx].line != rich_error->line)
break;
288 if (rich_errors[next_idx].column_end > column_end) column_end = rich_errors[next_idx].column_end;
291 pm_error_format_line(parser, buffer, format.number_prefix, rich_error->line, rich_error->column_start, column_end);
294 const uint8_t *start = parser->start + parser->line_offsets.
offsets[rich_error->line - parser->start_line];
295 if (start == parser->end) pm_buffer_append_byte(buffer,
'\n');
306 pm_buffer_append_string(buffer,
" ", 2);
307 pm_buffer_append_string(buffer, format.blank_prefix, format.blank_prefix_length);
310 if (last_column_start >= TRUNCATE) {
311 pm_buffer_append_string(buffer,
" ", 4);
312 column = last_column_start;
315 while (column < rich_error->column_start) {
316 pm_buffer_append_byte(buffer,
' ');
318 size_t char_width = pm_parser_encoding_char_width(parser, start + column, parser->end - (start + column));
319 column += (char_width == 0 ? 1 : char_width);
322 switch (format_type) {
324 pm_buffer_append_byte(buffer,
'^');
327 pm_buffer_append_format(buffer,
"%s^", COLOR_BOLD);
330 pm_buffer_append_format(buffer,
"%s^", COLOR_RED);
334 size_t char_width = pm_parser_encoding_char_width(parser, start + column, parser->end - (start + column));
335 column += (char_width == 0 ? 1 : char_width);
337 while (column < rich_error->column_end) {
338 pm_buffer_append_byte(buffer,
'~');
340 size_t char_width = pm_parser_encoding_char_width(parser, start + column, parser->end - (start + column));
341 column += (char_width == 0 ? 1 : char_width);
345 pm_buffer_append_format(buffer,
"%s", COLOR_RESET);
348 if (inline_messages) {
349 const char *message = pm_diagnostic_message(rich_error->diagnostic);
351 pm_buffer_append_byte(buffer,
' ');
352 pm_buffer_append_string(buffer, message, strlen(message));
355 pm_buffer_append_byte(buffer,
'\n');
359 last_line = rich_error->line;
362 if (idx == nerrors - 1) {
363 next_line = ((int32_t) parser->line_offsets.
size) + parser->start_line;
368 if ((parser->start + parser->line_offsets.
offsets[parser->line_offsets.
size - 1]) == parser->end) {
372 next_line = rich_errors[idx + 1].line;
375 if (next_line - last_line > 1) {
376 pm_buffer_append_string(buffer,
" ", 2);
377 pm_error_format_line(parser, buffer, format.number_prefix, ++last_line, 0, 0);
380 if (next_line - last_line > 1) {
381 pm_buffer_append_string(buffer,
" ", 2);
382 pm_error_format_line(parser, buffer, format.number_prefix, ++last_line, 0, 0);
390 pm_line_column_t start_line_column = pm_line_offset_list_line_column(line_offsets, location->
start, parser->start_line);
391 pm_line_column_t end_line_column = pm_line_offset_list_line_column(line_offsets, location->
start + location->
length, parser->start_line);
394 if (start_line_column.
line == end_line_column.
line) {
395 column_end = end_line_column.
column;
397 column_end = (uint32_t) (line_offsets->
offsets[start_line_column.
line - parser->start_line + 1] - line_offsets->
offsets[start_line_column.
line - parser->start_line] - 1);
401 if (start_line_column.
column == column_end) column_end++;
404 .diagnostic = diagnostic,
405 .line = start_line_column.
line,
406 .column_start = start_line_column.
column,
407 .column_end = column_end
413 size_t nerrors = parser->error_list.size;
416 int filepath_length = (int) pm_string_length(&parser->filepath);
417 const char *filepath = (
const char *) pm_string_source(&parser->filepath);
423 if (rich_errors == NULL) abort();
426 size_t rich_errors_idx = 0;
431 switch (pm_diagnostic_error_level(diagnostic)) {
433 if (is_utf8 && !location_is_utf8(parser, &location)) {
437 pm_rich_error_init(parser, &rich_errors[rich_errors_idx], diagnostic, &location);
438 rich_errors[rich_errors_idx].idx = rich_errors_idx;
446 pm_buffer_append_format(
448 "%.*s:%" PRIi32
": %s",
451 pm_line_offset_list_line(&parser->line_offsets, location.
start, parser->start_line),
452 pm_diagnostic_message(diagnostic)
455 if (location_is_utf8(parser, &location)) {
456 pm_buffer_append_byte(buffer,
'\n');
457 pm_rich_error_init(parser, rich_errors, diagnostic, &location);
458 pm_rich_errors_format(parser, buffer, format_type, 1, rich_errors,
false);
461 xfree_sized(rich_errors, rich_errors_size);
467 const char *message = pm_diagnostic_message(diagnostic);
468 pm_buffer_append_string(buffer, message, strlen(message));
470 xfree_sized(rich_errors, rich_errors_size);
476 assert(rich_errors_idx == nerrors);
480 pm_buffer_append_format(
482 "%.*s:%" PRIi32
": syntax error%s found\n",
486 (nerrors > 1) ?
"s" :
""
494 qsort(rich_errors, nerrors,
sizeof(
pm_rich_error_t), pm_rich_error_compare);
495 pm_rich_errors_format(parser, buffer, format_type, nerrors, rich_errors,
true);
502 for (
size_t idx = 0; idx < nerrors; idx++) {
505 if (idx > 0) pm_buffer_append_byte(buffer,
'\n');
506 pm_buffer_append_format(
508 "%.*s:%" PRIi32
": %s",
512 pm_diagnostic_message(rich_error->diagnostic)
517 xfree_sized(rich_errors, rich_errors_size);
pm_error_level_t
The levels of errors generated during parsing.
@ PM_ERROR_LEVEL_ARGUMENT
For errors that should raise an argument error.
@ PM_ERROR_LEVEL_LOAD
For errors that should raise a load error.
@ PM_ERROR_LEVEL_SYNTAX
For errors that should raise a syntax error.
#define xmalloc
Old name of ruby_xmalloc.
A line and column in a string.
uint32_t column
The column in bytes.
int32_t line
The line number.
A list of offsets of the start of lines in a string.
uint32_t * offsets
The list of offsets.
size_t size
The number of offsets in the list.
This struct represents a slice in the source code, defined by an offset and a length.
uint32_t start
The offset of the location from the start of the source.
uint32_t length
The length of the location.