diagnostics: complain about undeclared string tokens

String literals, which allow for better error messages, are (too)
liberally accepted by Bison, which might result in silent errors.  For
instance

    %type <exVal> cond "condition"

does not define “condition” as a string alias to 'cond' (nonterminal
symbols do not have string aliases).  It is rather equivalent to

    %nterm <exVal> cond
    %token <exVal> "condition"

i.e., it gives the type 'exVal' to the "condition" token, which was
clearly not the intention.

Introduce -Wdangling-alias to catch this.

* src/complain.h, src/complain.c: Add support for -Wdangling-alias.
(argmatch_warning_args): Sort.
* src/symtab.c (symbol_check_defined): Complain about dangling
aliases.
* doc/bison.texi: Document it.
* tests/input.at (Dangling aliases): New test.
This commit is contained in:
Akim Demaille
2019-11-12 08:28:51 +01:00
parent 28d1ca8f48
commit 8a910107b3
6 changed files with 134 additions and 13 deletions

38
NEWS
View File

@@ -4,6 +4,44 @@ GNU Bison NEWS
** New Features ** New Features
*** New diagnostic: -Wdangling-alias
String literals, which allow for better error messages, are (too)
liberally accepted by Bison, which might result in silent errors. For
instance
%type <exVal> cond "condition"
does not define "condition" as a string alias to 'cond' (nonterminal
symbols do not have string aliases). It is rather equivalent to
%nterm <exVal> cond
%token <exVal> "condition"
i.e., it gives the type 'exVal' to the "condition" token, which was
clearly not the intention.
Also, because string aliases need not be defined, typos such as "baz"
instead of "bar" will be not reported.
The option -Wdangling-alias catches these situations. On
%token BAR "bar"
%type <ival> foo "foo"
%%
foo: "baz" {}
bison -Wdangling-alias reports
warning: string literal not attached to a symbol
| %type <ival> foo "foo"
| ^~~~~
warning: string literal not attached to a symbol
| foo: "baz" {}
| ^~~~~
The -Wall option does not (yet?) include -Wdangling-alias.
*** Better POSIX Yacc compatibility diagnostics *** Better POSIX Yacc compatibility diagnostics
POSIX Yacc restricts %type to nonterminals. This is now diagnosed by POSIX Yacc restricts %type to nonterminals. This is now diagnosed by

View File

@@ -3551,8 +3551,9 @@ They are still considered distinct rules even when joined in this way.
@findex %empty @findex %empty
A rule is said to be @dfn{empty} if its right-hand side (@var{components}) A rule is said to be @dfn{empty} if its right-hand side (@var{components})
is empty. It means that @var{result} can match the empty string. For is empty. It means that @var{result} in the previous example can match the
example, here is how to define an optional semicolon: empty string. As another example, here is how to define an optional
semicolon:
@example @example
semicolon.opt: | ";"; semicolon.opt: | ";";
@@ -10531,6 +10532,52 @@ unexpected number of conflicts is an error, and an expected number of
conflicts is not reported, so @option{-W} and @option{--warning} then have conflicts is not reported, so @option{-W} and @option{--warning} then have
no effect on the conflict report. no effect on the conflict report.
@item dangling-alias
Report string literals that are not bound to a token symbol.
String literals, which allow for better error messages, are (too) liberally
accepted by Bison, which might result in silent errors. For instance
@example
%type <exVal> cond "condition"
@end example
@noindent
does not define ``condition'' as a string alias to @code{cond}---nonterminal
symbols do not have string aliases. It is rather equivalent to
@example
%nterm <exVal> cond
%token <exVal> "condition"
@end example
@noindent
i.e., it gives the @samp{"condition"} token the type @code{exVal}.
Also, because string aliases do not need to be defined, typos such as
@samp{"baz"} instead of @samp{"bar"} will be not reported.
The option @option{-Wdangling-alias} catches these situations. On
@example
%token BAR "bar"
%type <ival> foo "foo"
%%
foo: "baz" @{@}
@end example
@noindent
@command{bison -Wdangling-alias} reports
@example
@dwarning{warning}: string literal not attached to a symbol
| %type <ival> foo @dwarning{"foo"}
| @dwarning{^~~~~}
@dwarning{warning}: string literal not attached to a symbol
| foo: @dwarning{"baz"} @{@}
| @dwarning{^~~~~}
@end example
@item deprecated @item deprecated
Deprecated constructs whose support will be removed in future versions of Deprecated constructs whose support will be removed in future versions of
Bison. Bison.
@@ -10632,7 +10679,7 @@ releases of Bison may move warnings from this category to new, more specific
categories. categories.
@item all @item all
All the warnings except @code{yacc}. All the warnings except @code{dangling-alias} and @code{yacc}.
@item none @item none
Turn off all the warnings. Turn off all the warnings.

View File

