zsh-workers
 help / color / mirror / code / Atom feed
From: Peter Stephenson <p.w.stephenson@ntlworld.com>
To: Zsh hackers list <zsh-workers@zsh.org>
Subject: Re: Defining function based on alias
Date: Sun, 8 Jan 2017 19:19:22 +0000	[thread overview]
Message-ID: <20170108191922.3d3de59f@ntlworld.com> (raw)
In-Reply-To: <20170107221659.1b9232da@ntlworld.com>

On Sat, 7 Jan 2017 22:16:59 +0000
Peter Stephenson <p.w.stephenson@ntlworld.com> wrote:
> It's pretty much always an error to define a function when the name of
> the function is being expanded as an alias, right?  So we could make it
> an error, possible with an option though this would need to be active by
> default to make it useful.
> 
> % foo() { echo this is function foo; }
> % alias foo='echo bar'
> % foo() { echo this is not function foo; }
> zsh: defining function based on alias `foo'
> zsh: parse error near `()'

OK, let me rephrase this.

I am about to commit the following, which I hope will hope will stop
people coming to grief with this common confusion.  Please say if you
see any problems.

I have changed the code so that it doesn't complain if the alias
in affect at the start of the definition provided the "()" as well, since
there is no confusion in this case.

I have added an option to turn this off which is active in sh
compatibility mode since I would guess we can't get away with this
behaviour there.

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index f68a945..ba3ec17 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -1539,6 +1539,35 @@ enditem()
 
 subsect(Scripts and Functions)
 startitem()
+pindex(ALIAS_FUNC_DEF)
+pindex(NO_ALIAS_FUNC_DEF)
+pindex(ALIASFUNCDEF)
+pindex(NOALIASFUNCDEF)
+cindex(functions, defining with expanded aliases)
+cindex(aliases, expanding in function definition)
+item(tt(ALIAS_FUNC_DEF) <S>)(
+By default, zsh does not allow the definition of functions using
+the `var(name) tt(LPAR()RPAR())' syntax if var(name) was expanded as an
+alias: this causes an error.  This is usually the desired behaviour, as
+otherwise the combination of an alias and a function based on the same
+definition can easily cause problems.
+
+When this option is set, aliases can be used for defining functions.
+
+For example, consider the following definitions.
+
+example(alias foo=bar
+foo+LPAR()RPAR() {
+  print This probably does not do what you expect.
+})
+
+Here, tt(foo) is expanded as an alias to tt(bar) before the
+tt(LPAR()RPAR()) is encountered, so the function defined would be named
+tt(bar).  By default this is instead an error in native mode.  Note that
+quoting any part of the function name, or using the keyword
+tt(function), avoids the problem, so is recommended when the function
+name can also be an alias.
+)
 pindex(C_BASES)
 pindex(NO_C_BASES)
 pindex(CBASES)
diff --git a/Src/input.c b/Src/input.c
index eb968ea..f2c22c8 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -670,3 +670,22 @@ ingetptr(void)
 {
     return inbufptr;
 }
+
+/**/
+char *input_hasalias(void)
+{
+    int flags = inbufflags;
+    struct instacks *instackptr = instacktop;
+
+    for (;;)
+    {
+	if (!(flags & INP_CONT))
+	    break;
+	instackptr--;
+	if (instackptr->alias)
+	    return instackptr->alias->node.nam;
+	flags = instackptr->flags;
+    }
+
+    return NULL;
+}
diff --git a/Src/options.c b/Src/options.c
index 18619c8..4729ba5 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -78,6 +78,7 @@ mod_export HashTable optiontab;
  */
 static struct optname optns[] = {
 {{NULL, "aliases",	      OPT_EMULATE|OPT_ALL},	 ALIASESOPT},
+{{NULL, "aliasfuncdef",       OPT_EMULATE|OPT_BOURNE},	 ALIASFUNCDEF},
 {{NULL, "allexport",	      OPT_EMULATE},		 ALLEXPORT},
 {{NULL, "alwayslastprompt",   OPT_ALL},			 ALWAYSLASTPROMPT},
 {{NULL, "alwaystoend",	      0},			 ALWAYSTOEND},
diff --git a/Src/parse.c b/Src/parse.c
index 50a0d5f..ed6c4a8 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -1738,6 +1738,7 @@ par_simple(int *cmplx, int nr)
 {
     int oecused = ecused, isnull = 1, r, argc = 0, p, isfunc = 0, sr = 0;
     int c = *cmplx, nrediradd, assignments = 0, ppost = 0, is_typeset = 0;
+    char *hasalias = input_hasalias();
     wordcode postassigns = 0;
 
     r = ecused;
@@ -1809,6 +1810,8 @@ par_simple(int *cmplx, int nr)
 	} else
 	    break;
 	zshlex();
+	if (!hasalias)
+	    hasalias = input_hasalias();
     }
     if (tok == AMPER || tok == AMPERBANG)
 	YYERROR(oecused);
@@ -1839,6 +1842,8 @@ par_simple(int *cmplx, int nr)
 			char *idstring = dupstrpfx(tokstr+1, eptr-tokstr-1);
 			redir_var = 1;
 			zshlex();
+			if (!hasalias)
+			    hasalias = input_hasalias();
 
 			if (IS_REDIROP(tok) && tokfd == -1)
 			{
@@ -1874,6 +1879,8 @@ par_simple(int *cmplx, int nr)
 		    argc++;
 		}
 		zshlex();
+		if (!hasalias)
+		    hasalias = input_hasalias();
 	    }
 	} else if (IS_REDIROP(tok)) {
 	    *cmplx = c = 1;
@@ -1902,6 +1909,8 @@ par_simple(int *cmplx, int nr)
 	    ecstr(name);
 	    ecstr(str);
 	    zshlex();
+	    if (!hasalias)
+		hasalias = input_hasalias();
 	} else if (tok == ENVARRAY) {
 	    int n, parr;
 
@@ -1936,6 +1945,11 @@ par_simple(int *cmplx, int nr)
 	    /* Error if preceding assignments */
 	    if (assignments || postassigns)
 		YYERROR(oecused);
+	    if (hasalias && !isset(ALIASFUNCDEF) && argc &&
+		hasalias != input_hasalias()) {
+		zwarn("defining function based on alias `%s'", hasalias);
+		YYERROR(oecused);
+	    }
 
 	    *cmplx = c;
 	    lineno = 0;
