]> git.saurik.com Git - bison.git/blobdiff - tests/conflicts.at
Merge remote-tracking branch 'origin/maint'
[bison.git] / tests / conflicts.at
index 599d708b54486e24b232b6026f21e2fafcd59788..8b04449c732998197477f2fc2ebace711c6a66ba 100644 (file)
@@ -37,7 +37,7 @@ e: 'e' | /* Nothing. */;
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 0, [],
-[[input.y:4.9: warning: rule useless in parser due to conflicts: e: /* empty */
+[[input.y:4.9: warning: rule useless in parser due to conflicts: e: /* empty */ [-Wother]
 ]])
 
 AT_CLEANUP
@@ -49,20 +49,17 @@ AT_CLEANUP
 
 AT_SETUP([%nonassoc and eof])
 
+AT_BISON_OPTION_PUSHDEFS
 AT_DATA_GRAMMAR([input.y],
 [[
 %{
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 
 #define YYERROR_VERBOSE 1
-static void
-yyerror (const char *msg)
-{
-  fprintf (stderr, "%s\n", msg);
-}
-
+]AT_YYERROR_DEFINE[
 /* The current argument. */
 static const char *input;
 
@@ -70,8 +67,7 @@ static int
 yylex (void)
 {
   static size_t toknum;
-  if (! (toknum <= strlen (input)))
-    abort ();
+  assert (toknum <= strlen (input));
   return input[toknum++];
 }
 
@@ -92,6 +88,7 @@ main (int argc, const char *argv[])
   return yyparse ();
 }
 ]])
+AT_BISON_OPTION_POPDEFS
 
 m4_pushdef([AT_NONASSOC_AND_EOF_CHECK],
 [AT_BISON_CHECK([$1[ -o input.c input.y]])
@@ -120,10 +117,10 @@ AT_NONASSOC_AND_EOF_CHECK([], [[incorrect]])
 
 # We must disable default reductions in inconsistent states in order to
 # have an explicit list of all expected tokens.
-AT_NONASSOC_AND_EOF_CHECK([[-Dlr.default-reductions=consistent]],
+AT_NONASSOC_AND_EOF_CHECK([[-Dlr.default-reduction=consistent]],
                           [[correct]])
 
-# lr.default-reductions=consistent happens to work for this test case.
+# lr.default-reduction=consistent happens to work for this test case.
 # However, for other grammars, lookahead sets can be merged for
 # different left contexts, so it is still possible to have an incorrect
 # expected list.  Canonical LR is almost a general solution (that is, it
@@ -169,7 +166,7 @@ AT_SKEL_JAVA_IF([AT_DATA], [AT_DATA_GRAMMAR])([input.y],
   #include <string>]], [[
   #include <assert.h>
   #include <stdio.h>
-  void yyerror (char const *msg);]])[
+  ]AT_YYERROR_DECLARE])[
   ]AT_YYLEX_PROTOTYPE[;
   #define USE(Var)
 }
