mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 15:06:14 +01:00
GVariant: add way to print context of parse errors
This was a feature intended from the very beginning that somehow never got written. It's a way to replace these sort of error messages out of the GVariant parser: 1-2,10-15:unable to find a common type with something in the style of the Vala compiler: unable to find a common type: [1, 2, 3, 'str'] ^ ^^^^^ https://bugzilla.gnome.org/show_bug.cgi?id=715028
This commit is contained in:
parent
3f41e49285
commit
56fb675d86
@ -3229,6 +3229,7 @@ G_VARIANT_PARSE_ERROR
|
||||
g_variant_parse
|
||||
g_variant_new_parsed_va
|
||||
g_variant_new_parsed
|
||||
g_variant_parse_error_print_context
|
||||
|
||||
<SUBSECTION Private>
|
||||
g_variant_parse_error_quark
|
||||
|
@ -2558,3 +2558,225 @@ g_variant_builder_add_parsed (GVariantBuilder *builder,
|
||||
g_variant_builder_add_value (builder, g_variant_new_parsed_va (format, &ap));
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_num (const gchar *num,
|
||||
const gchar *limit,
|
||||
gint *result)
|
||||
{
|
||||
gchar *endptr;
|
||||
gint64 bignum;
|
||||
|
||||
bignum = g_ascii_strtoll (num, &endptr, 10);
|
||||
|
||||
if (endptr != limit)
|
||||
return FALSE;
|
||||
|
||||
if (bignum < 0 || bignum > G_MAXINT)
|
||||
return FALSE;
|
||||
|
||||
*result = bignum;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_last_line (GString *err,
|
||||
const gchar *str)
|
||||
{
|
||||
const gchar *last_nl;
|
||||
gchar *chomped;
|
||||
gint i;
|
||||
|
||||
/* This is an error at the end of input. If we have a file
|
||||
* with newlines, that's probably the empty string after the
|
||||
* last newline, which is not the most useful thing to show.
|
||||
*
|
||||
* Instead, show the last line of non-whitespace that we have
|
||||
* and put the pointer at the end of it.
|
||||
*/
|
||||
chomped = g_strchomp (g_strdup (str));
|
||||
last_nl = strrchr (chomped, '\n');
|
||||
if (last_nl == NULL)
|
||||
last_nl = chomped;
|
||||
else
|
||||
last_nl++;
|
||||
|
||||
/* Print the last line like so:
|
||||
*
|
||||
* [1, 2, 3,
|
||||
* ^
|
||||
*/
|
||||
g_string_append (err, " ");
|
||||
if (last_nl[0])
|
||||
g_string_append (err, last_nl);
|
||||
else
|
||||
g_string_append (err, "(empty input)");
|
||||
g_string_append (err, "\n ");
|
||||
for (i = 0; last_nl[i]; i++)
|
||||
g_string_append_c (err, ' ');
|
||||
g_string_append (err, "^\n");
|
||||
g_free (chomped);
|
||||
}
|
||||
|
||||
static void
|
||||
add_lines_from_range (GString *err,
|
||||
const gchar *str,
|
||||
const gchar *start1,
|
||||
const gchar *end1,
|
||||
const gchar *start2,
|
||||
const gchar *end2)
|
||||
{
|
||||
while (str < end1 || str < end2)
|
||||
{
|
||||
const gchar *nl;
|
||||
|
||||
nl = str + strcspn (str, "\n");
|
||||
|
||||
if ((start1 < nl && str < end1) || (start2 < nl && str < end2))
|
||||
{
|
||||
const gchar *s;
|
||||
|
||||
/* We're going to print this line */
|
||||
g_string_append (err, " ");
|
||||
g_string_append_len (err, str, nl - str);
|
||||
g_string_append (err, "\n ");
|
||||
|
||||
/* And add underlines... */
|
||||
for (s = str; s < nl; s++)
|
||||
{
|
||||
if ((start1 <= s && s < end1) || (start2 <= s && s < end2))
|
||||
g_string_append_c (err, '^');
|
||||
else
|
||||
g_string_append_c (err, ' ');
|
||||
}
|
||||
g_string_append_c (err, '\n');
|
||||
}
|
||||
|
||||
if (!*nl)
|
||||
break;
|
||||
|
||||
str = nl + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* g_variant_parse_error_print_context:
|
||||
* @error: a #GError from the #GVariantParseError domain
|
||||
* @source_str: the string that was given to the parser
|
||||
*
|
||||
* Pretty-prints a message showing the context of a #GVariant parse
|
||||
* error within the string for which parsing was attempted.
|
||||
*
|
||||
* The resulting string is suitable for output to the console or other
|
||||
* monospace media where newlines are treated in the usual way.
|
||||
*
|
||||
* The message will typically look something like one of the following:
|
||||
*
|
||||
* |[
|
||||
* unterminated string constant:
|
||||
* (1, 2, 3, 'abc
|
||||
* ^^^^
|
||||
* ]|
|
||||
*
|
||||
* or
|
||||
*
|
||||
* |[
|
||||
* unable to find a common type:
|
||||
* [1, 2, 3, 'str']
|
||||
* ^ ^^^^^
|
||||
* ]|
|
||||
*
|
||||
* The format of the message may change in a future version.
|
||||
*
|
||||
* @error must have come from a failed attempt to g_variant_parse() and
|
||||
* @source_str must be exactly the same string that caused the error.
|
||||
* If @source_str was not nul-terminated when you passed it to
|
||||
* g_variant_parse() then you must add nul termination before using this
|
||||
* function.
|
||||
*
|
||||
* Returns: (transfer full): the printed message
|
||||
*
|
||||
* Since: 2.40
|
||||
**/
|
||||
gchar *
|
||||
g_variant_parse_error_print_context (GError *error,
|
||||
const gchar *source_str)
|
||||
{
|
||||
const gchar *colon, *dash, *comma;
|
||||
gboolean success = FALSE;
|
||||
GString *err;
|
||||
|
||||
g_return_val_if_fail (error->domain == G_VARIANT_PARSE_ERROR, FALSE);
|
||||
|
||||
/* We can only have a limited number of possible types of ranges
|
||||
* emitted from the parser:
|
||||
*
|
||||
* - a: -- usually errors from the tokeniser (eof, invalid char, etc.)
|
||||
* - a-b: -- usually errors from handling one single token
|
||||
* - a-b,c-d: -- errors involving two tokens (ie: type inferencing)
|
||||
*
|
||||
* We never see, for example "a,c".
|
||||
*/
|
||||
|
||||
colon = strchr (error->message, ':');
|
||||
dash = strchr (error->message, '-');
|
||||
comma = strchr (error->message, ',');
|
||||
|
||||
if (!colon)
|
||||
return NULL;
|
||||
|
||||
err = g_string_new (colon + 1);
|
||||
g_string_append (err, ":\n");
|
||||
|
||||
if (dash == NULL || colon < dash)
|
||||
{
|
||||
gint point;
|
||||
|
||||
/* we have a single point */
|
||||
if (!parse_num (error->message, colon, &point))
|
||||
goto out;
|
||||
|
||||
if (point >= strlen (source_str))
|
||||
/* the error is at the end if the input */
|
||||
add_last_line (err, source_str);
|
||||
else
|
||||
/* otherwise just treat it as a error at a thin range */
|
||||
add_lines_from_range (err, source_str, source_str + point, source_str + point + 1, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We have one or two ranges... */
|
||||
if (comma && comma < colon)
|
||||
{
|
||||
gint start1, end1, start2, end2;
|
||||
const gchar *dash2;
|
||||
|
||||
/* Two ranges */
|
||||
dash2 = strchr (comma, '-');
|
||||
|
||||
if (!parse_num (error->message, dash, &start1) || !parse_num (dash + 1, comma, &end1) ||
|
||||
!parse_num (comma + 1, dash2, &start2) || !parse_num (dash2 + 1, colon, &end2))
|
||||
goto out;
|
||||
|
||||
add_lines_from_range (err, source_str,
|
||||
source_str + start1, source_str + end1,
|
||||
source_str + start2, source_str + end2);
|
||||
}
|
||||
else
|
||||
{
|
||||
gint start, end;
|
||||
|
||||
/* One range */
|
||||
if (!parse_num (error->message, dash, &start) || !parse_num (dash + 1, colon, &end))
|
||||
goto out;
|
||||
|
||||
add_lines_from_range (err, source_str, source_str + start, source_str + end, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
success = TRUE;
|
||||
|
||||
out:
|
||||
return g_string_free (err, !success);
|
||||
}
|
||||
|
@ -395,6 +395,10 @@ GLIB_AVAILABLE_IN_ALL
|
||||
GVariant * g_variant_new_parsed_va (const gchar *format,
|
||||
va_list *app);
|
||||
|
||||
GLIB_AVAILABLE_IN_2_40
|
||||
gchar * g_variant_parse_error_print_context (GError *error,
|
||||
const gchar *source_str);
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
gint g_variant_compare (gconstpointer one,
|
||||
gconstpointer two);
|
||||
|
Loading…
Reference in New Issue
Block a user