mirror of
https://git.savannah.gnu.org/git/bison.git
synced 2026-03-09 04:13:03 +00:00
Warn about unused values.
* src/symlist.h, src/symlist.c (symbol_list, symbol_list_new): Add
a `used' member.
(symbol_list_n_get, symbol_list_n_used_set): New.
(symbol_list_n_type_name_get): Use symbol_list_n_get.
* src/scan-gram.l (handle_action_dollar): Flag used symbols.
* src/reader.c (grammar_current_rule_check): Check that values are
used.
* src/symtab.c (symbol_print): Accept 0.
* tests/existing.at: Remove the type information.
Empty the actions.
Remove useless actions (beware of mid-rule actions: perl -000
-pi -e 's/s*{}(?=[ns]*[|;])//g').
* tests/actions.at (Exotic Dollars): Use unused values.
* tests/calc.at: Likewise.
* tests/glr-regression.at (No users destructors if stack 0 deleted):
Likewise.
* src/gram.c (rule_useful_p, rule_never_reduced_p): Use
rule_useful_p.
This commit is contained in:
23
ChangeLog
23
ChangeLog
@@ -1,3 +1,26 @@
|
||||
2005-12-22 Akim Demaille <akim@epita.fr>
|
||||
|
||||
Warn about unused values.
|
||||
* src/symlist.h, src/symlist.c (symbol_list, symbol_list_new): Add
|
||||
a `used' member.
|
||||
(symbol_list_n_get, symbol_list_n_used_set): New.
|
||||
(symbol_list_n_type_name_get): Use symbol_list_n_get.
|
||||
* src/scan-gram.l (handle_action_dollar): Flag used symbols.
|
||||
* src/reader.c (grammar_current_rule_check): Check that values are
|
||||
used.
|
||||
* src/symtab.c (symbol_print): Accept 0.
|
||||
* tests/existing.at: Remove the type information.
|
||||
Empty the actions.
|
||||
Remove useless actions (beware of mid-rule actions: perl -000
|
||||
-pi -e 's/\s*\{\}(?=[\n\s]*[|;])//g').
|
||||
* tests/actions.at (Exotic Dollars): Use unused values.
|
||||
* tests/calc.at: Likewise.
|
||||
* tests/glr-regression.at (No users destructors if stack 0 deleted):
|
||||
Likewise.
|
||||
|
||||
* src/gram.c (rule_useful_p, rule_never_reduced_p): Use
|
||||
rule_useful_p.
|
||||
|
||||
2005-12-21 Paul Eggert <eggert@cs.ucla.edu>
|
||||
|
||||
Undo 2005-12-01 tentative license wording change. The wording is
|
||||
|
||||
18
NEWS
18
NEWS
@@ -3,6 +3,24 @@ Bison News
|
||||
|
||||
Changes in version 2.1a:
|
||||
|
||||
* New warning: unused values
|
||||
Typed right-hand side symbols whose value are not used are reported.
|
||||
For instance
|
||||
|
||||
exp: exp "?" exp ":" exp { $$ = $1 + $3; }
|
||||
| exp "+" exp
|
||||
;
|
||||
|
||||
will trigger a warning about $5 of the first rule, and $3 in the
|
||||
second ($1 is copied to $$ by the default rule). To avoid this
|
||||
warning, let Bison believe the value is used, e.g.
|
||||
|
||||
exp: exp "?" exp ":" exp { $$ = $1 + $3; $5; }
|
||||
| exp "+" exp { $$ = $1; $3; }
|
||||
|
||||
This helps catching lost values and memory leaks: if a value is
|
||||
ignored, its associated memory will never be reclaimed.
|
||||
|
||||
* %destructor vs. YYABORT, YYACCEPT, and YYERROR.
|
||||
Destructors are now called when user code invokes YYABORT, YYACCEPT,
|
||||
and YYERROR, for all objects on the stack, other than objects
|
||||
|
||||
@@ -65,7 +65,7 @@ rule_useful_p (rule *r)
|
||||
bool
|
||||
rule_useless_p (rule *r)
|
||||
{
|
||||
return r->number >= nrules;
|
||||
return !rule_useful_p (r);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ rule_useless_p (rule *r)
|
||||
bool
|
||||
rule_never_reduced_p (rule *r)
|
||||
{
|
||||
return !r->useful && r->number < nrules;
|
||||
return !r->useful && rule_useful_p (r);
|
||||
}
|
||||
|
||||
|
||||
@@ -317,8 +317,7 @@ grammar_rules_never_reduced_report (const char *message)
|
||||
if (!rules[r].useful)
|
||||
{
|
||||
location_print (stderr, rules[r].location);
|
||||
fprintf (stderr, ": %s: %s: ",
|
||||
_("warning"), message);
|
||||
fprintf (stderr, ": %s: %s: ", _("warning"), message);
|
||||
rule_print (&rules[r], stderr);
|
||||
}
|
||||
}
|
||||
|
||||
77
src/reader.c
77
src/reader.c
@@ -40,7 +40,7 @@ static symbol_list *grammar = NULL;
|
||||
static bool start_flag = false;
|
||||
merger_list *merge_functions;
|
||||
|
||||
/* Has %union been seen? */
|
||||
/* Was %union seen? */
|
||||
bool typed = false;
|
||||
|
||||
/* Should rules have a default precedence? */
|
||||
@@ -104,7 +104,7 @@ get_merge_function (uniqstr name, uniqstr type, location loc)
|
||||
type = uniqstr_new ("");
|
||||
|
||||
head.next = merge_functions;
|
||||
for (syms = &head, n = 1; syms->next != NULL; syms = syms->next, n += 1)
|
||||
for (syms = &head, n = 1; syms->next; syms = syms->next, n += 1)
|
||||
if (UNIQSTR_EQ (name, syms->next->name))
|
||||
break;
|
||||
if (syms->next == NULL)
|
||||
@@ -128,11 +128,8 @@ get_merge_function (uniqstr name, uniqstr type, location loc)
|
||||
void
|
||||
free_merger_functions (void)
|
||||
{
|
||||
merger_list *L0;
|
||||
if (! glr_parser)
|
||||
return;
|
||||
L0 = merge_functions;
|
||||
while (L0 != NULL)
|
||||
merger_list *L0 = merge_functions;
|
||||
while (L0)
|
||||
{
|
||||
merger_list *L1 = L0->next;
|
||||
free (L0);
|
||||
@@ -150,13 +147,6 @@ free_merger_functions (void)
|
||||
| |
|
||||
| All actions are copied out, labelled by the rule number they apply |
|
||||
| to. |
|
||||
| |
|
||||
| Bison used to allow some %directives in the rules sections, but |
|
||||
| this is no longer consider appropriate: (i) the documented grammar |
|
||||
| doesn't claim it, (ii), it would promote bad style, (iii), error |
|
||||
| recovery for %directives consists in skipping the junk until a `%' |
|
||||
| is seen and helrp synchronizing. This scheme is definitely wrong |
|
||||
| in the rules section. |
|
||||
`-------------------------------------------------------------------*/
|
||||
|
||||
/* The (currently) last symbol of GRAMMAR. */
|
||||
@@ -206,7 +196,6 @@ grammar_rule_begin (symbol *lhs, location loc)
|
||||
current_rule = grammar_end;
|
||||
|
||||
/* Mark the rule's lhs as a nonterminal if not already so. */
|
||||
|
||||
if (lhs->class == unknown_sym)
|
||||
{
|
||||
lhs->class = nterm_sym;
|
||||
@@ -217,39 +206,55 @@ grammar_rule_begin (symbol *lhs, location loc)
|
||||
complain_at (loc, _("rule given for %s, which is a token"), lhs->tag);
|
||||
}
|
||||
|
||||
/* Check that the last rule (CURRENT_RULE) is properly defined. For
|
||||
instance, there should be no type clash on the default action. */
|
||||
|
||||
/*------------------------------------------------------------------.
|
||||
| Check that the last rule (CURRENT_RULE) is properly defined. For |
|
||||
| instance, there should be no type clash on the default action. |
|
||||
`------------------------------------------------------------------*/
|
||||
|
||||
static void
|
||||
grammar_current_rule_check (void)
|
||||
{
|
||||
symbol *lhs = current_rule->sym;
|
||||
char const *lhs_type = lhs->type_name;
|
||||
symbol *first_rhs = current_rule->next->sym;
|
||||
|
||||
/* If there is an action, then there is nothing we can do: the user
|
||||
is allowed to shoot herself in the foot. */
|
||||
if (current_rule->action)
|
||||
return;
|
||||
/* Type check.
|
||||
|
||||
/* Don't worry about the default action if $$ is untyped, since $$'s
|
||||
If there is an action, then there is nothing we can do: the user
|
||||
is allowed to shoot herself in the foot.
|
||||
|
||||
Don't worry about the default action if $$ is untyped, since $$'s
|
||||
value can't be used. */
|
||||
if (! lhs_type)
|
||||
return;
|
||||
|
||||
/* If $$ is being set in default way, report if any type mismatch. */
|
||||
if (first_rhs)
|
||||
if (!current_rule->action && lhs_type)
|
||||
{
|
||||
const char *rhs_type = first_rhs->type_name ? first_rhs->type_name : "";
|
||||
if (!UNIQSTR_EQ (lhs_type, rhs_type))
|
||||
symbol *first_rhs = current_rule->next->sym;
|
||||
/* If $$ is being set in default way, report if any type mismatch. */
|
||||
if (first_rhs)
|
||||
{
|
||||
const char *rhs_type =
|
||||
first_rhs->type_name ? first_rhs->type_name : "";
|
||||
if (!UNIQSTR_EQ (lhs_type, rhs_type))
|
||||
warn_at (current_rule->location,
|
||||
_("type clash on default action: <%s> != <%s>"),
|
||||
lhs_type, rhs_type);
|
||||
}
|
||||
/* Warn if there is no default for $$ but we need one. */
|
||||
else
|
||||
warn_at (current_rule->location,
|
||||
_("type clash on default action: <%s> != <%s>"),
|
||||
lhs_type, rhs_type);
|
||||
_("empty rule for typed nonterminal, and no action"));
|
||||
}
|
||||
|
||||
/* Check that all the symbol values are used. */
|
||||
if (typed)
|
||||
{
|
||||
symbol_list *l = current_rule;
|
||||
int n = 1;
|
||||
for (l = current_rule->next; l && l->sym; l = l->next, ++n)
|
||||
/* The default action `uses' $1. */
|
||||
if (! (!current_rule->action && n == 1)
|
||||
&& l->sym->type_name && !l->used)
|
||||
warn_at (current_rule->location, _("unused value: $%d"), n);
|
||||
}
|
||||
/* Warn if there is no default for $$ but we need one. */
|
||||
else
|
||||
warn_at (current_rule->location,
|
||||
_("empty rule for typed nonterminal, and no action"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,10 @@ void free_merger_functions (void);
|
||||
|
||||
extern merger_list *merge_functions;
|
||||
|
||||
/* Was %union seen? */
|
||||
extern bool typed;
|
||||
|
||||
/* Should rules have a default precedence? */
|
||||
extern bool default_prec;
|
||||
|
||||
#endif /* !READER_H_ */
|
||||
|
||||
@@ -825,9 +825,9 @@ handle_action_dollar (char *text, location loc)
|
||||
if (INT_MIN <= num && num <= rule_length && ! get_errno ())
|
||||
{
|
||||
int n = num;
|
||||
if (1-n > max_left_semantic_context)
|
||||
max_left_semantic_context = 1-n;
|
||||
if (!type_name && n > 0)
|
||||
if (max_left_semantic_context < 1 - n)
|
||||
max_left_semantic_context = 1 - n;
|
||||
if (!type_name && 0 < n)
|
||||
type_name = symbol_list_n_type_name_get (current_rule, loc, n);
|
||||
if (!type_name && typed)
|
||||
complain_at (loc, _("$%d of `%s' has no declared type"),
|
||||
@@ -837,6 +837,8 @@ handle_action_dollar (char *text, location loc)
|
||||
obstack_fgrow3 (&obstack_for_string,
|
||||
"]b4_rhs_value(%d, %d, [%s])[",
|
||||
rule_length, n, type_name);
|
||||
if (typed)
|
||||
symbol_list_n_used_set (current_rule, n, true);
|
||||
}
|
||||
else
|
||||
complain_at (loc, _("integer out of range: %s"), quote (text));
|
||||
|
||||
@@ -34,13 +34,19 @@ symbol_list *
|
||||
symbol_list_new (symbol *sym, location loc)
|
||||
{
|
||||
symbol_list *res = xmalloc (sizeof *res);
|
||||
res->next = NULL;
|
||||
|
||||
res->sym = sym;
|
||||
res->location = loc;
|
||||
|
||||
res->action = NULL;
|
||||
res->used = false;
|
||||
|
||||
res->ruleprec = NULL;
|
||||
res->dprec = 0;
|
||||
res->merger = 0;
|
||||
|
||||
res->next = NULL;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -56,7 +62,7 @@ symbol_list_print (symbol_list *l, FILE *f)
|
||||
{
|
||||
symbol_print (l->sym, f);
|
||||
if (l && l->sym)
|
||||
fputc (' ', f);
|
||||
fprintf (f, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,43 +96,64 @@ symbol_list_free (symbol_list *list)
|
||||
`--------------------*/
|
||||
|
||||
unsigned int
|
||||
symbol_list_length (symbol_list *list)
|
||||
symbol_list_length (symbol_list *l)
|
||||
{
|
||||
int res = 0;
|
||||
for (/* Nothing. */; list; list = list->next)
|
||||
for (/* Nothing. */; l; l = l->next)
|
||||
++res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------.
|
||||
| Get the data type (alternative in the union) of the value for |
|
||||
| symbol N in symbol list RP. |
|
||||
`--------------------------------------------------------------*/
|
||||
/*--------------------------------.
|
||||
| Get symbol N in symbol list L. |
|
||||
`--------------------------------*/
|
||||
|
||||
uniqstr
|
||||
symbol_list_n_type_name_get (symbol_list *rp, location loc, int n)
|
||||
symbol_list *
|
||||
symbol_list_n_get (symbol_list *l, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (n < 0)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
l = l->next;
|
||||
if (l == NULL || l->sym == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
/*--------------------------------------------------------------.
|
||||
| Get the data type (alternative in the union) of the value for |
|
||||
| symbol N in symbol list L. |
|
||||
`--------------------------------------------------------------*/
|
||||
|
||||
uniqstr
|
||||
symbol_list_n_type_name_get (symbol_list *l, location loc, int n)
|
||||
{
|
||||
l = symbol_list_n_get (l, n);
|
||||
if (!l)
|
||||
{
|
||||
complain_at (loc, _("invalid $ value: $%d"), n);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
|
||||
while (i < n)
|
||||
{
|
||||
rp = rp->next;
|
||||
if (rp == NULL || rp->sym == NULL)
|
||||
{
|
||||
complain_at (loc, _("invalid $ value: $%d"), n);
|
||||
return NULL;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
return rp->sym->type_name;
|
||||
return l->sym->type_name;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------.
|
||||
| The symbol N in symbol list L is USED. |
|
||||
`----------------------------------------*/
|
||||
|
||||
void
|
||||
symbol_list_n_used_set (symbol_list *l, int n, bool used)
|
||||
{
|
||||
l = symbol_list_n_get (l, n);
|
||||
if (l)
|
||||
l->used = used;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,10 @@
|
||||
# include "location.h"
|
||||
# include "symtab.h"
|
||||
|
||||
/* A list of symbols, used during the parsing to store the rules. */
|
||||
typedef struct symbol_list
|
||||
{
|
||||
struct symbol_list *next;
|
||||
/* The symbol. */
|
||||
symbol *sym;
|
||||
location location;
|
||||
|
||||
@@ -35,9 +36,16 @@ typedef struct symbol_list
|
||||
const char *action;
|
||||
location action_location;
|
||||
|
||||
/* Whether this symbol's value is used in the current action. */
|
||||
bool used;
|
||||
|
||||
/* Precedence/associativity. */
|
||||
symbol *ruleprec;
|
||||
int dprec;
|
||||
int merger;
|
||||
|
||||
/* The list. */
|
||||
struct symbol_list *next;
|
||||
} symbol_list;
|
||||
|
||||
|
||||
@@ -48,18 +56,24 @@ symbol_list *symbol_list_new (symbol *sym, location loc);
|
||||
void symbol_list_print (symbol_list *l, FILE *f);
|
||||
|
||||
/* Prepend SYM at LOC to the LIST. */
|
||||
symbol_list *symbol_list_prepend (symbol_list *list,
|
||||
symbol_list *symbol_list_prepend (symbol_list *l,
|
||||
symbol *sym,
|
||||
location loc);
|
||||
|
||||
/* Free the LIST, but not the symbols it contains. */
|
||||
void symbol_list_free (symbol_list *list);
|
||||
void symbol_list_free (symbol_list *l);
|
||||
|
||||
/* Return its length. */
|
||||
unsigned int symbol_list_length (symbol_list *list);
|
||||
unsigned int symbol_list_length (symbol_list *l);
|
||||
|
||||
/* Get symbol N in symbol list 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. */
|
||||
uniqstr symbol_list_n_type_name_get (symbol_list *rp, location loc, int n);
|
||||
uniqstr symbol_list_n_type_name_get (symbol_list *l, location loc, int n);
|
||||
|
||||
/* The symbol N in symbol list L is USED. */
|
||||
void symbol_list_n_used_set (symbol_list *l, int n, bool used);
|
||||
|
||||
#endif /* !SYMLIST_H_ */
|
||||
|
||||
13
src/symtab.c
13
src/symtab.c
@@ -85,10 +85,15 @@ symbol_new (uniqstr tag, location loc)
|
||||
void
|
||||
symbol_print (symbol *s, FILE *f)
|
||||
{
|
||||
fprintf (f, "\"%s\"", s->tag);
|
||||
SYMBOL_ATTR_PRINT (type_name);
|
||||
SYMBOL_ATTR_PRINT (destructor);
|
||||
SYMBOL_ATTR_PRINT (printer);
|
||||
if (s)
|
||||
{
|
||||
fprintf (f, "\"%s\"", s->tag);
|
||||
SYMBOL_ATTR_PRINT (type_name);
|
||||
SYMBOL_ATTR_PRINT (destructor);
|
||||
SYMBOL_ATTR_PRINT (printer);
|
||||
}
|
||||
else
|
||||
fprintf (f, "<NULL>");
|
||||
}
|
||||
|
||||
#undef SYMBOL_ATTR_PRINT
|
||||
|
||||
@@ -98,6 +98,7 @@ AT_DATA_GRAMMAR([[input.y]],
|
||||
static int yylex (void);
|
||||
# define YYDEBUG 1
|
||||
# define YYERROR_VERBOSE 1
|
||||
# define USE(Var)
|
||||
%}
|
||||
|
||||
%union
|
||||
@@ -112,6 +113,7 @@ AT_DATA_GRAMMAR([[input.y]],
|
||||
exp: a_1 a_2 { $<val>$ = 3; } { $<val>$ = $<val>3 + 1; } a_5
|
||||
sum_of_the_five_previous_values
|
||||
{
|
||||
USE (($1, $2, $5));
|
||||
printf ("%d\n", $6);
|
||||
}
|
||||
;
|
||||
|
||||
@@ -54,6 +54,7 @@ AT_LALR1_CC_IF(
|
||||
# define alarm(seconds) /* empty */
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#define USE(Var)
|
||||
|
||||
/* Exercise pre-prologue dependency to %union. */
|
||||
typedef int semantic_value;
|
||||
@@ -109,7 +110,7 @@ input:
|
||||
|
||||
line:
|
||||
'\n'
|
||||
| exp '\n' { ]AT_PARAM_IF([*result = global_result = $1;])[ }
|
||||
| exp '\n' { ]AT_PARAM_IF([*result = global_result = $1], [USE ($1)])[; }
|
||||
;
|
||||
|
||||
exp:
|
||||
|
||||
1636
tests/existing.at
1636
tests/existing.at
File diff suppressed because it is too large
Load Diff
@@ -746,13 +746,14 @@ AT_SETUP([No users destructors if stack 0 deleted])
|
||||
AT_DATA_GRAMMAR([glr-regr9.y],
|
||||
[[
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
static void yyerror (char const *);
|
||||
static int yylex (void);
|
||||
#define YYSTACKEXPANDABLE 0
|
||||
# define YYSTACKEXPANDABLE 0
|
||||
static int tokens = 0;
|
||||
static int destructors = 0;
|
||||
# define USE(Var)
|
||||
%}
|
||||
|
||||
%glr-parser
|
||||
@@ -766,7 +767,7 @@ AT_DATA_GRAMMAR([glr-regr9.y],
|
||||
%%
|
||||
|
||||
start:
|
||||
ambig0 'a' { destructors += 2; }
|
||||
ambig0 'a' { destructors += 2; USE ($2); }
|
||||
| ambig1 start { destructors += 1; }
|
||||
| ambig2 start { destructors += 1; }
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user