From 0db2648930e3b6c376a539aabe368aade83ee29a Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Fri, 30 Nov 2012 14:33:05 +0100 Subject: [PATCH 1/8] getargs: add support for --flags/-f Introduce -fdiagnostics-show-caret * src/getargs.c (flag_flag): New global. * src/getargs.h (flag): New enum. --- src/getargs.c | 40 ++++++++++++++++++++++++++++++++++++++-- src/getargs.h | 12 ++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/getargs.c b/src/getargs.c index c480649d..1b7c6f1b 100644 --- a/src/getargs.c +++ b/src/getargs.c @@ -63,6 +63,7 @@ bool error_verbose = false; bool nondeterministic_parser = false; bool glr_parser = false; +int feature_flag = feature_none; int report_flag = report_none; int trace_flag = trace_none; int warnings_flag = warnings_conflicts_sr | warnings_conflicts_rr @@ -257,6 +258,26 @@ static const int warnings_types[] = ARGMATCH_VERIFY (warnings_args, warnings_types); +/*-----------------------. +| --feature's handling. | +`-----------------------*/ + +static const char * const feature_args[] = +{ + "none", + "caret", "diagnostics-show-caret", + "all", + 0 +}; + +static const int feature_types[] = +{ + feature_none, + feature_caret, feature_caret, + feature_all +}; + +ARGMATCH_VERIFY (feature_args, feature_types); /*-------------------------------------------. | Display the help message and exit STATUS. | @@ -301,6 +322,7 @@ Operation modes:\n\ --print-datadir output directory containing skeletons and XSLT\n\ -y, --yacc emulate POSIX Yacc\n\ -W, --warnings[=CATEGORY] report the warnings falling in CATEGORY\n\ + -f, --feature[=FEATURE] activate miscellaneous features\n\ \n\ "), stdout); @@ -316,8 +338,8 @@ Parser:\n\ deprecated by '-Dapi.prefix=PREFIX'\n\ -l, --no-lines don't generate '#line' directives\n\ -k, --token-table include a table of token names\n\ -\n\ "), stdout); + putc ('\n', stdout); /* Keep -d and --defines separate so that ../build-aux/cross-options.pl * won't assume that -d also takes an argument. */ @@ -333,8 +355,8 @@ Output:\n\ -g, --graph[=FILE] also output a graph of the automaton\n\ -x, --xml[=FILE] also output an XML report of the automaton\n\ (the XML schema is experimental)\n\ -\n\ "), stdout); + putc ('\n', stdout); fputs (_("\ Warning categories include:\n\ @@ -360,6 +382,14 @@ THINGS is a list of comma separated words that can include:\n\ `all' include all the above information\n\ `none' disable the report\n\ "), stdout); + putc ('\n', stdout); + + fputs (_("\ +FEATURE is a list of comma separated words that can include:\n\ + `caret' show errors with carets\n\ + `all' all of the above\n\ + `none' disable all of the above\n\ + "), stdout); putc ('\n', stdout); printf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT); @@ -469,6 +499,7 @@ static char const short_options[] = "W::" "b:" "d" + "f::" "e" "g::" "h" @@ -521,6 +552,7 @@ static struct option const long_options[] = /* Output. */ { "defines", optional_argument, 0, 'd' }, + { "flag", optional_argument, 0, 'f' }, /* Operation modes. */ { "fixed-output-files", no_argument, 0, 'y' }, @@ -613,6 +645,10 @@ getargs (int argc, char *argv[]) version (); exit (EXIT_SUCCESS); + case 'f': + FLAGS_ARGMATCH (flag, optarg); + break; + case 'W': FLAGS_ARGMATCH (warnings, optarg); break; diff --git a/src/getargs.h b/src/getargs.h index ef97822a..b2126fc2 100644 --- a/src/getargs.h +++ b/src/getargs.h @@ -130,6 +130,18 @@ enum warnings /** What warnings are issued. */ extern int warnings_flag; +/*-------------. +| --features. | +`-------------*/ + +enum feature + { + feature_none = 0, /**< No additional feature. */ + feature_caret = 1 << 0, /**< Enhance the output of errors with carets. */ + feature_all = ~0 /**< All above features. */ + }; +/** What additional features to use. */ +extern int feature_flag; /** Process the command line arguments. * From 3f5d1b2c67651a9d620946de421f2e51600b885e Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Fri, 30 Nov 2012 14:34:56 +0100 Subject: [PATCH 2/8] errors: show carets * src/locations.c (caret_info): New, persistant information useful for... (location_caret): New, print a caret. (cleanup_caret): Release caret_info cleanly, call it... * src/main.c (main): Here. * src/complain.c (error_message): Call location_caret here. --- src/complain.c | 12 +++++--- src/getargs.c | 2 +- src/gram.c | 13 +++++--- src/location.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/location.h | 7 +++++ src/main.c | 2 ++ 6 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/complain.c b/src/complain.c index b063c6b6..ede0ccf4 100644 --- a/src/complain.c +++ b/src/complain.c @@ -74,11 +74,15 @@ error_message (location *loc, vfprintf (stderr, message, args); { size_t l = strlen (message); - if (l < 2 || message[l-2] != ':' || message[l-1] != ' ') { - putc ('\n', stderr); - fflush (stderr); - } + if (l < 2 || message[l - 2] != ':' || message[l - 1] != ' ') + { + putc ('\n', stderr); + fflush (stderr); + if (loc && feature_flag & feature_caret) + location_caret (stderr, *loc); + } } + fflush (stderr); } /** Wrap error_message() with varargs handling. */ diff --git a/src/getargs.c b/src/getargs.c index 1b7c6f1b..c8865bfb 100644 --- a/src/getargs.c +++ b/src/getargs.c @@ -646,7 +646,7 @@ getargs (int argc, char *argv[]) exit (EXIT_SUCCESS); case 'f': - FLAGS_ARGMATCH (flag, optarg); + FLAGS_ARGMATCH (feature, optarg); break; case 'W': diff --git a/src/gram.c b/src/gram.c index d1b38043..5730e596 100644 --- a/src/gram.c +++ b/src/gram.c @@ -308,11 +308,16 @@ grammar_rules_useless_report (const char *message) for (r = 0; r < nrules ; ++r) if (!rules[r].useful) { - warn_at (rules[r].location, "%s: ", message); - if (warnings_flag & warnings_other) + if (feature_flag & feature_caret) + warn_at (rules[r].location, "%s", message); + else { - rule_print (&rules[r], stderr); - fflush (stderr); + warn_at (rules[r].location, "%s: ", message); + if (warnings_flag & warnings_other) + { + rule_print (&rules[r], stderr); + fflush (stderr); + } } } } diff --git a/src/location.c b/src/location.c index fa1b53cb..6e5306c6 100644 --- a/src/location.c +++ b/src/location.c @@ -138,6 +138,89 @@ location_print (FILE *out, location loc) return res; } + +/* Persistant data used by location_caret to avoid reopening and rereading the + same file all over for each error. */ +struct caret_info +{ + FILE* source; + size_t line; + size_t offset; +}; + +static struct caret_info caret_info = { NULL, 1, 0 }; + +/* Free any allocated ressources and close any open file handles that are + left-over by the usage of location_caret. */ +void +cleanup_caret () +{ + if (caret_info.source) + fclose (caret_info.source); +} + +/* Output to OUT the line and caret corresponding to location LOC. */ +void +location_caret (FILE *out, location loc) +{ + /* FIXME: find a way to support X-file locations, and only open once each + file. That would make the procedure future-proof. */ + if (! (caret_info.source + || (caret_info.source = fopen (loc.start.file, "r"))) + || loc.start.column == -1 || loc.start.line == -1) + return; + + /* If the line we want to quote is seekable (the same line as the previous + location), just seek it. If it was before, we lost track of it, so + return to the start of file. */ + if (caret_info.line <= loc.start.line) + fseek (caret_info.source, caret_info.offset, SEEK_SET); + else + { + caret_info.line = 1; + caret_info.offset = 0; + fseek (caret_info.source, caret_info.offset, SEEK_SET); + } + + /* Advance to the line's position, keeping track of the offset. */ + { + int i; + for (i = caret_info.line; i < loc.start.line; caret_info.offset++) + if (fgetc (caret_info.source) == '\n') + ++i; + } + caret_info.line = loc.start.line; + + /* Read the actual line. Don't update the offset, so that we keep a pointer + to the start of the line. */ + { + ssize_t len = 0; + char *buf = NULL; + if ((len = getline (&buf, (size_t*) &len, caret_info.source)) != -1) + { + /* The caret of a multiline location ends with the first line. */ + int end = loc.start.line != loc.end.line ? len : loc.end.column; + + if (len) + { + int i = loc.start.column; + /* Quote the file, indent by a single column. */ + fputc (' ', out); + fwrite (buf, 1, len, out); + + /* Print the caret, with the same indent as above. */ + fputc (' ', out); + fprintf (out, "%*s", loc.start.column - 1, ""); + do { + fputc ('^', out); + } while (++i < end); + } + fputc ('\n', out); + free (buf); + } + } +} + void boundary_set_from_string (boundary *bound, char *loc_str) { diff --git a/src/location.h b/src/location.h index 5ebb92e3..c1859aeb 100644 --- a/src/location.h +++ b/src/location.h @@ -102,6 +102,13 @@ void location_compute (location *loc, characters. */ unsigned location_print (FILE *out, location loc); +/* Free any allocated ressources and close any open file handles that are + left-over by the usage of location_caret. */ +void cleanup_caret (void); + +/* Output to OUT the line and caret corresponding to location LOC. */ +void location_caret (FILE *out, location loc); + /* Return -1, 0, 1, depending whether a is before, equal, or after b. */ static inline int diff --git a/src/main.c b/src/main.c index 0396b0f1..184d789d 100644 --- a/src/main.c +++ b/src/main.c @@ -216,5 +216,7 @@ main (int argc, char *argv[]) timevar_stop (TV_TOTAL); timevar_print (stderr); + cleanup_caret (); + return complaint_issued ? EXIT_FAILURE : EXIT_SUCCESS; } From 505ece51710209eb4aba50166f8f7ddc57d0562b Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Mon, 3 Dec 2012 11:12:07 +0100 Subject: [PATCH 3/8] tests: enhance existing tests with carets * tests/actions.at: Unset value. * tests/conflicts.at: Rule useless due to conflicts. * tests/input.at: Missing terminator, unexpected end of file, command line redefinition of variable. * tests/named-refs.at: Many errors. * tests/reduce.at: Useless nonterminals and rules. * tests/regression.at: Large token. --- tests/actions.at | 9 ++++ tests/conflicts.at | 6 +++ tests/input.at | 31 ++++++++++++ tests/named-refs.at | 121 ++++++++++++++++++++++++++++++++++++++++++++ tests/reduce.at | 79 +++++++++++++++++++++++++++++ tests/regression.at | 8 +++ 6 files changed, 254 insertions(+) diff --git a/tests/actions.at b/tests/actions.at index 5e897a2e..ba46fe36 100644 --- a/tests/actions.at +++ b/tests/actions.at @@ -1304,6 +1304,15 @@ AT_BISON_CHECK([-o input.c input.y], 0,, input.y:30.3-35.37: warning: unused value: $3 ]]) +AT_BISON_CHECK([-fcaret -o input.c input.y], 0,, +[[input.y:33.3-23: warning: unset value: $$ + { @$ = 4; } // Only used. + ^^^^^^^^^^^^^^^^^^^^^ +input.y:30.3-35.37: warning: unused value: $3 + { @$ = 1; } // Not set or used. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +]]) + AT_COMPILE([input]) AT_PARSER_CHECK([./input], 1,, [[Starting parse diff --git a/tests/conflicts.at b/tests/conflicts.at index a13d7546..5653b481 100644 --- a/tests/conflicts.at +++ b/tests/conflicts.at @@ -41,6 +41,12 @@ AT_BISON_CHECK([-o input.c input.y], 0, [], [[input.y:4.9: warning: rule useless in parser due to conflicts: e: /* empty */ ]]) +AT_BISON_CHECK([-fcaret -o input.c input.y], 0, [], +[[input.y:4.9: warning: rule useless in parser due to conflicts + e: 'e' | /* Nothing. */; + ^ +]]) + AT_CLEANUP diff --git a/tests/input.at b/tests/input.at index 474b7dd1..cd0dd6ac 100644 --- a/tests/input.at +++ b/tests/input.at @@ -813,6 +813,25 @@ input.y:19.13-20.0: error: missing '}' at end of file input.y:20.1: error: syntax error, unexpected end of file ]]) +AT_BISON_CHECK([-fcaret -o input.c input.y], 1, [], +[[input.y:1.10-2.0: error: missing '"' at end of line + %token A "a + ^^ +input.y:4.10-5.0: error: missing "'" at end of line + %token C '1 + ^^ +input.y:14.11-15.0: error: missing "'" at end of line + %type 'a + ^^ +input.y:16.11-17.0: error: missing '"' at end of line + %type "a + ^^ +input.y:19.13-20.0: error: missing '}' at end of file + %destructor { free ($$) + ^^^^^^^^^^^ +input.y:20.1: error: syntax error, unexpected end of file +]]) + AT_CLEANUP @@ -1045,6 +1064,18 @@ AT_BISON_CHECK([[-Dvar=cmd-d input-dg.y]], [[1]], [], :1: previous definition ]]) +AT_DATA([[input-dg.y]], +[[%define var "gram" +%% +start: ; +]]) +AT_BISON_CHECK([[-fcaret -Dvar=cmd-d input-dg.y]], [[1]], [], +[[input-dg.y:1.9-11: error: %define variable 'var' redefined + %define var "gram" + ^^^ +:2: previous definition +]]) + AT_DATA([[input-unused.y]], [[%% start: ; diff --git a/tests/named-refs.at b/tests/named-refs.at index de48e0f7..46b0159c 100644 --- a/tests/named-refs.at +++ b/tests/named-refs.at @@ -393,6 +393,127 @@ test.y:46.46-54: error: invalid reference: '$then-a.f' test.y:45.12-46.65: symbol not found in production: then test.y:45.41-46: possibly meant: $[then-a].f at $4 ]]) + +AT_BISON_CHECK([-fcaret -o test.c test.y], 1, [], +[[test.y:24.36-41: error: invalid reference: '$cond1' + { $if_stmt1 = new IfStmt($cond1, $then.f1, $else); }; + ^^^^^^ +test.y:23.11-24.62: symbol not found in production: cond1 + if_stmt1: IF expr[cond] THEN stmt[then] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:26.43-53: error: invalid reference: '$stmt.field' + { $if_stmt2 = new IfStmt($cond, $stmt.field, 0); }; + ^^^^^^^^^^^ +test.y:25.11-26.60: symbol not found in production: stmt + if_stmt2: IF expr[cond] THEN stmt[then] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:25.35-38: possibly meant: $then.field, hiding $stmt.field at $4 + if_stmt2: IF expr[cond] THEN stmt[then] FI + ^^^^ +test.y:28.43-52: error: invalid reference: '$stmt.list' + { $if_stmt3 = new IfStmt($cond, $stmt.list, 0); }; + ^^^^^^^^^^ +test.y:27.11-28.59: symbol not found in production: stmt + if_stmt3: IF expr[cond] THEN stmt.list FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:27.30-38: possibly meant: $[stmt.list] at $4 + if_stmt3: IF expr[cond] THEN stmt.list FI + ^^^^^^^^^ +test.y:30.43-46: error: ambiguous reference: '$xyz' + { $if_stmt4 = new IfStmt($cond, $xyz, $cond); }; + ^^^^ +test.y:29.35-37: refers to: $xyz at $4 + if_stmt4: IF expr[cond] THEN stmt[xyz] ELSE stmt[xyz] FI + ^^^ +test.y:29.50-52: refers to: $xyz at $6 + if_stmt4: IF expr[cond] THEN stmt[xyz] ELSE stmt[xyz] FI + ^^^ +test.y:32.43-52: error: invalid reference: '$stmt.list' + { $if_stmt5 = new IfStmt($cond, $stmt.list, $else); }; + ^^^^^^^^^^ +test.y:31.11-32.63: symbol not found in production: stmt + if_stmt5: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:31.40-43: possibly meant: $then, hiding $[stmt.list] at $4 + if_stmt5: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:31.61-64: possibly meant: $else, hiding $[stmt.list] at $6 + if_stmt5: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:34.43-58: error: invalid reference: '$stmt.list.field' + { $if_stmt6 = new IfStmt($cond, $stmt.list.field, $else); }; + ^^^^^^^^^^^^^^^^ +test.y:33.11-34.69: symbol not found in production: stmt + if_stmt6: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:33.40-43: possibly meant: $then.field, hiding $[stmt.list].field at $4 + if_stmt6: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:33.61-64: possibly meant: $else.field, hiding $[stmt.list].field at $6 + if_stmt6: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:36.43-54: error: invalid reference: '$[stmt.list]' + { $if_stmt7 = new IfStmt($cond, $[stmt.list].field, $else); }; + ^^^^^^^^^^^^ +test.y:35.11-36.71: symbol not found in production: stmt.list + if_stmt7: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:35.40-43: possibly meant: $then, hiding $[stmt.list] at $4 + if_stmt7: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:35.61-64: possibly meant: $else, hiding $[stmt.list] at $6 + if_stmt7: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^ +test.y:38.43-49: error: invalid reference: '$then.1' + { $if_stmt8 = new IfStmt($cond, $then.1, $else); }; + ^^^^^^^ +test.y:37.11-38.60: symbol not found in production: then + if_stmt8: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:37.40-45: possibly meant: $[then.1] at $4 + if_stmt8: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + ^^^^^^ +test.y:40.43-55: error: invalid reference: '$then.1.field' + { $if_stmt9 = new IfStmt($cond, $then.1.field, $else); }; + ^^^^^^^^^^^^^ +test.y:39.11-40.66: symbol not found in production: then + if_stmt9: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:39.40-45: possibly meant: $[then.1].field at $4 + if_stmt9: IF expr[cond] THEN stmt.list[then.1] ELSE stmt.list[else] FI + ^^^^^^ +test.y:42.44-50: error: invalid reference: '$stmt.x' + { $if_stmt10 = new IfStmt($cond, $stmt.x, 0); }; + ^^^^^^^ +test.y:41.12-42.57: symbol not found in production: stmt + if_stmt10: IF expr[cond] THEN stmt[stmt.x] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:41.36-41: possibly meant: $[stmt.x].x, hiding $stmt.x at $4 + if_stmt10: IF expr[cond] THEN stmt[stmt.x] FI + ^^^^^^ +test.y:41.36-41: possibly meant: $[stmt.x] at $4 + if_stmt10: IF expr[cond] THEN stmt[stmt.x] FI + ^^^^^^ +test.y:44.13-22: error: invalid reference: '$if-stmt-a' + { $if-stmt-a = new IfStmt($cond, $then, $else); }; + ^^^^^^^^^^ +test.y:43.12-44.59: symbol not found in production: if + if-stmt-a: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:43.1-9: possibly meant: $[if-stmt-a] at $$ + if-stmt-a: IF expr[cond] THEN stmt.list[then] ELSE stmt.list[else] FI + ^^^^^^^^^ +test.y:46.46-54: error: invalid reference: '$then-a.f' + { $[if-stmt-b] = new IfStmt($cond, $then-a.f, $else); }; + ^^^^^^^^^ +test.y:45.12-46.65: symbol not found in production: then + if-stmt-b: IF expr[cond] THEN if-stmt-a[then-a] ELSE stmt.list[else] FI + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +test.y:45.41-46: possibly meant: $[then-a].f at $4 + if-stmt-b: IF expr[cond] THEN if-stmt-a[then-a] ELSE stmt.list[else] FI + ^^^^^^ +]]) + AT_CLEANUP ####################################################################### diff --git a/tests/reduce.at b/tests/reduce.at index bf43bf9d..47f923cf 100644 --- a/tests/reduce.at +++ b/tests/reduce.at @@ -142,6 +142,65 @@ useless8: '8'; useless9: '9'; ]]) +AT_BISON_CHECK([[-fcaret input.y]], 0, [], +[[input.y: warning: 9 nonterminals useless in grammar +input.y: warning: 9 rules useless in grammar +input.y:6.1-8: warning: nonterminal useless in grammar: useless1 + useless1: '1'; + ^^^^^^^^ +input.y:7.1-8: warning: nonterminal useless in grammar: useless2 + useless2: '2'; + ^^^^^^^^ +input.y:8.1-8: warning: nonterminal useless in grammar: useless3 + useless3: '3'; + ^^^^^^^^ +input.y:9.1-8: warning: nonterminal useless in grammar: useless4 + useless4: '4'; + ^^^^^^^^ +input.y:10.1-8: warning: nonterminal useless in grammar: useless5 + useless5: '5'; + ^^^^^^^^ +input.y:11.1-8: warning: nonterminal useless in grammar: useless6 + useless6: '6'; + ^^^^^^^^ +input.y:12.1-8: warning: nonterminal useless in grammar: useless7 + useless7: '7'; + ^^^^^^^^ +input.y:13.1-8: warning: nonterminal useless in grammar: useless8 + useless8: '8'; + ^^^^^^^^ +input.y:14.1-8: warning: nonterminal useless in grammar: useless9 + useless9: '9'; + ^^^^^^^^ +input.y:6.11-13: warning: rule useless in grammar + useless1: '1'; + ^^^ +input.y:7.11-13: warning: rule useless in grammar + useless2: '2'; + ^^^ +input.y:8.11-13: warning: rule useless in grammar + useless3: '3'; + ^^^ +input.y:9.11-13: warning: rule useless in grammar + useless4: '4'; + ^^^ +input.y:10.11-13: warning: rule useless in grammar + useless5: '5'; + ^^^ +input.y:11.11-13: warning: rule useless in grammar + useless6: '6'; + ^^^ +input.y:12.11-13: warning: rule useless in grammar + useless7: '7'; + ^^^ +input.y:13.11-13: warning: rule useless in grammar + useless8: '8'; + ^^^ +input.y:14.11-13: warning: rule useless in grammar + useless9: '9'; + ^^^ +]]) + AT_BISON_CHECK([[input.y]], 0, [], [[input.y: warning: 9 nonterminals useless in grammar input.y: warning: 9 rules useless in grammar @@ -238,6 +297,26 @@ non_productive: non_productive useless_token %% ]]) +AT_BISON_CHECK([[-fcaret not-reduced.y]], 0, [], +[[not-reduced.y: warning: 2 nonterminals useless in grammar +not-reduced.y: warning: 3 rules useless in grammar +not-reduced.y:14.1-13: warning: nonterminal useless in grammar: not_reachable + not_reachable: useful { /* A not reachable action. */ } + ^^^^^^^^^^^^^ +not-reduced.y:11.6-19: warning: nonterminal useless in grammar: non_productive + | non_productive { /* A non productive action. */ } + ^^^^^^^^^^^^^^ +not-reduced.y:11.6-57: warning: rule useless in grammar + | non_productive { /* A non productive action. */ } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +not-reduced.y:14.16-56: warning: rule useless in grammar + not_reachable: useful { /* A not reachable action. */ } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +not-reduced.y:17.17-18.63: warning: rule useless in grammar + non_productive: non_productive useless_token + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +]]) + AT_BISON_CHECK([[not-reduced.y]], 0, [], [[not-reduced.y: warning: 2 nonterminals useless in grammar not-reduced.y: warning: 3 rules useless in grammar diff --git a/tests/regression.at b/tests/regression.at index 3fa20e27..db6c3b9f 100644 --- a/tests/regression.at +++ b/tests/regression.at @@ -481,6 +481,14 @@ AT_BISON_CHECK([-o input.c input.y], [[0]], [[]], [[input.y:22.8-14: warning: symbol SPECIAL redeclared input.y:22.8-63: warning: symbol "\\'?\"\a\b\f\n\r\t\v\001\201\001\201??!" used more than once as a literal string ]]) +AT_BISON_CHECK([-fcaret -o input.c input.y], [[0]], [[]], +[[input.y:22.8-14: warning: symbol SPECIAL redeclared + %token SPECIAL "\\\'\?\"\a\b\f\n\r\t\v\001\201\x001\x000081??!" + ^^^^^^^ +input.y:22.8-63: warning: symbol "\\'?\"\a\b\f\n\r\t\v\001\201\001\201??!" used more than once as a literal string + %token SPECIAL "\\\'\?\"\a\b\f\n\r\t\v\001\201\x001\x000081??!" + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +]]) AT_COMPILE([input]) # Checking the error message here guarantees that yytname, which does contain From 7bada5355e10f560269825cbd658caaa473573f7 Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Tue, 4 Dec 2012 13:12:12 +0100 Subject: [PATCH 4/8] doc: document carets * NEWS: Announce it. * doc/bison.texi (Bison Options): Here. --- NEWS | 13 +++++++++++++ doc/bison.texi | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/NEWS b/NEWS index 93a1ef54..0c3d025e 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,19 @@ GNU Bison NEWS The introduction of this feature, in 2.4, was four years ago. The --language option and the %language directive are no longer experimental. +** New format for error reports: carets + + Caret errors have been added to Bison, for example (taken from the + documentation): + + input.y:3.20-23: error: ambiguous reference: '$exp' + exp: exp '+' exp { $exp = $1 + $2; }; + ^^^^ + + The default behaviour for now is still not to display these unless explictly + asked with -fall of -fcaret. However, in a later release, it will be made the + default behavior (but may still be deactivated with -fno-caret). + ** New value for %define variable: api.pure full The %define variable api.pure requests a pure (reentrant) parser. However, diff --git a/doc/bison.texi b/doc/bison.texi index 5952225c..06af0884 100644 --- a/doc/bison.texi +++ b/doc/bison.texi @@ -9239,6 +9239,56 @@ Treat warnings as errors. A category can be turned off by prefixing its name with @samp{no-}. For instance, @option{-Wno-yacc} will hide the warnings about POSIX Yacc incompatibilities. + +@item -f [@var{feature}] +@itemx --feature[=@var{feature}] +Activate miscellaneous @var{feature}. @var{feature} can be one of: +@table @code +@item caret +@itemx diagnostics-show-caret +Show caret errors, in a manner similar to GCC's +@option{-fdiagnostics-show-caret}, or Clang's @option{-fcaret-diagnotics}. The +location provided with the message is used to quote the corresponding line of +the source file, underlining the important part of it with carets (^). Here is +an example, using the following file @file{input.y}: + +@example +%type exp +%% +exp: exp '+' exp @{ $exp = $1 + $2; @}; +@end example + +When invoked with @option{-fcaret}, Bison will report: + +@example +@group +input.y:3.20-23: error: ambiguous reference: '$exp' + exp: exp '+' exp @{ $exp = $1 + $2; @}; + ^^^^ +@end group +@group +input.y:3.1-3: refers to: $exp at $$ + exp: exp '+' exp @{ $exp = $1 + $2; @}; + ^^^ +@end group +@group +input.y:3.6-8: refers to: $exp at $1 + exp: exp '+' exp @{ $exp = $1 + $2; @}; + ^^^ +@end group +@group +input.y:3.14-16: refers to: $exp at $3 + exp: exp '+' exp @{ $exp = $1 + $2; @}; + ^^^ +@end group +@group +input.y:3.32-33: error: $2 of 'exp' has no declared type + exp: exp '+' exp @{ $exp = $1 + $2; @}; + ^^ +@end group +@end example + +@end table @end table @noindent From 7ae57e2a352764327f7b6b969a28d8c6640eab33 Mon Sep 17 00:00:00 2001 From: Akim Demaille Date: Wed, 5 Dec 2012 11:21:21 +0100 Subject: [PATCH 5/8] c++: support wide strings for file names Reported by Mark Boyall. http://lists.gnu.org/archive/html/help-bison/2011-08/msg00002.html * data/location.cc (operator<<): Be templated on the type of output stream. * tests/headers.at (Several parsers): Adjust. --- THANKS | 1 + data/location.cc | 9 ++++++--- tests/headers.at | 12 +++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/THANKS b/THANKS index 218eea79..9a64012c 100644 --- a/THANKS +++ b/THANKS @@ -68,6 +68,7 @@ Lie Yan lie.yan@kaust.edu.sa Magnus Fromreide magfr@lysator.liu.se Marc Autret autret_m@epita.fr Marc Mendiola mmendiol@usc.edu +Mark Boyall wolfeinstein@gmail.com Martin Jacobs martin.jacobs@arcor.de Martin Mokrejs mmokrejs@natur.cuni.cz Martin Nylin martin.nylin@linuxmail.org diff --git a/data/location.cc b/data/location.cc index 58182a34..4082e09d 100644 --- a/data/location.cc +++ b/data/location.cc @@ -136,8 +136,9 @@ b4_copyright([Positions for Bison parsers in C++], ** \param ostr the destination output stream ** \param pos a reference to the position to redirect */ - inline std::ostream& - operator<< (std::ostream& ostr, const position& pos) + template + inline std::basic_ostream& + operator<< (std::basic_ostream& ostr, const position& pos) { if (pos.filename) ostr << *pos.filename << ':'; @@ -275,7 +276,9 @@ b4_copyright([Locations for Bison parsers in C++], ** ** Avoid duplicate information. */ - inline std::ostream& operator<< (std::ostream& ostr, const location& loc) + template + inline std::basic_ostream& + operator<< (std::basic_ostream& ostr, const location& loc) { position last = loc.end - 1; ostr << loc.begin; diff --git a/tests/headers.at b/tests/headers.at index dcf09a72..351b8a7b 100644 --- a/tests/headers.at +++ b/tests/headers.at @@ -229,14 +229,16 @@ AT_TEST([x8], [%define api.pure %define api.push-pull both]) # C++ output relies on namespaces and still uses yy a lot. # # Check there is no 'YY' left. -# Ignore comments, YYPARSE_PARAM (obsolete), YYPUSH_MORE(_DEFINED)? -# (constant definition), YY_\w+_INCLUDED (header guards). +# Ignore comments, YYChar (template parameter), YYPARSE_PARAM +# (obsolete), YYPUSH_MORE(_DEFINED)? (constant definition), +# YY_\w+_INCLUDED (header guards). # # YYDEBUG (not renamed) can be read, but not changed. AT_CHECK([[$PERL -n -0777 -e ' s{/\*.*?\*/}{}gs; s{//.*}{}g; - s{\b(YYPARSE_PARAM + s{\b(YYChar + |YYPARSE_PARAM |YYPUSH_MORE(_DEFINED)? |YY_\w+_INCLUDED |YY_NULL @@ -244,13 +246,13 @@ AT_CHECK([[$PERL -n -0777 -e ' )\b}{}gx; while (/^(.*YY.*)$/gm) { - print "$ARGV: $1\n"; + print "$ARGV: invalid exported YY: $1\n"; } if ($ARGV =~ /\.h$/) { while (/^(.*yy.*)$/gm) { - print "$ARGV: $1\n"; + print "$ARGV: invalid exported yy: $1\n"; } } ' -- *.hh *.h]]) From e35cd6def7a19423a5f0fc566d844f6019df111a Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Wed, 5 Dec 2012 15:27:25 +0100 Subject: [PATCH 6/8] misc: require getline * bootstrap.conf: Here, used by src/location.c. * src/getargs.c (long_options): Rename --flags to --feature. --- bootstrap.conf | 4 +++- build-aux/git-log-fix | 5 +++++ lib/.gitignore | 2 ++ m4/.gitignore | 2 ++ src/getargs.c | 2 +- 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bootstrap.conf b/bootstrap.conf index 165d5e7d..9dccd001 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -20,7 +20,9 @@ gnulib_modules=' argmatch assert calloc-posix close closeout config-h c-strcase configmake dirname - error extensions fdl fopen-safer getopt-gnu + error extensions fdl fopen-safer + getline + getopt-gnu gettext git-version-gen gitlog-to-changelog gpl-3.0 hash inttypes isnan javacomp-script javaexec-script ldexpl malloc-gnu mbschr mbsrchr diff --git a/build-aux/git-log-fix b/build-aux/git-log-fix index af702fe6..72111507 100644 --- a/build-aux/git-log-fix +++ b/build-aux/git-log-fix @@ -1,3 +1,8 @@ # This file is expected to be used via gitlog-to-changelog's --amend=FILE # option. It specifies what changes to make to each given SHA1's commit # log and metadata, using Perl-eval'able expressions. + +0db2648930e3b6c376a539aabe368aade83ee29a +s/--flags/--feature/; +s/flag_flag/feature_flag/; +s/\bflag\b/feature/; diff --git a/lib/.gitignore b/lib/.gitignore index 15354476..2e92c2ff 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -278,3 +278,5 @@ /stdio.c /unistd.c /wctype-h.c +/getdelim.c +/getline.c diff --git a/m4/.gitignore b/m4/.gitignore index 7d112490..8b665097 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -180,3 +180,5 @@ /xstrndup.m4 /obstack-printf.m4 /extern-inline.m4 +/getdelim.m4 +/getline.m4 diff --git a/src/getargs.c b/src/getargs.c index c8865bfb..ab2a28e8 100644 --- a/src/getargs.c +++ b/src/getargs.c @@ -552,7 +552,7 @@ static struct option const long_options[] = /* Output. */ { "defines", optional_argument, 0, 'd' }, - { "flag", optional_argument, 0, 'f' }, + { "feature", optional_argument, 0, 'f' }, /* Operation modes. */ { "fixed-output-files", no_argument, 0, 'y' }, From b56484a5d3f983b163287f5bf0a77b80529200cf Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Wed, 5 Dec 2012 18:26:36 +0100 Subject: [PATCH 7/8] cpp: make the check of Flex version portable This was problematic with tcc 0.9.25 * src/flex-scanner.h (FLEX_VERSION_GT): Rewrite and rename as... (FLEX_VERSION): This. --- src/flex-scanner.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/flex-scanner.h b/src/flex-scanner.h index 99798411..028082ec 100644 --- a/src/flex-scanner.h +++ b/src/flex-scanner.h @@ -23,18 +23,18 @@ /* Whether this version of Flex is (strictly) greater than Major.Minor.Subminor. */ -#define FLEX_VERSION_GT(Major, Minor, Subminor) \ - (defined YY_FLEX_MAJOR_VERSION \ - && (Major < YY_FLEX_MAJOR_VERSION \ - || (Major == YY_FLEX_MAJOR_VERSION \ - && (defined YY_FLEX_MINOR_VERSION \ - && (Minor < YY_FLEX_MINOR_VERSION \ - || (Minor == YY_FLEX_MINOR_VERSION \ - && defined YY_FLEX_SUBMINOR_VERSION \ - && Subminor < YY_FLEX_SUBMINOR_VERSION)))))) - +#ifdef YY_FLEX_SUBMINOR_VERSION +# define FLEX_VERSION \ + (YY_FLEX_MAJOR_VERSION) * 1000000 \ ++ (YY_FLEX_MINOR_VERSION) * 1000 \ ++ (YY_FLEX_SUBMINOR_VERSION) +#else +# define FLEX_VERSION \ + (YY_FLEX_MAJOR_VERSION) * 1000000 \ ++ (YY_FLEX_MINOR_VERSION) * 1000 +#endif /* Pacify "gcc -Wmissing-prototypes" when flex 2.5.31 is used. */ -# if ! FLEX_VERSION_GT (2, 5, 31) +# if FLEX_VERSION <= 2005031 int FLEX_PREFIX (get_lineno) (void); FILE *FLEX_PREFIX (get_in) (void); FILE *FLEX_PREFIX (get_out) (void); @@ -65,7 +65,7 @@ int FLEX_PREFIX (lex_destroy) (void); versions according to the Flex manual) leak memory if yylex_destroy is not invoked. However, yylex_destroy is not defined before Flex 2.5.9, so give an implementation here that at least appears to work with Flex 2.5.4. */ -#if ! FLEX_VERSION_GT (2, 5, 9) +#if FLEX_VERSION <= 2005009 # define yylex_destroy() yy_delete_buffer (YY_CURRENT_BUFFER) #endif From 9960a6ae75842aa7836f39b59e82eef0319338bc Mon Sep 17 00:00:00 2001 From: Theophile Ranquet Date: Thu, 6 Dec 2012 10:49:12 +0100 Subject: [PATCH 8/8] misc: pacify the Tiny C Compiler * src/graphviz.c (conclude_red): Remove a useless return. --- src/graphviz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphviz.c b/src/graphviz.c index 16ea8b9e..9da5f6e3 100644 --- a/src/graphviz.c +++ b/src/graphviz.c @@ -103,7 +103,7 @@ conclude_red (struct obstack *out, int source, rule_number ruleno, /* If no lookahead tokens were valid transitions, this reduction is actually hidden, so cancel everything. */ if (first) - return (void) obstack_finish0 (out); + (void) obstack_finish0 (out); else { char const *ed = enabled ? "" : "d";