diff --git a/Src/zsh.h b/Src/zsh.h
index f22d8b1..2a41638 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2222,6 +2222,7 @@ struct histent {
 enum {
     OPT_INVALID,
     ALIASESOPT,
+    ALIASFUNCDEF,
     ALLEXPORT,
     ALWAYSLASTPROMPT,
     ALWAYSTOEND,
diff --git a/Test/A02alias.ztst b/Test/A02alias.ztst
index e52578e..e68e93e 100644
--- a/Test/A02alias.ztst
+++ b/Test/A02alias.ztst
@@ -82,6 +82,7 @@
 0:Global aliasing quotes
 > a string S 
 *>*5*echo S a string S "
+# "
 # Note there is a trailing space on the "> a string S " line
 
   (
@@ -115,3 +116,24 @@
 1:error message has the correct sign
 ?(eval):alias:1: bad option: +x
 ?(eval):alias:1: bad option: -z
+
+  # Usual issue that aliases aren't expanded until we
+  # trigger a new parse...
+  (alias badalias=notacommand
+  eval 'badalias() { print does not work; }')
+1:ALIAS_FUNC_DEF off by default.
+?(eval):1: defining function based on alias `badalias'
+?(eval):1: parse error near `()'
+
+  (alias goodalias=isafunc
+  setopt ALIAS_FUNC_DEF
+  eval 'goodalias() { print does now work; }'
+  isafunc)
+0:ALIAS_FUNC_DEF causes the icky behaviour to be avaliable
+>does now work
+
+  (alias thisisokthough='thisworks() { print That worked; }'
+  eval thisisokthough
+  thisworks)
+0:NO_ALIAS_FUNC_DEF works if the alias is a complete definition
+>That worked


  reply	other threads:[~2017-01-08 19:19 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-07 22:16 Peter Stephenson
2017-01-08 19:19 ` Peter Stephenson [this message]
2017-01-08 20:14   ` Bart Schaefer
2017-01-08 20:23     ` Peter Stephenson
2017-01-09  0:09   ` Bart Schaefer
2017-01-09  4:02     ` Daniel Shahaf
2017-01-09 10:33       ` Peter Stephenson
2017-01-09  1:55   ` Daniel Shahaf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170108191922.3d3de59f@ntlworld.com \
    --to=p.w.stephenson@ntlworld.com \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).