@@ -113,13 +113,14 @@ static const argmatch_warning_doc argmatch_warning_docs[] =
{ {
{ "conflicts-sr", N_("S/R conflicts (enabled by default)") }, { "conflicts-sr", N_("S/R conflicts (enabled by default)") },
{ "conflicts-rr", N_("R/R conflicts (enabled by default)") }, { "conflicts-rr", N_("R/R conflicts (enabled by default)") },
{ "dangling-alias", N_("string aliases not attached to a symbol") },
{ "deprecated", N_("obsolete constructs") }, { "deprecated", N_("obsolete constructs") },
{ "empty-rule", N_("empty rules without %empty") }, { "empty-rule", N_("empty rules without %empty") },
{ "midrule-values", N_("unset or unused midrule values") }, { "midrule-values", N_("unset or unused midrule values") },
{ "precedence", N_("useless precedence and associativity") }, { "precedence", N_("useless precedence and associativity") },
{ "yacc", N_("incompatibilities with POSIX Yacc") }, { "yacc", N_("incompatibilities with POSIX Yacc") },
{ "other", N_("all other warnings (enabled by default)") }, { "other", N_("all other warnings (enabled by default)") },
{ "all", N_("all the warnings except 'yacc'") }, { "all", N_("all the warnings except 'dangling-alias' and 'yacc'") },
{ "no-CATEGORY", N_("turn off warnings in CATEGORY") }, { "no-CATEGORY", N_("turn off warnings in CATEGORY") },
{ "none", N_("turn off all the warnings") }, { "none", N_("turn off all the warnings") },
{ "error[=CATEGORY]", N_("treat warnings as errors") }, { "error[=CATEGORY]", N_("treat warnings as errors") },
@@ -128,17 +129,18 @@ static const argmatch_warning_doc argmatch_warning_docs[] =
static const argmatch_warning_arg argmatch_warning_args[] = static const argmatch_warning_arg argmatch_warning_args[] =
{ {
{ "none", Wnone }, { "all", Wall },
{ "midrule-values", Wmidrule_values },
{ "yacc", Wyacc },
{ "conflicts-sr", Wconflicts_sr },
{ "conflicts-rr", Wconflicts_rr }, { "conflicts-rr", Wconflicts_rr },
{ "conflicts-sr", Wconflicts_sr },
{ "dangling-alias", Wdangling_alias },
{ "deprecated", Wdeprecated }, { "deprecated", Wdeprecated },
{ "empty-rule", Wempty_rule }, { "empty-rule", Wempty_rule },
{ "precedence", Wprecedence },
{ "other", Wother },
{ "all", Wall },
{ "everything", Weverything }, { "everything", Weverything },
{ "midrule-values", Wmidrule_values },
{ "none", Wnone },
{ "other", Wother },
{ "precedence", Wprecedence },
{ "yacc", Wyacc },
{ NULL, Wnone } { NULL, Wnone }
}; };

View File

@@ -47,6 +47,7 @@ typedef enum
{ {
warning_conflicts_rr, warning_conflicts_rr,
warning_conflicts_sr, warning_conflicts_sr,
warning_dangling_alias,
warning_deprecated, warning_deprecated,
warning_empty_rule, warning_empty_rule,
warning_midrule_values, warning_midrule_values,
@@ -104,6 +105,7 @@ typedef enum
Wconflicts_rr = 1 << warning_conflicts_rr, Wconflicts_rr = 1 << warning_conflicts_rr,
Wconflicts_sr = 1 << warning_conflicts_sr, Wconflicts_sr = 1 << warning_conflicts_sr,
Wdangling_alias = 1 << warning_dangling_alias,
Wdeprecated = 1 << warning_deprecated, Wdeprecated = 1 << warning_deprecated,
Wempty_rule = 1 << warning_empty_rule, Wempty_rule = 1 << warning_empty_rule,
Wmidrule_values = 1 << warning_midrule_values, Wmidrule_values = 1 << warning_midrule_values,
@@ -118,7 +120,7 @@ typedef enum
/**< All above warnings. */ /**< All above warnings. */
Weverything = ~complaint & ~fatal & ~silent, Weverything = ~complaint & ~fatal & ~silent,
Wall = Weverything & ~Wyacc Wall = Weverything & ~Wdangling_alias & ~Wyacc
} warnings; } warnings;
/** Whether the warnings of \a flags are all unset. /** Whether the warnings of \a flags are all unset.

View File

@@ -26,12 +26,12 @@
#include <assure.h> #include <assure.h>
#include <fstrcmp.h> #include <fstrcmp.h>
#include <hash.h> #include <hash.h>
#include <quote.h>
#include "complain.h" #include "complain.h"
#include "getargs.h" #include "getargs.h"
#include "gram.h" #include "gram.h"
#include "intprops.h" #include "intprops.h"
#include "quote.h"
static struct hash_table *symbol_table = NULL; static struct hash_table *symbol_table = NULL;
static struct hash_table *semantic_type_table = NULL; static struct hash_table *semantic_type_table = NULL;
@@ -610,6 +610,13 @@ symbol_check_defined (symbol *sym)
s->number = nvars++; s->number = nvars++;
} }
if (s->class == token_sym
&& sym->tag[0] == '"'
&& !sym->is_alias)
complain (&sym->location, Wdangling_alias,
_("string literal %s not attached to a symbol"),
sym->tag);
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
symbol_code_props_get (sym, i)->is_used = true; symbol_code_props_get (sym, i)->is_used = true;

View File

@@ -298,6 +298,31 @@ input.y:8.14: error: syntax error, unexpected integer
AT_CLEANUP AT_CLEANUP
## ------------------ ##
## Dangling aliases. ##
## ------------------ ##
AT_SETUP([Dangling aliases])
AT_DATA([input.y],
[[%token FOO "foo"
%type <val> "bar"
%%
expr: "foo" "bar" "baz"
]])
AT_BISON_CHECK([-fcaret -Wdangling input.y], [0], [],
[[input.y:2.13-17: warning: string literal "bar" not attached to a symbol [-Wdangling-alias]
2 | %type <val> "bar"
| ^~~~~
input.y:4.19-23: warning: string literal "baz" not attached to a symbol [-Wdangling-alias]
4 | expr: "foo" "bar" "baz"
| ^~~~~
]])
AT_CLEANUP
## --------------------- ## ## --------------------- ##
## Symbol declarations. ## ## Symbol declarations. ##
## --------------------- ## ## --------------------- ##