Improve error messages for missing subcommands.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 11 Sep 2022 01:11:18 +0000 (18:11 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 11 Sep 2022 01:17:04 +0000 (18:17 -0700)
13 files changed:
src/language/control/loop.c
src/language/data-io/combine-files.c
src/language/data-io/file-handle.c
src/language/data-io/get.c
src/language/data-io/save-translate.c
src/language/dictionary/sys-file-info.c
src/language/lexer/lexer.c
src/language/lexer/lexer.h
src/language/stats/matrix.c
src/language/stats/t-test-parser.c
tests/language/control/loop.at
tests/language/stats/matrix.at
tests/language/stats/t-test.at

index 13af8225467a8c548256cd6b540779815a287f10..5c8cb14f09292d84100cce4ed90cbdabe92fbfbb 100644 (file)
@@ -223,7 +223,7 @@ parse_index_clause (struct dataset *ds, struct lexer *lexer,
     }
   if (loop->last_expr == NULL)
     {
-      lex_sbc_missing ("TO");
+      lex_sbc_missing (lexer, "TO");
       return false;
     }
 
index 9f39cad7754f7af50949c0e4cfdc1f479936a5a8..d0e08b5fd0f8f16ff0bebe7335281a1b470ad093 100644 (file)
@@ -380,7 +380,7 @@ combine_files (enum comb_command_type command,
     {
       if (command == COMB_UPDATE)
         {
-          lex_sbc_missing ("BY");
+          lex_sbc_missing (lexer, "BY");
           goto error;
         }
       if (n_tables)
index 01c56de1960d872cc7235ee785acfbf1253a3c8a..01d707c0b96b3e7bea6c6c5cbf2f89b5c013540d 100644 (file)
@@ -202,7 +202,7 @@ cmd_file_handle (struct lexer *lexer, struct dataset *ds UNUSED)
   struct fh_properties properties = *fh_default_properties ();
   if (file_name == NULL)
     {
-      lex_sbc_missing ("NAME");
+      lex_sbc_missing (lexer, "NAME");
       goto exit;
     }
 
index e1bad76b93a10a108e75f8fd4f2b16d2d86cc952..cb1789aa3704027dc53a3b09c0f3970324a45023 100644 (file)
@@ -119,7 +119,7 @@ parse_read_command (struct lexer *lexer, struct dataset *ds,
 
   if (fh == NULL)
     {
-      lex_sbc_missing ("FILE");
+      lex_sbc_missing (lexer, "FILE");
       goto error;
     }
 
index 3b6836799756bfc3f68677f83998752f4e637984..c157701e6cbd21fbc36fda554cf5bde173d9d9e6 100644 (file)
@@ -236,12 +236,12 @@ cmd_save_translate (struct lexer *lexer, struct dataset *ds)
 
   if (type == 0)
     {
-      lex_sbc_missing ("TYPE");
+      lex_sbc_missing (lexer, "TYPE");
       goto error;
     }
   else if (handle == NULL)
     {
-      lex_sbc_missing ("OUTFILE");
+      lex_sbc_missing (lexer, "OUTFILE");
       goto error;
     }
   else if (!replace && fn_exists (handle))
index 4ba4ddd4e0a35e9c8459d66ef6614e508f39d795..6ff0f9072df3f7a7070acab9867d02705e147cbd 100644 (file)
@@ -144,7 +144,7 @@ cmd_sysfile_info (struct lexer *lexer, struct dataset *ds)
 
   if (h == NULL)
     {
-      lex_sbc_missing ("FILE");
+      lex_sbc_missing (lexer, "FILE");
       goto error;
     }
 
index 9a67f969224e66be80d0d4c36a47fdb1d31ffd65..cdfc84836085377fd006518301491eed087269d3 100644 (file)
@@ -628,9 +628,10 @@ lex_sbc_only_once (struct lexer *lexer, const char *sbc)
    command has been parsed, and so lex_error() would always report "Syntax
    error at end of command", which does not help the user find the error. */
 void
-lex_sbc_missing (const char *sbc)
+lex_sbc_missing (struct lexer *lexer, const char *sbc)
 {
-  msg (SE, _("Required subcommand %s was not specified."), sbc);
+  lex_ofs_error (lexer, 0, lex_max_ofs (lexer),
+                 _("Required subcommand %s was not specified."), sbc);
 }
 
 /* Reports an error to the effect that specification SPEC may only be specified
@@ -1474,6 +1475,25 @@ lex_ofs (const struct lexer *lexer)
   return src ? src->parse_ofs : 0;
 }
 
+/* Returns the offset of the last token in the current command. */
+int
+lex_max_ofs (const struct lexer *lexer)
+{
+  struct lex_source *src = lex_source__ (lexer);
+  if (!src)
+    return 0;
+
+  int ofs = MAX (1, src->n_parse) - 1;
+  for (;;)
+    {
+      enum token_type type = lex_source_ofs__ (src, ofs)->token.type;
+      if (type == T_ENDCMD || type == T_STOP)
+        return ofs;
+
+      ofs++;
+    }
+}
+
 /* Returns the token within LEXER's current command with offset OFS.  Use
    lex_ofs() to find out the offset of the current token. */
 const struct token *
index c00f70646d56a7552f3cc8979de540532d2f3570..027339f96eeced45ec541e14b3da0e280abbf730 100644 (file)
@@ -159,6 +159,7 @@ struct substring lex_next_tokss (const struct lexer *, int n);
 
 /* Looking at the current command, including lookahead and lookbehind. */
 int lex_ofs (const struct lexer *);
+int lex_max_ofs (const struct lexer *);
 const struct token *lex_ofs_token (const struct lexer *, int ofs);
 struct msg_location *lex_ofs_location (const struct lexer *, int ofs0, int ofs1);
 struct msg_point lex_ofs_start_point (const struct lexer *, int ofs);
@@ -202,7 +203,7 @@ void lex_error_expecting_valist (struct lexer *, va_list);
 void lex_error_expecting_array (struct lexer *, const char **, size_t n);
 
 void lex_sbc_only_once (struct lexer *, const char *);
-void lex_sbc_missing (const char *);
+void lex_sbc_missing (struct lexer *, const char *);
 
 void lex_spec_only_once (struct lexer *, const char *subcommand,
                          const char *specification);
index 7c8d6519d18270b01d8b50c317d14e424c885351..01d448078752753109b18cdb32907dc14fe8f709 100644 (file)
@@ -6331,7 +6331,7 @@ matrix_save_parse (struct matrix_state *s)
         fh = fh_ref (s->prev_save_file);
       else
         {
-          lex_sbc_missing ("OUTFILE");
+          lex_sbc_missing (s->lexer, "OUTFILE");
           goto error;
         }
     }
@@ -6596,7 +6596,7 @@ matrix_read_parse (struct matrix_state *s)
 
   if (!read->c1)
     {
-      lex_sbc_missing ("FIELD");
+      lex_sbc_missing (s->lexer, "FIELD");
       goto error;
     }
 
@@ -6615,7 +6615,7 @@ matrix_read_parse (struct matrix_state *s)
         fh = fh_ref (s->prev_read_file);
       else
         {
-          lex_sbc_missing ("FILE");
+          lex_sbc_missing (s->lexer, "FILE");
           goto error;
         }
     }
@@ -7172,7 +7172,7 @@ matrix_write_parse (struct matrix_state *s)
 
   if (!write->c1)
     {
-      lex_sbc_missing ("FIELD");
+      lex_sbc_missing (s->lexer, "FIELD");
       goto error;
     }
 
@@ -7182,7 +7182,7 @@ matrix_write_parse (struct matrix_state *s)
         fh = fh_ref (s->prev_write_file);
       else
         {
-          lex_sbc_missing ("OUTFILE");
+          lex_sbc_missing (s->lexer, "OUTFILE");
           goto error;
         }
     }
@@ -7896,7 +7896,7 @@ matrix_msave_parse (struct matrix_state *s)
     }
   if (!msave->rowtype)
     {
-      lex_sbc_missing ("TYPE");
+      lex_sbc_missing (s->lexer, "TYPE");
       goto error;
     }
 
