diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2021-02-15 13:10:39 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2021-02-15 13:40:51 +0200 |
commit | 5bacabb09f8f4808f8c3029cf31425aea91846a0 (patch) | |
tree | c598924201dadd2940b64538c232464515a048d5 | |
parent | b58dc6296515d0886bbd4197c74d26994d428d8d (diff) | |
download | mailfromd-5bacabb09f8f4808f8c3029cf31425aea91846a0.tar.gz mailfromd-5bacabb09f8f4808f8c3029cf31425aea91846a0.tar.bz2 |
Warn about uninitialized variables; meaningful use of string variables in boolean context
* src/mailfromd.h (variable) <initialized>: New member.
(variable_check_initialized): New proto.
* src/symbols.c (init_variable): Set initialized to 0.
* src/gram.y: Convert the use of string variable in boolean context
to s != ''.
(create_asgn_node): Set var->initialized to 1.
(create_node_variable): Check if the variable is initialized.
(variable_check_initialized): New function.
* src/lex.l (variable_or_const): Check if the variable is
initialized.
* mflib/dns.mf4 (dns_getname, dns_getaddr)
(getns,getmx): Initialize result.
* tests/arginit.at: New test.
* tests/Makefile.am: Add new test.
* tests/testsuite.at: Include new test.
* doc/mailfromd.texi: Elaborate on default values of variables.
* NEWS: Version 8.9.90
* configure.ac: Likewise.
-rw-r--r-- | NEWS | 25 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | doc/mailfromd.texi | 30 | ||||
-rw-r--r-- | mflib/dns.mf4 | 8 | ||||
-rw-r--r-- | src/gram.y | 54 | ||||
-rw-r--r-- | src/lex.l | 3 | ||||
-rw-r--r-- | src/mailfromd.h | 5 | ||||
-rw-r--r-- | src/symbols.c | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/arginit.at | 56 | ||||
-rw-r--r-- | tests/testsuite.at | 1 |
11 files changed, 165 insertions, 21 deletions
@@ -1,8 +1,31 @@ -Mailfromd NEWS -- history of user-visible changes. 2020-12-29 +Mailfromd NEWS -- history of user-visible changes. 2021-02-15 See the end of file for copying conditions. Please send Mailfromd bug reports to <bug-mailfromd@gnu.org.ua> +Version 8.9.90 (git) + +* Use of uninitialized automatic variables + +The MFL compiler issues a warning if it encounters the use of a +previously uninitialized automatic variable. In future versions +the warning will change to error. + +* Use of string variables in boolean context + +Strings can meaningfully be used in boolean context. For example + + func f(string s) + do + if s + echo "non-empty + fi + done + +The use of "s" in conditional is equivalent to + + if s != "" + Version 8.9, 2020-12-29 * The sed function. diff --git a/configure.ac b/configure.ac index 7d75d27f..287f09d0 100644 --- a/configure.ac +++ b/configure.ac @@ -17,6 +17,7 @@ AC_PREREQ(2.63) m4_define([MF_VERSION_MAJOR], 8) m4_define([MF_VERSION_MINOR], 9) +m4_define([MF_VERSION_PATCH], 90) AC_INIT([mailfromd], MF_VERSION_MAJOR.MF_VERSION_MINOR[]m4_ifdef([MF_VERSION_PATCH],.MF_VERSION_PATCH), [bug-mailfromd@gnu.org.ua], diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi index b88ca5e3..8bfbda57 100644 --- a/doc/mailfromd.texi +++ b/doc/mailfromd.texi @@ -5184,6 +5184,11 @@ variables and @samp{number} for numeric ones. string var @end example + If a variable declaration occurs within a function +(@pxref{Functions,User-defined}) or handler (@pxref{Handlers}), it +declares an automatic variable, local to this function or handler. +Otherwise, it declares a global variable. + @cindex qualifiers, variable declaration @kwindex public @kwindex static @@ -5215,23 +5220,26 @@ or precious static string rcpt_list @end example - The declaration can be followed by any valid @acronym{MFL} -expression, which supplies the @dfn{initial value} for the + Declaration can be followed by any valid @acronym{MFL} +expression, which supplies the initial value or @dfn{initializer} for the variable, for example: @example string var "test" @end example - If a variable declaration occurs within a function -(@pxref{Functions,User-defined}) or handler (@pxref{Handlers}), it -declares an automatic variable, local to this function or handler. -Otherwise, it declares a global variable. + A global variable declared without initializer is implicitly +initialized to a null value: numeric variables assume initial value 0, +string variable are initialized to empty string. + + The value of an automatic variable declared without initializer is +unspecified. It is an error to use such variable prior to assigning +it a value. @cindex variable, assigning a value @cindex variable assignment @kwindex set - A variable is assigned a value using @code{set} statement: + A variable is assigned a value using the @code{set} statement: @example set @var{name} @var{expr} @@ -5256,9 +5264,11 @@ topmost lexical level. This is called @dfn{implicit variable declaration}. @cindex variables, referencing - Variables are referenced using the notation @samp{%@var{name}}. The -variable being referenced must have been declared earlier (either -explicitly or implicitly). + In the @acronym{MFL} program, variables are referenced by their +name. When appearing inside a double-quoted string, variables are +referenced using the notation @samp{%@var{name}}. Any variable being +referenced must have been declared earlier (either explicitly or +implicitly). @menu * Predefined variables:: diff --git a/mflib/dns.mf4 b/mflib/dns.mf4 index 9c7e7d85..3d52f086 100644 --- a/mflib/dns.mf4 +++ b/mflib/dns.mf4 @@ -85,7 +85,7 @@ done func dns_getname (string ipstr) returns string do - string result + string result '' set n dns_query(DNS_TYPE_PTR, ipstr, 1) loop for set i 0, while i < dns_reply_count(n), @@ -103,7 +103,7 @@ done func dns_getaddr (string domain) returns string do - string result + string result '' set n dns_query(DNS_TYPE_A, domain, 1) loop for set i 0, while i < dns_reply_count(n), @@ -121,7 +121,7 @@ done func getns (string domain; number resolve_names, number sort_names) returns string do - string result + string result '' set n dns_query(DNS_TYPE_NS, domain, sort_names, resolve_names) loop for set i 0, while i < dns_reply_count(n), @@ -139,7 +139,7 @@ done func getmx (string domain; number resolve_names) returns string do - string result + string result '' set n dns_query(DNS_TYPE_MX, domain, 0, resolve_names) loop for set i 0, while i < dns_reply_count(n), @@ -449,7 +449,7 @@ _create_alias(void *item, void *data) %type <node> decl stmt condition action sendmail_action header_action if_cond else_cond on_cond atom argref paren_argref - funcall expr maybe_expr maybe_xcode_expr + funcall expr bool_expr maybe_expr maybe_xcode_expr simp_expr atom_expr asgn catch simple_catch try_block throw return case_cond autodcl constdecl @@ -1392,7 +1392,7 @@ condition : if_cond | on_cond ; -if_cond : T_IF expr stmtlist else_cond T_FI +if_cond : T_IF bool_expr stmtlist else_cond T_FI { $$ = alloc_node(node_type_if, &@1); $$->v.cond.cond = $2; @@ -1405,7 +1405,7 @@ else_cond : /* empty */ { $$ = NULL; } - | T_ELIF expr stmtlist else_cond + | T_ELIF bool_expr stmtlist else_cond { $$ = alloc_node(node_type_if, &@1); $$->v.cond.cond = $2; @@ -2246,6 +2246,30 @@ return : T_RETURN } ; +bool_expr : expr + { + switch (node_type($1)) { + case dtype_string: + $$ = alloc_node(node_type_bin, &@1); + $$->v.bin.opcode = bin_ne; + $$->v.bin.arg[0] = $1; + $$->v.bin.arg[1] = alloc_node(node_type_string, &@1); + $$->v.bin.arg[1]->v.literal = literal_lookup(""); + break; + + case dtype_number: + case dtype_pointer: + $$ = $1; + break; + + default: + parse_error_locus(&@1, + _("unspecified data type in conditional expression: please, report")); + YYERROR; + } + } + ; + /* *************************** */ /* ON statement */ /* *************************** */ @@ -3674,6 +3698,7 @@ create_asgn_node(struct variable *var, NODE *expr, node->v.asgn.var = var; node->v.asgn.nframes = catch_nesting; node->v.asgn.node = cast_to(var->type, expr); + var->initialized = 1; return node; } @@ -4487,7 +4512,10 @@ declare_function(struct function *func, struct mu_locus_range const *loc, NODE * create_node_variable(struct variable *var, struct mu_locus_range const *locus) { - NODE *node = alloc_node(node_type_variable, locus); + NODE *node; + + variable_check_initialized(var, locus); + node = alloc_node(node_type_variable, locus); node->v.var_ref.variable = var; node->v.var_ref.nframes = catch_nesting; return node; @@ -4548,3 +4576,21 @@ create_node_backref(long num, struct mu_locus_range const *locus) node->v.number = num; return node; } + +static inline int +variable_is_initialized(struct variable *var) +{ + return var->storage_class != storage_auto || var->initialized; +} + +void +variable_check_initialized(struct variable *var, + struct mu_locus_range const *loc) +{ + if (!variable_is_initialized(var)) { + parse_warning_locus(loc, + _("use of uninitialized variable '%s'"), + var->sym.name); + var->initialized = 1; + } +} @@ -261,7 +261,7 @@ lex_new_source(const char *name, int flag) /* Return constant or variable token corresponding to the current value of yylval.literal->text. */ static int -variable_or_const() +variable_or_const(void) { struct variable *vptr; const struct constant *cptr; @@ -289,6 +289,7 @@ variable_or_const() yylval.literal->text); return T_BOGUS; } + variable_check_initialized(vptr, &yylloc); add_xref(vptr, &yylloc); yylval.var = vptr; return T_VARIABLE; diff --git a/src/mailfromd.h b/src/mailfromd.h index d6582e05..e0610a2f 100644 --- a/src/mailfromd.h +++ b/src/mailfromd.h @@ -502,6 +502,8 @@ struct variable { size_t *addrptr; /* Address pointer (for built-in vars) */ struct variable *shadowed; /* Points to the variable shadowed by this one */ + int initialized; /* Is the variable initialized (for + automatic variables) */ mu_list_t xref; /* List of struct mu_locus_range */ struct variable *next; /* Next variable in this class */ }; @@ -765,6 +767,9 @@ void register_macro(enum smtp_state tag, const char *macro); char *get_stage_macro_string(enum gacopyz_stage i); struct exmask *exmask_create(void); +void variable_check_initialized(struct variable *var, + struct mu_locus_range const *loc); + /* Data types and declarations for handling compiled configuration code */ diff --git a/src/symbols.c b/src/symbols.c index ad0b6994..bed911b5 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -736,6 +736,7 @@ init_variable(struct variable *var) var->off = 0; var->addrptr = NULL; var->shadowed = NULL; + var->initialized = 0; var->xref = NULL; var->next = NULL; } @@ -901,7 +902,6 @@ literal_lookup(const char *text) } return lit; } - struct constant * define_constant(const char *name, struct value *value, unsigned flags, diff --git a/tests/Makefile.am b/tests/Makefile.am index dcea96dc..e6655304 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -44,6 +44,7 @@ TESTSUITE_AT = \ ack.at\ alias.at\ arg.at\ + arginit.at\ ashadow.at\ bctx00.at\ bctx01.at\ diff --git a/tests/arginit.at b/tests/arginit.at new file mode 100644 index 00000000..212ce62c --- /dev/null +++ b/tests/arginit.at @@ -0,0 +1,56 @@ +# This file is part of Mailfromd testsuite. -*- Autotest -*- +# Copyright (C) 2021 Sergey Poznyakoff +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +AT_SETUP([Use of uninitialized variables]) +AT_KEYWORDS([arginit variable variables]) +MF_RUN_TEXT([ +func x() +do + string result + set y result +done + +func main(...) returns number +do + x() + x() +done +], +[0], +[], +[], +[mailfromd: prog:5.9-14: warning: use of uninitialized variable 'result' +]) + +MF_RUN_TEXT([ +func x() +do + string result + set y "result is %result" +done + +func main(...) returns number +do + x() + x() +done +], +[0], +[], +[], +[mailfromd: prog:5.20-26: warning: use of uninitialized variable 'result' +]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 19532d08..3400738a 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -189,6 +189,7 @@ m4_include([ml.at]) m4_include([ml01.at]) m4_include([declvar.at]) +m4_include([arginit.at]) AT_BANNER([Macros]) m4_include([macros.at]) |