multistart: check duplicates

Akim Demaille <[email protected]>
Newsgroups gmane.comp.parsers.bison.patches
Message-ID <[email protected]>
commit a157644ebb735dc519e675f2baec08b0efcebce6
Author: Akim Demaille <[email protected]>
Date:   Sun Nov 22 11:19:48 2020 +0100

    multistart: check duplicates
    
    * src/symlist.h, src/symlist.c (symbol_list_find_symbol)
    (symbol_list_last): New.
    (symbol_list_append): Use symbol_list_last.
    * src/reader.c (grammar_start_symbols_add): Check and discard duplicates.
    * tests/input.at (Duplicate %start symbol): New.
    * tests/reduce.at (Bad start symbols): Add the multistart keyword.

diff --git a/src/reader.c b/src/reader.c
index 17038b42..b4b5527a 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -55,7 +55,34 @@ bool default_prec = true;
 void
 grammar_start_symbols_add (symbol_list *syms)
 {
-  start_symbols = symbol_list_append (start_symbols, syms);
+  /* Report and ignore duplicates.  Append the others to START_SYMBOLS.  */
+  symbol_list *last = symbol_list_last (start_symbols);
+  for (symbol_list *l = syms; l && l->content.sym; /* nothing */)
+    {
+      /* Is there a previous definition?  */
+      symbol_list *first = symbol_list_find_symbol (start_symbols, l->content.sym);
+      if (first)
+        {
+          duplicate_directive ("%start", first->sym_loc, l->sym_loc);
+          l = l->next;
+        }
+      else
+        {
+          if (last)
+            {
+              last->next = l;
+              last = l;
+            }
+          else
+            {
+              last = l;
+              start_symbols = last;
+            }
+          symbol_list *next = l->next;
+          l->next = NULL;
+          l = next;
+        }
+    }
 }
 
 
diff --git a/src/symlist.c b/src/symlist.c
index f3bc12bc..1e899646 100644
--- a/src/symlist.c
+++ b/src/symlist.c
@@ -91,6 +91,16 @@ symbol_list_type_set (symbol_list *syms, uniqstr type_name)
 }
 
 
+symbol_list *
+symbol_list_find_symbol (symbol_list *l, const symbol *sym)
+{
+  for (/* Nothing. */; l && l->content.sym; l = l->next)
+    if (l->content.sym == sym)
+      return l;
+  return NULL;
+}
+
+
 /*-----------------------------------------------------------------------.
 | Print this list, for which every content_type must be SYMLIST_SYMBOL.  |
 `-----------------------------------------------------------------------*/
@@ -129,23 +139,29 @@ symbol_list_prepend (symbol_list *list, symbol_list *node)
 }
 
 
-/*-------------------------.
-| Append NODE to the LIST. |
-`-------------------------*/
-
 symbol_list *
-symbol_list_append (symbol_list *list, symbol_list *node)
+symbol_list_last (symbol_list *list)
 {
   if (!list)
-    return node;
+    return NULL;
   symbol_list *next = list;
   while (next->next)
     next = next->next;
-  next->next = node;
+  return next;
+}
+
+symbol_list *
+symbol_list_append (symbol_list *list, symbol_list *node)
+{
+  if (list)
+    symbol_list_last (list)->next = node;
+  else
+    list = node;
   return list;
 }
 
 
+
 /*-----------------------------------------------.
 | Free the LIST, but not the items it contains.  |
 `-----------------------------------------------*/
diff --git a/src/symlist.h b/src/symlist.h
index 39fdec40..6a3a4d53 100644
--- a/src/symlist.h
+++ b/src/symlist.h
@@ -112,6 +112,9 @@ symbol_list *symbol_list_type_new (uniqstr type_name, location loc);
  ** \returns \c syms */
 symbol_list *symbol_list_type_set (symbol_list *syms, uniqstr type_name);
 
+/** Find a symbol with the same content as \c sym within \c syms.  */
+symbol_list *symbol_list_find_symbol (symbol_list *syms, const symbol *sym);
+
 /** Print this list.
 
   \pre For every node \c n in the list, <tt>n->content_type =
@@ -121,6 +124,9 @@ void symbol_list_syms_print (const symbol_list *l, FILE *f);
 /** Prepend \c node to \c list.  */
 symbol_list *symbol_list_prepend (symbol_list *list, symbol_list *node);
 