@@ -210,31 +207,11 @@ public Object getLVal ()
   *lvalp = 1;
   return *input++;
 }]])[
-
-/*----------.
-| yyerror.  |
-`----------*/]AT_SKEL_JAVA_IF([[
-
-public void yyerror (String msg)
-{
-  System.err.println (msg);
-}
-
+]AT_YYERROR_DEFINE[
+]AT_SKEL_JAVA_IF([[
 };
 
-%%]], [AT_SKEL_CC_IF([[
-
-void
-yy::parser::error (std::string const &msg)
-{
-  std::cerr << msg << std::endl;
-}]], [[
-
-void
-yyerror (char const *msg)
-{
-  fprintf (stderr, "%s\n", msg);
-}]])])[
+%%]])[
 
 /*-------.
 | main.  |
@@ -333,12 +310,12 @@ AT_CONSISTENT_ERRORS_CHECK([[%define lr.type ielr
 
 # Even canonical LR doesn't foresee the error for 'a'!
 AT_CONSISTENT_ERRORS_CHECK([[%define lr.type ielr
-                             %define lr.default-reductions consistent]],
+                             %define lr.default-reduction consistent]],
                            [AT_PREVIOUS_STATE_GRAMMAR],
                            [AT_PREVIOUS_STATE_INPUT],
                            [[$end]], [[ab]])
 AT_CONSISTENT_ERRORS_CHECK([[%define lr.type ielr
-                             %define lr.default-reductions accepting]],
+                             %define lr.default-reduction accepting]],
                            [AT_PREVIOUS_STATE_GRAMMAR],
                            [AT_PREVIOUS_STATE_INPUT],
                            [[$end]], [[ab]])
@@ -419,13 +396,13 @@ AT_CONSISTENT_ERRORS_CHECK([[%glr-parser]],
                            [['b']], [[none]])
 # No C++ or Java test because yychar cannot be manipulated by users.
 
-AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reductions consistent]],
+AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reduction consistent]],
                            [AT_USER_ACTION_GRAMMAR],
                            [AT_USER_ACTION_INPUT],
                            [['b']], [[none]])
 
 # Canonical LR doesn't foresee the error for 'a'!
-AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reductions accepting]],
+AT_CONSISTENT_ERRORS_CHECK([[%define lr.default-reduction accepting]],
                            [AT_USER_ACTION_GRAMMAR],
                            [AT_USER_ACTION_INPUT],
                            [[$end]], [[a]])
@@ -439,7 +416,7 @@ AT_CONSISTENT_ERRORS_CHECK([[%define parse.lac full]],
                            [AT_USER_ACTION_INPUT],
                            [['b']], [[none]])
 AT_CONSISTENT_ERRORS_CHECK([[%define parse.lac full
-                             %define lr.default-reductions accepting]],
+                             %define lr.default-reduction accepting]],
                            [AT_USER_ACTION_GRAMMAR],
                            [AT_USER_ACTION_INPUT],
                            [[$end]], [[none]])
@@ -465,12 +442,12 @@ AT_CLEANUP
 # with minimal LR parser tables.
 
 AT_SETUP([[LAC: %nonassoc requires splitting canonical LR states]])
-
+AT_BISON_OPTION_PUSHDEFS
 AT_DATA_GRAMMAR([[input.y]],
 [[%code {
   #include <stdio.h>
-  void yyerror (char const *);
-  int yylex (void);
+  ]AT_YYERROR_DECLARE[
+  ]AT_YYLEX_DECLARE[
 }
 
 %error-verbose
@@ -507,19 +484,8 @@ look:
 reduce-nonassoc: %prec 'a';
 
 %%
-
-void
-yyerror (char const *msg)
-{
-  fprintf (stderr, "%s\n", msg);
-}
-
-int
-yylex (void)
-{
-  char const *input = "aaa";
-  return *input++;
-}
+]AT_YYERROR_DEFINE[
+]AT_YYLEX_DEFINE(["aaa"])[
 
 int
 main (void)
@@ -527,11 +493,12 @@ main (void)
   return yyparse ();
 }
 ]])
+AT_BISON_OPTION_POPDEFS
 
 # Show canonical LR's failure.
 AT_BISON_CHECK([[-Dlr.type=canonical-lr -o input.c input.y]],
                [[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
 ]])
 AT_COMPILE([[input]])
 AT_PARSER_CHECK([[./input]], [[1]], [[]],
@@ -541,7 +508,7 @@ AT_PARSER_CHECK([[./input]], [[1]], [[]],
 # It's corrected by LAC.
 AT_BISON_CHECK([[-Dlr.type=canonical-lr -Dparse.lac=full \
                  -o input.c input.y]], [[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
 ]])
 AT_COMPILE([[input]])
 AT_PARSER_CHECK([[./input]], [[1]], [[]],
@@ -551,7 +518,7 @@ AT_PARSER_CHECK([[./input]], [[1]], [[]],
 # IELR is sufficient when LAC is used.
 AT_BISON_CHECK([[-Dlr.type=ielr -Dparse.lac=full -o input.c input.y]],
                [[0]], [[]],
-[[input.y: conflicts: 2 shift/reduce
+[[input.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
 ]])
 AT_COMPILE([[input]])
 AT_PARSER_CHECK([[./input]], [[1]], [[]],
@@ -575,8 +542,8 @@ exp: exp OP exp | NUM;
 ]])
 
 AT_BISON_CHECK([-o input.c --report=all input.y], 0, [],
-[input.y: conflicts: 1 shift/reduce
-])
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+]])
 
 # Check the contents of the report.
 AT_CHECK([cat input.output], [],
@@ -607,7 +574,7 @@ exp (6)
     on left: 1 2, on right: 0 1
 
 
-state 0
+State 0
 
     0 $accept: . exp $end
     1 exp: . exp OP exp
@@ -618,14 +585,14 @@ state 0
     exp  go to state 2
 
 
-state 1
+State 1
 
     2 exp: NUM .
 
     $default  reduce using rule 2 (exp)
 
 
-state 2
+State 2
 
     0 $accept: exp . $end
     1 exp: exp . OP exp
@@ -634,14 +601,14 @@ state 2
     OP    shift, and go to state 4
 
 
-state 3
+State 3
 
     0 $accept: exp $end .
 
     $default  accept
 
 
-state 4
+State 4
 
     1 exp: . exp OP exp
     1    | exp OP . exp
@@ -652,7 +619,7 @@ state 4
     exp  go to state 5
 
 
-state 5
+State 5
 
     1 exp: exp . OP exp
     1    | exp OP exp .  [$end, OP]
@@ -710,7 +677,7 @@ exp (6)
     on left: 1 2, on right: 0 1
 
 
-state 0
+State 0
 
     0 $accept: . exp $end
     1 exp: . exp OP exp
@@ -721,14 +688,14 @@ state 0
     exp  go to state 2
 
 
-state 1
+State 1
 
     2 exp: NUM .
 
     $default  reduce using rule 2 (exp)
 
 
-state 2
+State 2
 
     0 $accept: exp . $end
     1 exp: exp . OP exp
@@ -737,14 +704,14 @@ state 2
     OP    shift, and go to state 4
 
 
-state 3
+State 3
 
     0 $accept: exp $end .
 
     $default  accept
 
 
-state 4
+State 4
 
     1 exp: . exp OP exp
     1    | exp OP . exp
@@ -755,7 +722,7 @@ state 4
     exp  go to state 5
 
 
-state 5
+State 5
 
     1 exp: exp . OP exp
     1    | exp OP exp .  [$end, OP]
@@ -817,8 +784,8 @@ cond:
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 0, [],
-[[input.y: conflicts: 1 shift/reduce
-input.y:12.3-18: warning: rule useless in parser due to conflicts: cond: cond "then" cond
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+input.y:12.3-18: warning: rule useless in parser due to conflicts: cond: cond "then" cond [-Wother]
 ]])
 
 AT_CLEANUP
@@ -861,8 +828,8 @@ id : '0';
 ]])
 
 AT_BISON_CHECK([-o input.c --report=all input.y], 0, [],
-[[input.y: conflicts: 1 reduce/reduce
-input.y:4.6-8: warning: rule useless in parser due to conflicts: id: '0'
+[[input.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+input.y:4.6-8: warning: rule useless in parser due to conflicts: id: '0' [-Wother]
 ]])
 
 # Check the contents of the report.
@@ -906,7 +873,7 @@ id (7)
     on left: 4, on right: 2
 
 
-state 0
+State 0
 
     0 $accept: . exp $end
     1 exp: . num
@@ -921,7 +888,7 @@ state 0
     id   go to state 4
 
 
-state 1
+State 1
 
     3 num: '0' .  [$end]
     4 id: '0' .  [$end]
@@ -931,28 +898,28 @@ state 1
     $default  reduce using rule 3 (num)
 
 
-state 2
+State 2
 
     0 $accept: exp . $end
 
     $end  shift, and go to state 5
 
 
-state 3
+State 3
 
     1 exp: num .
 
     $default  reduce using rule 1 (exp)
 
 
-state 4
+State 4
 
     2 exp: id .
 
     $default  reduce using rule 2 (exp)
 
 
-state 5
+State 5
 
     0 $accept: exp $end .
 
@@ -978,9 +945,8 @@ exp: exp OP exp | NUM;
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 shift/reduce
-input.y: expected 0 shift/reduce conflicts
-])
+[[input.y: error: shift/reduce conflicts: 1 found, 0 expected
+]])
 AT_CLEANUP
 
 
@@ -1015,9 +981,8 @@ exp: exp OP exp | NUM;
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 shift/reduce
-input.y: expected 2 shift/reduce conflicts
-])
+[[input.y: error: shift/reduce conflicts: 1 found, 2 expected
+]])
 AT_CLEANUP
 
 
@@ -1035,9 +1000,8 @@ a: 'a';
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 1, [],
-[input.y: conflicts: 1 reduce/reduce
-input.y: expected 0 reduce/reduce conflicts
-])
+[[input.y: error: reduce/reduce conflicts: 1 found, 0 expected
+]])
 AT_CLEANUP
 
 
@@ -1079,7 +1043,7 @@ e:   e '+' e
 ]])
 
 AT_BISON_CHECK([-o input.c input.y], 0, [],
-[[input.y: conflicts: 4 shift/reduce
+[[input.y: warning: 4 shift/reduce conflicts [-Wconflicts-sr]
 ]])
 AT_CLEANUP
 
@@ -1181,14 +1145,15 @@ reported_conflicts:
 ]])
 
 AT_BISON_CHECK([[--report=all input.y]], 0, [],
-[[input.y: conflicts: 1 shift/reduce, 1 reduce/reduce
-input.y:12.5-20: warning: rule useless in parser due to conflicts: resolved_conflict: 'a' unreachable1
-input.y:20.5-20: warning: rule useless in parser due to conflicts: unreachable1: 'a' unreachable2
-input.y:21.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */
-input.y:25.13: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input.y:25.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input.y:31.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a'
-input.y:32.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */
+[[input.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+input.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+input.y:12.5-20: warning: rule useless in parser due to conflicts: resolved_conflict: 'a' unreachable1 [-Wother]
+input.y:20.5-20: warning: rule useless in parser due to conflicts: unreachable1: 'a' unreachable2 [-Wother]
+input.y:21.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */ [-Wother]
+input.y:25.13: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input.y:25.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input.y:31.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a' [-Wother]
+input.y:32.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */ [-Wother]
 ]])
 
 AT_CHECK([[cat input.output]], 0,
@@ -1253,7 +1218,7 @@ reported_conflicts (9)
     on left: 8 9 10, on right: 1
 
 
-state 0
+State 0
 
     0 $accept: . start $end
     1 start: . resolved_conflict 'a' reported_conflicts 'a'
@@ -1268,28 +1233,28 @@ state 0
     Conflict between rule 3 and token 'a' resolved as reduce (%left 'a').
 
 
-state 1
+State 1
 
     0 $accept: start . $end
 
     $end  shift, and go to state 3
 
 
-state 2
+State 2
 
     1 start: resolved_conflict . 'a' reported_conflicts 'a'
 
     'a'  shift, and go to state 4
 
 
-state 3
+State 3
 
     0 $accept: start $end .
 
     $default  accept
 
 
-state 4
+State 4
 
     1 start: resolved_conflict 'a' . reported_conflicts 'a'
     8 reported_conflicts: . 'a'
@@ -1303,7 +1268,7 @@ state 4
     reported_conflicts  go to state 6
 
 
-state 5
+State 5
 
     8 reported_conflicts: 'a' .  ['a']
     9                   | 'a' .  ['a']
@@ -1313,14 +1278,14 @@ state 5
     $default  reduce using rule 8 (reported_conflicts)
 
 
-state 6
+State 6
 
     1 start: resolved_conflict 'a' reported_conflicts . 'a'
 
     'a'  shift, and go to state 7
 
 
-state 7
+State 7
 
     1 start: resolved_conflict 'a' reported_conflicts 'a' .
 
@@ -1328,16 +1293,17 @@ state 7
 ]])
 
 AT_DATA([[input-keep.y]],
-[[%define lr.keep-unreachable-states
+[[%define lr.keep-unreachable-state
 ]])
 AT_CHECK([[cat input.y >> input-keep.y]])
 
 AT_BISON_CHECK([[input-keep.y]], 0, [],
-[[input-keep.y: conflicts: 2 shift/reduce, 2 reduce/reduce
-input-keep.y:22.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */
-input-keep.y:26.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */
-input-keep.y:32.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a'
-input-keep.y:33.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */
+[[input-keep.y: warning: 2 shift/reduce conflicts [-Wconflicts-sr]
+input-keep.y: warning: 2 reduce/reduce conflicts [-Wconflicts-rr]
+input-keep.y:22.4: warning: rule useless in parser due to conflicts: unreachable1: /* empty */ [-Wother]
+input-keep.y:26.16: warning: rule useless in parser due to conflicts: unreachable2: /* empty */ [-Wother]
+input-keep.y:32.5-7: warning: rule useless in parser due to conflicts: reported_conflicts: 'a' [-Wother]
+input-keep.y:33.4: warning: rule useless in parser due to conflicts: reported_conflicts: /* empty */ [-Wother]
 ]])
 
 AT_CLEANUP
@@ -1374,8 +1340,8 @@ empty_c2: %prec 'c' ;
 empty_c3: %prec 'd' ;
 ]])
 AT_BISON_CHECK([[--report=all -o input.c input.y]], 0, [], [ignore])
-AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
-[[state 0
+AT_CHECK([[cat input.output | sed -n '/^State 0$/,/^State 1$/p']], 0,
+[[State 0
 
     0 $accept: . start $end
     1 start: . 'a'
@@ -1411,7 +1377,7 @@ AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
     Conflict between rule 13 and token 'c' resolved as reduce ('c' < 'd').
 
 
-state 1
+State 1
 ]])
 
 AT_CLEANUP
@@ -1450,8 +1416,8 @@ empty_c3: %prec 'c' ;
 ]])
 
 AT_BISON_CHECK([[--report=all -o input.c input.y]], 0, [], [ignore])
-AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
-[[state 0
+AT_CHECK([[cat input.output | sed -n '/^State 0$/,/^State 1$/p']], 0,
+[[State 0
 
     0 $accept: . start $end
     1 start: . 'a'
@@ -1487,14 +1453,45 @@ AT_CHECK([[cat input.output | sed -n '/^state 0$/,/^state 1$/p']], 0,
     Conflict between rule 11 and token 'c' resolved as an error (%nonassoc 'c').
 
 
-state 1
+State 1
 ]])
 AT_CLEANUP
 
 
-## --------------------------------- ##
-## -W versus %expect and %expect-rr  ##
-## --------------------------------- ##
+## -------------------- ##
+## %expect-rr non GLR.  ##
+## -------------------- ##
+
+AT_SETUP([[%expect-rr non GLR]])
+
+AT_DATA([[1.y]],
+[[%expect-rr 0
+%%
+exp: 'a'
+]])
+
+AT_BISON_CHECK([[1.y]], [[0]], [],
+[[1.y: warning: %expect-rr applies only to GLR parsers [-Wother]
+]])
+
+AT_DATA([[2.y]],
+[[%expect-rr 1
+%%
+exp: 'a' | 'a';
+]])
+
+AT_BISON_CHECK([[2.y]], [[0]], [],
+[[2.y: warning: %expect-rr applies only to GLR parsers [-Wother]
+2.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
+2.y:3.12-14: warning: rule useless in parser due to conflicts: exp: 'a' [-Wother]
+]])
+
+AT_CLEANUP
+
+
+## ---------------------------------- ##
+## -W versus %expect and %expect-rr.  ##
+## ---------------------------------- ##
 
 AT_SETUP([[-W versus %expect and %expect-rr]])
 
@@ -1520,17 +1517,27 @@ B: ;
 ]])
 
 AT_BISON_CHECK([[sr-rr.y]], [[0]], [[]],
-[[sr-rr.y: conflicts: 1 shift/reduce, 1 reduce/reduce
+[[sr-rr.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
+sr-rr.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
 ]])
 AT_BISON_CHECK([[-Wno-conflicts-sr sr-rr.y]], [[0]], [[]],
-[[sr-rr.y: conflicts: 1 reduce/reduce
+[[sr-rr.y: warning: 1 reduce/reduce conflict [-Wconflicts-rr]
 ]])
 AT_BISON_CHECK([[-Wno-conflicts-rr sr-rr.y]], [[0]], [[]],
-[[sr-rr.y: conflicts: 1 shift/reduce
+[[sr-rr.y: warning: 1 shift/reduce conflict [-Wconflicts-sr]
 ]])
 
-[for gram in sr-rr sr rr; do
+[
+# This is piece of code is rather complex for a simple task: try every
+# combinaison of (0 or 1 real SR) x (0 or 1 real RR) x (don't %expect
+# or %expect 0, 1, or 2 SR) x (don't %expect-rr or %expect-rr 0, 1, or 2
+# RR).
+
+# Number and types of genuine conflicts in the grammar.
+for gram in sr-rr sr rr; do
+  # Number of expected s/r conflicts.
   for sr_exp_i in '' 0 1 2; do
+    # Number of expected r/r conflicts.
     for rr_exp_i in '' 0 1 2; do
       test -z "$sr_exp_i" && test -z "$rr_exp_i" && continue
 
@@ -1553,36 +1560,38 @@ AT_BISON_CHECK([[-Wno-conflicts-rr sr-rr.y]], [[0]], [[]],
       echo "$directives" > $file
       cat $gram.y >> $file
 
-      # Count actual conflicts.
-      conflicts=
-      sr_count=0
-      rr_count=0
-      if test $gram = sr || test $gram = sr-rr; then
-        conflicts="1 shift/reduce"
-        sr_count=1
-      fi
-      if test $gram = rr || test $gram = sr-rr; then
-        if test -n "$conflicts"; then
-          conflicts="$conflicts, "
-        fi
-        conflicts="${conflicts}1 reduce/reduce"
-        rr_count=1
-      fi
+      # Number of found conflicts.
+      case $gram in
+        (sr)    sr_count=1; rr_count=0;;
+        (rr)    sr_count=0; rr_count=1;;
+        (sr-rr) sr_count=1; rr_count=1;;
+      esac
+
+      # Update number of expected conflicts: if %expect is given then
+      # %expect-rr defaults to 0, and vice-versa.  Leave empty if
+      # nothing expected.
+      case $sr_exp_i:$rr_exp_i in
+        ?:) rr_exp_i=0;;
+        :?) sr_exp_i=0;;
+      esac
 
       # Run tests.
       if test $sr_count -eq $sr_exp && test $rr_count -eq $rr_exp; then
         ]AT_BISON_CHECK([[-Wnone $file]])[
         ]AT_BISON_CHECK([[-Werror $file]])[
       else
-        echo "$file: conflicts: $conflicts" > experr
-        if test $sr_count -ne $sr_exp; then
-          if test $sr_exp -ne 1; then s=s; else s= ; fi
-          echo "$file: expected $sr_exp shift/reduce conflict$s" >> experr
-        fi
-        if test $rr_count -ne $rr_exp; then
-          if test $rr_exp -ne 1; then s=s; else s= ; fi
-          echo "$file: expected $rr_exp reduce/reduce conflict$s" >> experr
-        fi
+        {
+          if test -z "$sr_exp_i" && test "$sr_count" -ne 0; then
+            echo "warning: $sr_count shift/reduce conflicts"
+          elif test "$sr_exp_i" -ne "$sr_count"; then
+            echo "error: shift/reduce conflicts: $sr_count found, $sr_exp_i expected"
+          fi
+          if test -z "$rr_exp_i" && test "$rr_count" -ne 0; then
+            echo "warning: $rr_count reduce/reduce conflicts"
+          elif test "$rr_exp_i" -ne "$rr_count"; then
+            echo "error: reduce/reduce conflicts: $rr_count found, $rr_exp_i expected"
+          fi
+        } | sed -e "s/^/$file: /" > experr
         ]AT_BISON_CHECK([[-Wnone $file]], [[1]], [[]], [[experr]])[
         ]AT_BISON_CHECK([[-Werror $file]], [[1]], [[]], [[experr]])[
       fi