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> 2005-12-21 Paul Eggert <eggert@cs.ucla.edu>
Undo 2005-12-01 tentative license wording change. The wording is 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: 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. * %destructor vs. YYABORT, YYACCEPT, and YYERROR.
Destructors are now called when user code invokes YYABORT, YYACCEPT, Destructors are now called when user code invokes YYABORT, YYACCEPT,
and YYERROR, for all objects on the stack, other than objects and YYERROR, for all objects on the stack, other than objects

View File

@@ -65,7 +65,7 @@ rule_useful_p (rule *r)
bool bool
rule_useless_p (rule *r) rule_useless_p (rule *r)
{ {
return r->number >= nrules; return !rule_useful_p (r);
} }
@@ -77,7 +77,7 @@ rule_useless_p (rule *r)
bool bool
rule_never_reduced_p (rule *r) 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) if (!rules[r].useful)
{ {
location_print (stderr, rules[r].location); location_print (stderr, rules[r].location);
fprintf (stderr, ": %s: %s: ", fprintf (stderr, ": %s: %s: ", _("warning"), message);
_("warning"), message);
rule_print (&rules[r], stderr); rule_print (&rules[r], stderr);
} }
} }

View File

@@ -40,7 +40,7 @@ static symbol_list *grammar = NULL;
static bool start_flag = false; static bool start_flag = false;
merger_list *merge_functions; merger_list *merge_functions;
/* Has %union been seen? */ /* Was %union seen? */
bool typed = false; bool typed = false;
/* Should rules have a default precedence? */ /* Should rules have a default precedence? */
@@ -104,7 +104,7 @@ get_merge_function (uniqstr name, uniqstr type, location loc)
type = uniqstr_new (""); type = uniqstr_new ("");
head.next = merge_functions; 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)) if (UNIQSTR_EQ (name, syms->next->name))
break; break;
if (syms->next == NULL) if (syms->next == NULL)
@@ -128,11 +128,8 @@ get_merge_function (uniqstr name, uniqstr type, location loc)
void void
free_merger_functions (void) free_merger_functions (void)
{ {
merger_list *L0; merger_list *L0 = merge_functions;
if (! glr_parser) while (L0)
return;
L0 = merge_functions;
while (L0 != NULL)
{ {
merger_list *L1 = L0->next; merger_list *L1 = L0->next;
free (L0); free (L0);
@@ -150,13 +147,6 @@ free_merger_functions (void)
| | | |
| All actions are copied out, labelled by the rule number they apply | | All actions are copied out, labelled by the rule number they apply |
| to. | | 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. */ /* The (currently) last symbol of GRAMMAR. */
@@ -206,7 +196,6 @@ grammar_rule_begin (symbol *lhs, location loc)
current_rule = grammar_end; current_rule = grammar_end;
/* Mark the rule's lhs as a nonterminal if not already so. */ /* Mark the rule's lhs as a nonterminal if not already so. */
if (lhs->class == unknown_sym) if (lhs->class == unknown_sym)
{ {
lhs->class = nterm_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); 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 static void
grammar_current_rule_check (void) grammar_current_rule_check (void)
{ {
symbol *lhs = current_rule->sym; symbol *lhs = current_rule->sym;
char const *lhs_type = lhs->type_name; 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 /* Type check.
is allowed to shoot herself in the foot. */
if (current_rule->action)
return;
/* 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. */ value can't be used. */
if (! lhs_type) if (!current_rule->action && lhs_type)
return;
/* 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 : ""; symbol *first_rhs = current_rule->next->sym;
if (!UNIQSTR_EQ (lhs_type, rhs_type)) /* 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, warn_at (current_rule->location,
_("type clash on default action: <%s> != <%s>"), _("empty rule for typed nonterminal, and no action"));
lhs_type, rhs_type); }
/* 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; extern merger_list *merge_functions;
/* Was %union seen? */
extern bool typed; extern bool typed;
/* Should rules have a default precedence? */
extern bool default_prec; extern bool default_prec;
#endif /* !READER_H_ */ #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 ()) if (INT_MIN <= num && num <= rule_length && ! get_errno ())
{ {
int n = num; int n = num;
if (1-n > max_left_semantic_context) if (max_left_semantic_context < 1 - n)
max_left_semantic_context = 1-n; max_left_semantic_context = 1 - n;
if (!type_name && n > 0) if (!type_name && 0 < n)
type_name = symbol_list_n_type_name_get (current_rule, loc, n); type_name = symbol_list_n_type_name_get (current_rule, loc, n);
if (!type_name && typed) if (!type_name && typed)
complain_at (loc, _("$%d of `%s' has no declared type"), 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, obstack_fgrow3 (&obstack_for_string,
"]b4_rhs_value(%d, %d, [%s])[", "]b4_rhs_value(%d, %d, [%s])[",
rule_length, n, type_name); rule_length, n, type_name);
if (typed)
symbol_list_n_used_set (current_rule, n, true);
} }
else else
complain_at (loc, _("integer out of range: %s"), quote (text)); 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_new (symbol *sym, location loc)
{ {
symbol_list *res = xmalloc (sizeof *res); symbol_list *res = xmalloc (sizeof *res);
res->next = NULL;
res->sym = sym; res->sym = sym;
res->location = loc; res->location = loc;
res->action = NULL; res->action = NULL;
res->used = false;
res->ruleprec = NULL; res->ruleprec = NULL;
res->dprec = 0; res->dprec = 0;
res->merger = 0; res->merger = 0;
res->next = NULL;
return res; return res;
} }
@@ -56,7 +62,7 @@ symbol_list_print (symbol_list *l, FILE *f)
{ {
symbol_print (l->sym, f); symbol_print (l->sym, f);
if (l && l->sym) if (l && l->sym)
fputc (' ', f); fprintf (f, ", ");
} }
} }
@@ -90,43 +96,64 @@ symbol_list_free (symbol_list *list)
`--------------------*/ `--------------------*/
unsigned int unsigned int
symbol_list_length (symbol_list *list) symbol_list_length (symbol_list *l)
{ {
int res = 0; int res = 0;
for (/* Nothing. */; list; list = list->next) for (/* Nothing. */; l; l = l->next)
++res; ++res;
return res; return res;
} }
/*--------------------------------------------------------------. /*--------------------------------.
| Get the data type (alternative in the union) of the value for | | Get symbol N in symbol list L. |
| symbol N in symbol list RP. | `--------------------------------*/
`--------------------------------------------------------------*/
uniqstr symbol_list *
symbol_list_n_type_name_get (symbol_list *rp, location loc, int n) symbol_list_n_get (symbol_list *l, int n)
{ {
int i; int i;
if (n < 0) 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); complain_at (loc, _("invalid $ value: $%d"), n);
return NULL; return NULL;
} }
return l->sym->type_name;
i = 0; }
while (i < n)
{ /*----------------------------------------.
rp = rp->next; | The symbol N in symbol list L is USED. |
if (rp == NULL || rp->sym == NULL) `----------------------------------------*/
{
complain_at (loc, _("invalid $ value: $%d"), n); void
return NULL; symbol_list_n_used_set (symbol_list *l, int n, bool used)
} {
++i; l = symbol_list_n_get (l, n);
} if (l)
l->used = used;
return rp->sym->type_name;
} }

View File

@@ -25,9 +25,10 @@
# include "location.h" # include "location.h"
# include "symtab.h" # include "symtab.h"
/* A list of symbols, used during the parsing to store the rules. */
typedef struct symbol_list typedef struct symbol_list
{ {
struct symbol_list *next; /* The symbol. */
symbol *sym; symbol *sym;
location location; location location;
@@ -35,9 +36,16 @@ typedef struct symbol_list
const char *action; const char *action;
location action_location; location action_location;
/* Whether this symbol's value is used in the current action. */
bool used;
/* Precedence/associativity. */
symbol *ruleprec; symbol *ruleprec;
int dprec; int dprec;
int merger; int merger;
/* The list. */
struct symbol_list *next;
} symbol_list; } symbol_list;
@@ -48,18 +56,24 @@ symbol_list *symbol_list_new (symbol *sym, location loc);
void symbol_list_print (symbol_list *l, FILE *f); void symbol_list_print (symbol_list *l, FILE *f);
/* Prepend SYM at LOC to the LIST. */ /* 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, symbol *sym,
location loc); location loc);
/* Free the LIST, but not the symbols it contains. */ /* 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. */ /* 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 /* Get the data type (alternative in the union) of the value for
symbol N in rule RULE. */ 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_ */ #endif /* !SYMLIST_H_ */

View File

@@ -85,10 +85,15 @@ symbol_new (uniqstr tag, location loc)
void void
symbol_print (symbol *s, FILE *f) symbol_print (symbol *s, FILE *f)
{ {
fprintf (f, "\"%s\"", s->tag); if (s)
SYMBOL_ATTR_PRINT (type_name); {
SYMBOL_ATTR_PRINT (destructor); fprintf (f, "\"%s\"", s->tag);
SYMBOL_ATTR_PRINT (printer); SYMBOL_ATTR_PRINT (type_name);
SYMBOL_ATTR_PRINT (destructor);
SYMBOL_ATTR_PRINT (printer);
}
else
fprintf (f, "<NULL>");
} }
#undef SYMBOL_ATTR_PRINT #undef SYMBOL_ATTR_PRINT

View File

@@ -98,6 +98,7 @@ AT_DATA_GRAMMAR([[input.y]],
static int yylex (void); static int yylex (void);
# define YYDEBUG 1 # define YYDEBUG 1
# define YYERROR_VERBOSE 1 # define YYERROR_VERBOSE 1
# define USE(Var)
%} %}
%union %union
@@ -112,6 +113,7 @@ AT_DATA_GRAMMAR([[input.y]],
exp: a_1 a_2 { $<val>$ = 3; } { $<val>$ = $<val>3 + 1; } a_5 exp: a_1 a_2 { $<val>$ = 3; } { $<val>$ = $<val>3 + 1; } a_5
sum_of_the_five_previous_values sum_of_the_five_previous_values
{ {
USE (($1, $2, $5));
printf ("%d\n", $6); printf ("%d\n", $6);
} }
; ;

View File

@@ -54,6 +54,7 @@ AT_LALR1_CC_IF(
# define alarm(seconds) /* empty */ # define alarm(seconds) /* empty */
#endif #endif
#include <ctype.h> #include <ctype.h>
#define USE(Var)
/* Exercise pre-prologue dependency to %union. */ /* Exercise pre-prologue dependency to %union. */
typedef int semantic_value; typedef int semantic_value;
@@ -109,7 +110,7 @@ input:
line: line:
'\n' '\n'
| exp '\n' { ]AT_PARAM_IF([*result = global_result = $1;])[ } | exp '\n' { ]AT_PARAM_IF([*result = global_result = $1], [USE ($1)])[; }
; ;
exp: 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], AT_DATA_GRAMMAR([glr-regr9.y],
[[ [[
%{ %{
#include <stdio.h> # include <stdio.h>
#include <stdlib.h> # include <stdlib.h>
static void yyerror (char const *); static void yyerror (char const *);
static int yylex (void); static int yylex (void);
#define YYSTACKEXPANDABLE 0 # define YYSTACKEXPANDABLE 0
static int tokens = 0; static int tokens = 0;
static int destructors = 0; static int destructors = 0;
# define USE(Var)
%} %}
%glr-parser %glr-parser
@@ -766,7 +767,7 @@ AT_DATA_GRAMMAR([glr-regr9.y],
%% %%
start: start:
ambig0 'a' { destructors += 2; } ambig0 'a' { destructors += 2; USE ($2); }
| ambig1 start { destructors += 1; } | ambig1 start { destructors += 1; }
| ambig2 start { destructors += 1; } | ambig2 start { destructors += 1; }
; ;