@@ -7914,7 +7914,7 @@ matrix_msave_parse (struct matrix_state *s)
         }
       if (!common->outfile)
         {
-          lex_sbc_missing ("OUTFILE");
+          lex_sbc_missing (s->lexer, "OUTFILE");
           goto error;
         }
       common->location = lex_ofs_location (s->lexer, start_ofs,
index 84759d8d10564ce723171df5998c1674092bb966..cc6e35edb9cbca4061ff56823fb904927bb197f5 100644 (file)
@@ -316,7 +316,7 @@ cmd_t_test (struct lexer *lexer, struct dataset *ds)
 
   if (tt.n_vars == 0 && tt.mode != MODE_PAIRED)
     {
-      lex_sbc_missing ("VARIABLES");
+      lex_sbc_missing (lexer, "VARIABLES");
       goto exit;
     }
 
index 7f1f6e3b3227a4af87e8146cee1216478b833041..37125d93990c0b74b5d97373060dfadddadc4259 100644 (file)
@@ -338,7 +338,9 @@ loop.sps:19.21-19.22: error: LOOP: Subcommand BY may only be specified once.
    19 | LOOP A=1 BY 5 TO 10 BY !.
       |                     ^~
 
-loop.sps:21: error: LOOP: Required subcommand TO was not specified.
+loop.sps:21.1-21.9: error: LOOP: Required subcommand TO was not specified.
+   21 | LOOP A=1.
+      | ^~~~~~~~~
 
 loop.sps:23.6: error: LOOP: Syntax error.
    23 | LOOP !.
index a152cbf009c6b228bd34c7c0499a05bebd3a5cdb..c3cb334650b52c0dcc17ebc14f0e44e638c2a8e2 100644 (file)
@@ -3388,7 +3388,9 @@ or FORMAT.
    17 | READ x/!.
       |        ^
 
-matrix.sps:18: error: READ: Required subcommand FIELD was not specified.
+matrix.sps:18.1-18.7: error: READ: Required subcommand FIELD was not specified.
+   18 | READ x.
+      | ^~~~~~~
 
 matrix.sps:19: error: READ: SIZE is required for reading data into a full
 matrix (as opposed to a submatrix).
@@ -3397,7 +3399,9 @@ matrix.sps:19.6: note: READ: This expression designates a full matrix.
    19 | READ x/FIELD=1 TO 10.
       |      ^
 
-matrix.sps:20: error: READ: Required subcommand FILE was not specified.
+matrix.sps:20.1-20.32: error: READ: Required subcommand FILE was not specified.
+   20 | READ x/FIELD=1 TO 10/SIZE={1,2}.
+      | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 matrix.sps:21: error: READ: 15 repetitions cannot fit in record width 10.
 
@@ -3602,9 +3606,15 @@ HOLD, or FORMAT.
    14 | WRITE 1/!.
       |         ^
 
-matrix.sps:15: error: WRITE: Required subcommand FIELD was not specified.
+matrix.sps:15.1-15.8: error: WRITE: Required subcommand FIELD was not
+specified.
+   15 | WRITE 1.
+      | ^~~~~~~~
 
-matrix.sps:16: error: WRITE: Required subcommand OUTFILE was not specified.
+matrix.sps:16.1-16.22: error: WRITE: Required subcommand OUTFILE was not
+specified.
+   16 | WRITE 1/FIELD=1 TO 10.
+      | ^~~~~~~~~~~~~~~~~~~~~~
 
 matrix.sps:17.51-17.55: note: WRITE: This syntax designates the number of
 repetitions.
@@ -4004,7 +4014,9 @@ or STRINGS.
     6 | SAVE 1/!.
       |        ^
 
-matrix.sps:7: error: SAVE: Required subcommand OUTFILE was not specified.
+matrix.sps:7.1-7.7: error: SAVE: Required subcommand OUTFILE was not specified.
+    7 | SAVE 1.
+      | ^~~~~~~
 
 matrix.sps:8: warning: SAVE: VARIABLES and NAMES both specified; ignoring
 NAMES.
@@ -4774,7 +4786,9 @@ FNAMES, SNAMES, SPLIT, or FACTOR.
    10 | MSAVE 1/!.
       |         ^
 
-matrix.sps:11: error: MSAVE: Required subcommand TYPE was not specified.
+matrix.sps:11.1-11.8: error: MSAVE: Required subcommand TYPE was not specified.
+   11 | MSAVE 1.
+      | ^~~~~~~~
 
 matrix.sps:12.25: error: MSAVE: FNAMES requires FACTOR.
    12 | MSAVE 1/TYPE=COV/FNAMES=x.
@@ -4784,7 +4798,10 @@ matrix.sps:13.25: error: MSAVE: SNAMES requires SPLIT.
    13 | MSAVE 1/TYPE=COV/SNAMES=x.
       |                         ^
 
-matrix.sps:14: error: MSAVE: Required subcommand OUTFILE was not specified.
+matrix.sps:14.1-14.17: error: MSAVE: Required subcommand OUTFILE was not
+specified.
+   14 | MSAVE 1/TYPE=COV.
+      | ^~~~~~~~~~~~~~~~~
 
 matrix.sps:20: error: MSAVE: OUTFILE must name the same file on each MSAVE
 within a single MATRIX command.
index 7b36fb4aaeb3f21dec552c52b40924aa9cf1a21c..bf1b293603fe1bd0b31e806ce278ef9886c07a6c 100644 (file)
@@ -729,9 +729,13 @@ T-TEST /testval=2.0 .
 T-TEST /groups=id(3) .
 ])
 AT_CHECK([pspp -O format=csv t-test.sps], [1], [dnl
-t-test.sps:11: error: T-TEST: Required subcommand VARIABLES was not specified.
+"t-test.sps:11.1-11.21: error: T-TEST: Required subcommand VARIABLES was not specified.
+   11 | T-TEST /testval=2.0 .
+      | ^~~~~~~~~~~~~~~~~~~~~"
 
-t-test.sps:12: error: T-TEST: Required subcommand VARIABLES was not specified.
+"t-test.sps:12.1-12.22: error: T-TEST: Required subcommand VARIABLES was not specified.
+   12 | T-TEST /groups=id(3) .
+      | ^~~~~~~~~~~~~~~~~~~~~~"
 ])
 AT_CLEANUP