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:
Akim Demaille
2005-12-22 11:40:05 +00:00
parent 8bb4c753e2
commit affac6132a
13 changed files with 386 additions and 1514 deletions

View File

@@ -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
View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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"));
}

View File

@@ -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_ */

View File

@@ -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));

View File

@@ -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;
}

View File

@@ -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_ */

View File

@@ -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

View File

@@ -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);
}
;

View File

@@ -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:

File diff suppressed because it is too large Load Diff

View File

@@ -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; }
;