From c861b17bbf002129f29e22ab625fd3516ba792a2 Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Mon, 23 Jan 2017 09:50:57 +0000 Subject: 40391: Add WARN_NESTED_VAR option and functions -W. These are companions to WARN_CREATED_GLOBAL, warning when a variable from an enclosing scope is altered. --- Src/builtin.c | 12 +++++++---- Src/exec.c | 13 +++++++++--- Src/options.c | 1 + Src/params.c | 66 ++++++++++++++++++++++++++++++++++++++--------------------- Src/zsh.h | 10 ++++++++- 5 files changed, 71 insertions(+), 31 deletions(-) (limited to 'Src') diff --git a/Src/builtin.c b/Src/builtin.c index 7a04a79a7..2fb1a70a4 100644 --- a/Src/builtin.c +++ b/Src/builtin.c @@ -46,7 +46,7 @@ static struct builtin builtins[] = BUILTIN(".", BINF_PSPECIAL, bin_dot, 1, -1, 0, NULL, NULL), BUILTIN(":", BINF_PSPECIAL, bin_true, 0, -1, 0, NULL, NULL), BUILTIN("alias", BINF_MAGICEQUALS | BINF_PLUSOPTS, bin_alias, 0, -1, 0, "Lgmrs", NULL), - BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwXz", "u"), + BUILTIN("autoload", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "dmktrRTUwWXz", "u"), BUILTIN("bg", 0, bin_fg, 0, -1, BIN_BG, NULL, NULL), BUILTIN("break", BINF_PSPECIAL, bin_break, 0, 1, BIN_BREAK, NULL, NULL), BUILTIN("bye", 0, bin_break, 0, 1, BIN_EXIT, NULL, NULL), @@ -72,7 +72,7 @@ static struct builtin builtins[] = BUILTIN("fc", 0, bin_fc, 0, -1, BIN_FC, "aAdDe:EfiIlLmnpPrRt:W", NULL), BUILTIN("fg", 0, bin_fg, 0, -1, BIN_FG, NULL, NULL), BUILTIN("float", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%Z:%ghlprtux", "E"), - BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUx:z", NULL), + BUILTIN("functions", BINF_PLUSOPTS, bin_functions, 0, -1, 0, "kmMtTuUWx:z", NULL), BUILTIN("getln", 0, bin_read, 0, -1, 0, "ecnAlE", "zr"), BUILTIN("getopts", 0, bin_getopts, 2, -1, 0, NULL, NULL), BUILTIN("hash", BINF_MAGICEQUALS, bin_hash, 0, -1, 0, "Ldfmrv", NULL), @@ -796,8 +796,8 @@ set_pwd_env(void) unsetparam_pm(pm, 0, 1); } - setsparam("PWD", ztrdup(pwd)); - setsparam("OLDPWD", ztrdup(oldpwd)); + assignsparam("PWD", ztrdup(pwd), 0); + assignsparam("OLDPWD", ztrdup(oldpwd), 0); pm = (Param) paramtab->getnode(paramtab, "PWD"); if (!(pm->node.flags & PM_EXPORTED)) @@ -3068,6 +3068,10 @@ bin_functions(char *name, char **argv, Options ops, int func) on |= PM_TAGGED_LOCAL; else if (OPT_PLUS(ops,'T')) off |= PM_TAGGED_LOCAL; + if (OPT_MINUS(ops,'W')) + on |= PM_WARNNESTED; + else if (OPT_PLUS(ops,'W')) + off |= PM_WARNNESTED; roff = off; if (OPT_MINUS(ops,'z')) { on |= PM_ZSHSTORED; diff --git a/Src/exec.c b/Src/exec.c index 6c8264394..8f4969f52 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -2379,9 +2379,7 @@ addvars(Estate state, Wordcode pc, int addflags) * to be restored after the command, since then the assignment * is implicitly scoped. */ - flags = (!(addflags & ADDVAR_RESTORE) && - locallevel > forklevel && isset(WARNCREATEGLOBAL)) ? - ASSPM_WARN_CREATE : 0; + flags = !(addflags & ADDVAR_RESTORE) ? ASSPM_WARN : 0; xtr = isset(XTRACE); if (xtr) { printprompt4(); @@ -5431,6 +5429,14 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) else opts[XTRACE] = 0; } + if (flags & PM_WARNNESTED) + opts[WARNNESTEDVAR] = 1; + else if (oflags & PM_WARNNESTED) { + if (shfunc->node.nam == ANONYMOUS_FUNCTION_NAME) + flags |= PM_WARNNESTED; + else + opts[WARNNESTEDVAR] = 0; + } ooflags = oflags; /* * oflags is static, because we compare it on the next recursive @@ -5549,6 +5555,7 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval) opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; opts[LOCALLOOPS] = saveopts[LOCALLOOPS]; + opts[WARNNESTEDVAR] = saveopts[WARNNESTEDVAR]; } if (opts[LOCALLOOPS]) { diff --git a/Src/options.c b/Src/options.c index 4729ba54a..e0b67d205 100644 --- a/Src/options.c +++ b/Src/options.c @@ -258,6 +258,7 @@ static struct optname optns[] = { {{NULL, "verbose", 0}, VERBOSE}, {{NULL, "vi", 0}, VIMODE}, {{NULL, "warncreateglobal", OPT_EMULATE}, WARNCREATEGLOBAL}, +{{NULL, "warnnestedvar", OPT_EMULATE}, WARNNESTEDVAR}, {{NULL, "xtrace", 0}, XTRACE}, {{NULL, "zle", OPT_SPECIAL}, USEZLE}, {{NULL, "braceexpand", OPT_ALIAS}, /* ksh/bash */ -IGNOREBRACES}, diff --git a/Src/params.c b/Src/params.c index d4904666f..ebdd25225 100644 --- a/Src/params.c +++ b/Src/params.c @@ -2849,20 +2849,47 @@ gethkparam(char *s) return NULL; } +/* + * Function behind WARNCREATEGLOBAL and WARNNESTEDVAR option. + * + * For WARNNESTEDVAR: + * Called when the variable is created. + * Apply heuristics to see if this variable was just created + * globally but in a local context. + * + * For WARNNESTEDVAR: + * Called when the variable already exists and is set. + * Apply heuristics to see if this variable is setting + * a variable that was created in a less nested function + * or globally. + */ + /**/ static void -check_warn_create(Param pm, const char *pmtype) +check_warn_pm(Param pm, const char *pmtype, int created) { Funcstack i; - if (pm->level != 0 || (pm->node.flags & PM_SPECIAL)) + if (created && isset(WARNCREATEGLOBAL)) { + if (locallevel <= forklevel || pm->level != 0) + return; + } else if (!created && isset(WARNNESTEDVAR)) { + if (pm->level >= locallevel) + return; + } else + return; + + if (pm->node.flags & PM_SPECIAL) return; for (i = funcstack; i; i = i->prev) { if (i->tp == FS_FUNC) { + char *msg; DPUTS(!i->name, "funcstack entry with no name"); - zwarn("%s parameter %s created globally in function %s", - pmtype, pm->node.nam, i->name); + msg = created ? + "%s parameter %s created globally in function %s" : + "%s parameter %s set in enclosing scope in function %s"; + zwarn(msg, pmtype, pm->node.nam, i->name); break; } } @@ -2923,8 +2950,8 @@ assignsparam(char *s, char *val, int flags) /* errflag |= ERRFLAG_ERROR; */ return NULL; } - if (flags & ASSPM_WARN_CREATE) - check_warn_create(v->pm, "scalar"); + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "scalar", flags & ASSPM_WARN_CREATE); if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { switch (PM_TYPE(v->pm->node.flags)) { @@ -3005,9 +3032,7 @@ assignsparam(char *s, char *val, int flags) mod_export Param setsparam(char *s, char *val) { - return assignsparam( - s, val, isset(WARNCREATEGLOBAL) && locallevel > forklevel ? - ASSPM_WARN_CREATE : 0); + return assignsparam(s, val, ASSPM_WARN); } /**/ @@ -3074,8 +3099,8 @@ assignaparam(char *s, char **val, int flags) return NULL; } - if (flags & ASSPM_WARN_CREATE) - check_warn_create(v->pm, "array"); + if (flags & ASSPM_WARN) + check_warn_pm(v->pm, "array", flags & ASSPM_WARN_CREATE); if (flags & ASSPM_AUGMENT) { if (v->start == 0 && v->end == -1) { if (PM_TYPE(v->pm->node.flags) & PM_ARRAY) { @@ -3103,9 +3128,7 @@ assignaparam(char *s, char **val, int flags) mod_export Param setaparam(char *s, char **aval) { - return assignaparam( - s, aval, isset(WARNCREATEGLOBAL) && locallevel > forklevel ? - ASSPM_WARN_CREATE : 0); + return assignaparam(s, aval, ASSPM_WARN); } /**/ @@ -3134,7 +3157,7 @@ sethparam(char *s, char **val) queue_signals(); if (!(v = fetchvalue(&vbuf, &s, 1, SCANPM_ASSIGNING))) { createparam(t, PM_HASHED); - checkcreate = isset(WARNCREATEGLOBAL) && locallevel > forklevel; + checkcreate = 1; } else if (!(PM_TYPE(v->pm->node.flags) & PM_HASHED) && !(v->pm->node.flags & PM_SPECIAL)) { unsetparam(t); @@ -3148,8 +3171,7 @@ sethparam(char *s, char **val) /* errflag |= ERRFLAG_ERROR; */ return NULL; } - if (checkcreate) - check_warn_create(v->pm, "associative array"); + check_warn_pm(v->pm, "associative array", checkcreate); setarrvalue(v, val); unqueue_signals(); return v->pm; @@ -3214,8 +3236,9 @@ setnparam(char *s, mnumber val) unqueue_signals(); return NULL; } - if (!was_unset && isset(WARNCREATEGLOBAL) && locallevel > forklevel) - check_warn_create(v->pm, "numeric"); + check_warn_pm(v->pm, "numeric", !was_unset); + } else { + check_warn_pm(v->pm, "numeric", 0); } setnumvalue(v, val); unqueue_signals(); @@ -3250,10 +3273,7 @@ setiparam_no_convert(char *s, zlong val) */ char buf[BDIGBUFSIZE]; convbase(buf, val, 10); - return assignsparam( - s, ztrdup(buf), - isset(WARNCREATEGLOBAL) && locallevel > forklevel ? - ASSPM_WARN_CREATE : 0); + return assignsparam(s, ztrdup(buf), ASSPM_WARN); } /* Unset a parameter */ diff --git a/Src/zsh.h b/Src/zsh.h index 7d18333be..d0222605b 100644 --- a/Src/zsh.h +++ b/Src/zsh.h @@ -1815,6 +1815,7 @@ struct tieddata { #define PM_HIDE (1<<14) /* Special behaviour hidden by local */ #define PM_CUR_FPATH (1<<14) /* (function): can use $fpath with filename */ #define PM_HIDEVAL (1<<15) /* Value not shown in `typeset' commands */ +#define PM_WARNNESTED (1<<15) /* (function): non-recursive WARNNESTEDVAR */ #define PM_TIED (1<<16) /* array tied to colon-path or v.v. */ #define PM_TAGGED_LOCAL (1<<16) /* (function): non-recursive PM_TAGGED */ @@ -2016,9 +2017,15 @@ struct paramdef { * Flags for assignsparam and assignaparam. */ enum { + /* Add to rather than override value */ ASSPM_AUGMENT = 1 << 0, + /* Test for warning if creating global variable in function */ ASSPM_WARN_CREATE = 1 << 1, - ASSPM_ENV_IMPORT = 1 << 2 + /* Test for warning if using nested variable in function */ + ASSPM_WARN_NESTED = 1 << 2, + ASSPM_WARN = (ASSPM_WARN_CREATE|ASSPM_WARN_NESTED), + /* Import from environment, so exercise care evaluating value */ + ASSPM_ENV_IMPORT = 1 << 3, }; /* node for named directory hash table (nameddirtab) */ @@ -2400,6 +2407,7 @@ enum { VERBOSE, VIMODE, WARNCREATEGLOBAL, + WARNNESTEDVAR, XTRACE, USEZLE, DVORAK, -- cgit 1.4.1