summaryrefslogtreecommitdiff
path: root/Src
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2017-01-23 09:50:57 +0000
committerPeter Stephenson <pws@zsh.org>2017-01-23 09:50:57 +0000
commitc861b17bbf002129f29e22ab625fd3516ba792a2 (patch)
treed1cd26c6a1f298840fd51fae19a4a25f279ecf10 /Src
parent8cb130fe7311aa428e93978eeceb5ab141959aa6 (diff)
downloadzsh-c861b17bbf002129f29e22ab625fd3516ba792a2.tar.gz
zsh-c861b17bbf002129f29e22ab625fd3516ba792a2.zip
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.
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c12
-rw-r--r--Src/exec.c13
-rw-r--r--Src/options.c1
-rw-r--r--Src/params.c66
-rw-r--r--Src/zsh.h10
5 files changed, 71 insertions, 31 deletions
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,