+/** The last node of this list. */
+symbol_list *symbol_list_last (symbol_list *list);
+
 /** Append \c node to \c list.  */
 symbol_list *symbol_list_append (symbol_list *list, symbol_list *node);
 
@@ -136,11 +142,11 @@ int symbol_list_length (symbol_list const *l);
  **/
 symbol_list *symbol_list_n_get (symbol_list *l, int n);
 
-/* Get the data type (alternative in the union) of the value for
-   symbol N in rule RULE.  */
+/** Get the data type (alternative in the union) of the value for
+    symbol N in rule RULE.  */
 uniqstr symbol_list_n_type_name_get (symbol_list *l, int n);
 
-/* Check whether the node is a border element of a rule. */
+/** Check whether the node is a border element of a rule. */
 bool symbol_list_null (symbol_list *node);
 
 /** Set the \c \%destructor or \c \%printer for \c node as \c cprops.  */
diff --git a/tests/input.at b/tests/input.at
index 8cf58914..26f52cf7 100644
--- a/tests/input.at
+++ b/tests/input.at
@@ -1814,6 +1814,75 @@ AT_BISON_CHECK([-o input.c input.y])
 AT_CLEANUP
 
 
+## ------------------------- ##
+## Duplicate %start symbol.  ##
+## ------------------------- ##
+
+AT_SETUP([Duplicate %start symbol])
+
+AT_KEYWORDS([multistart])
+
+AT_DATA([input.y],
+[[%start exp exp exp
+%%
+exp: %empty;
+]])
+
+AT_BISON_CHECK([-fcaret input.y], [0], [],
+[[input.y:1.12-14: warning: duplicate directive [-Wother]
+    1 | %start exp exp exp
+      |            ^~~
+input.y:1.8-10: note: previous declaration
+    1 | %start exp exp exp
+      |        ^~~
+input.y:1.16-18: warning: duplicate directive [-Wother]
+    1 | %start exp exp exp
+      |                ^~~
+input.y:1.8-10: note: previous declaration
+    1 | %start exp exp exp
+      |        ^~~
+input.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
+]])
+
+AT_DATA([input.y],
+[[%start exp foo exp
+%%
+exp: foo;
+foo: %empty;
+]])
+
+AT_BISON_CHECK([-fcaret input.y], [0], [],
+[[input.y:1.16-18: warning: duplicate directive [-Wother]
+    1 | %start exp foo exp
+      |                ^~~
+input.y:1.8-10: note: previous declaration
+    1 | %start exp foo exp
+      |        ^~~
+input.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
+]])
+
+AT_DATA([input.y],
+[[%start exp foo
+%start exp
+%%
+exp: foo;
+foo: %empty;
+]])
+
+AT_BISON_CHECK([-fcaret input.y], [0], [],
+[[input.y:2.8-10: warning: duplicate directive [-Wother]
+    2 | %start exp
+      |        ^~~
+input.y:1.8-10: note: previous declaration
+    1 | %start exp foo
+      |        ^~~
+input.y: warning: fix-its can be applied.  Rerun with option '--update'. [-Wother]
+]])
+
+
+AT_CLEANUP
+
+
 ## --------------------- ##
 ## %prec takes a token.  ##
 ## --------------------- ##
diff --git a/tests/reduce.at b/tests/reduce.at
index f9d59a26..daaf3378 100644
--- a/tests/reduce.at
+++ b/tests/reduce.at
@@ -452,7 +452,7 @@ AT_CLEANUP
 AT_SETUP([Bad start symbols])
 
 m4_pushdef([AT_TEST],
-[
+[AT_BISON_OPTION_PUSHDEFS([$1])
 AT_DATA([[input.y]],
 [%%
 $1
@@ -461,6 +461,7 @@ $1
 AT_BISON_CHECK([[input.y]], 1, [],
 [$2
 ])
+AT_BISON_OPTION_POPDEFS([$1])
 ])
 
 AT_TEST(
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.