better error messages are awesome!
[pspp] / src / libpspp / message.c
index e3d301efd7ae8ae76a09a83360e1b09ed8f5c671..ab50f2b0225fa570c7b7b08aad6690bfc0896398 100644 (file)
@@ -27,6 +27,8 @@
 #include <unistd.h>
 
 #include "libpspp/cast.h"
+#include "libpspp/intern.h"
+#include "language/lexer/lexer.h"
 #include "libpspp/str.h"
 #include "libpspp/version.h"
 #include "data/settings.h"
@@ -120,7 +122,8 @@ msg_set_handler (void (*handler) (const struct msg *, void *aux), void *aux)
 void
 msg_location_uninit (struct msg_location *loc)
 {
-  free (loc->file_name);
+  lex_source_unref (loc->src);
+  intern_unref (loc->file_name);
 }
 
 void
@@ -133,6 +136,41 @@ msg_location_destroy (struct msg_location *loc)
     }
 }
 
+static int
+msg_point_compare_3way (const struct msg_point *a, const struct msg_point *b)
+{
+  return (!a->line ? 1
+          : !b->line ? -1
+          : a->line > b->line ? 1
+          : a->line < b->line ? -1
+          : !a->column ? 1
+          : !b->column ? -1
+          : a->column > b->column ? 1
+          : a->column < b->column ? -1
+          : 0);
+}
+
+void
+msg_location_merge (struct msg_location **dstp, const struct msg_location *src)
+{
+  struct msg_location *dst = *dstp;
+  if (!dst)
+    {
+      *dstp = msg_location_dup (src);
+      return;
+    }
+
+  if (dst->file_name != src->file_name)
+    {
+      /* Failure. */
+      return;
+    }
+  if (msg_point_compare_3way (&dst->p[0], &src->p[0]) > 0)
+    dst->p[0] = src->p[0];
+  if (msg_point_compare_3way (&dst->p[1], &src->p[1]) < 0)
+    dst->p[1] = src->p[1];
+}
+
 struct msg_location *
 msg_location_dup (const struct msg_location *src)
 {
@@ -140,13 +178,11 @@ msg_location_dup (const struct msg_location *src)
     return NULL;
 
   struct msg_location *dst = xmalloc (sizeof *dst);
-  *dst = (struct msg_location) {
-    .file_name = xstrdup_if_nonnull (src->file_name),
-    .first_line = src->first_line,
-    .last_line = src->last_line,
-    .first_column = src->first_column,
-    .last_column = src->last_column,
-  };
+  *dst = *src;
+  if (src->file_name)
+    dst->file_name = intern_ref (src->file_name);
+  if (src->src)
+    lex_source_ref (dst->src);
   return dst;
 }
 
@@ -154,8 +190,8 @@ bool
 msg_location_is_empty (const struct msg_location *loc)
 {
   return !loc || (!loc->file_name
-                  && loc->first_line <= 0
-                  && loc->first_column <= 0);
+                  && loc->p[0].line <= 0
+                  && loc->p[0].column <= 0);
 }
 
 void
@@ -167,10 +203,10 @@ msg_location_format (const struct msg_location *loc, struct string *s)
   if (loc->file_name)
     ds_put_cstr (s, loc->file_name);
 
-  int l1 = loc->first_line;
-  int l2 = MAX (loc->first_line, loc->last_line - 1);
-  int c1 = loc->first_column;
-  int c2 = MAX (loc->first_column, loc->last_column - 1);
+  int l1 = loc->p[0].line;
+  int l2 = MAX (l1, loc->p[1].line);
+  int c1 = loc->p[0].column;
+  int c2 = MAX (c1, loc->p[1].column);
 
   if (l1 > 0)
     {
@@ -327,6 +363,22 @@ msg_to_string (const struct msg *m)
 
   ds_put_cstr (&s, m->text);
 
+  const struct msg_location *loc = m->location;
+  if (loc->src && loc->p[0].line)
+    {
+      struct substring line = lex_source_get_line (loc->src, m->location->p[0].line);
+      ds_put_format (&s, "\n%5d | %.*s", loc->p[0].line, (int) line.length, line.string);
+      if (loc->p[0].column && loc->p[1].column >= loc->p[0].column)
+        {
+          ds_put_cstr (&s, "      | ");
+          ds_put_byte_multiple (&s, ' ', loc->p[0].column - 1);
+          int n = loc->p[1].column - loc->p[0].column + 1;
+          ds_put_byte (&s, '^');
+          if (n > 1)
+            ds_put_byte_multiple (&s, '~', n - 1);
+        }
+    }
+
   return ds_cstr (&s);
 }
 \f