GNU bug report logs - #79760
Missing ‘lexical-binding’ cookie warning for scripts

Previous Next

Package: emacs;

Reported by: Jonas Bernoulli <jonas <at> bernoul.li>

Date: Mon, 3 Nov 2025 19:53:02 UTC

Severity: normal

To reply to this bug, email your comments to 79760 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Mon, 03 Nov 2025 19:53:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Jonas Bernoulli <jonas <at> bernoul.li>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Mon, 03 Nov 2025 19:53:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: bug-gnu-emacs <at> gnu.org
Subject: Missing ‘lexical-binding’ cookie
 warning for scripts
Date: Mon, 03 Nov 2025 20:52:20 +0100
Hello,

I get 

  Missing ‘lexical-binding’ cookie in "da-script"

warnings for scripts that have to place the cookie on the second line
because they look like this:

  #!/bin/sh
  ":"; exec emacs -Q --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
  (... lisp goes here ...)

But when I visit such files, the local value of`lexical-binding' is t
as expected.

     Cheers,
     Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Mon, 03 Nov 2025 20:27:01 GMT) Full text and rfc822 format available.

Message #8 received at 79760 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: 79760 <at> debbugs.gnu.org
Subject: Re: bug#79760: Missing ‘lexical-binding’ cookie warning for scripts
Date: Mon, 03 Nov 2025 22:26:42 +0200
> Date: Mon, 03 Nov 2025 20:52:20 +0100
> From:  Jonas Bernoulli via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
> 
> Hello,
> 
> I get 
> 
>   Missing ‘lexical-binding’ cookie in "da-script"
> 
> warnings for scripts that have to place the cookie on the second line
> because they look like this:
> 
>   #!/bin/sh
>   ":"; exec emacs -Q --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
>   (... lisp goes here ...)
> 
> But when I visit such files, the local value of`lexical-binding' is t
> as expected.

You expect the part after the # to be somehow passed to the Emacs that
is executed by "exec emacs"?  That's a comment, as far as the shell is
concerned, isn't it?  The Lisp code passed to Emacs is what starts
with (... lisp goes here ...), right?  Or am I missing something?




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Mon, 03 Nov 2025 21:43:02 GMT) Full text and rfc822 format available.

Message #11 received at 79760 <at> debbugs.gnu.org (full text, mbox):

From: Pip Cet <pipcet <at> protonmail.com>
To: Eli Zaretskii <eliz <at> gnu.org>
Cc: 79760 <at> debbugs.gnu.org, Jonas Bernoulli <jonas <at> bernoul.li>
Subject: Re: bug#79760: Missing ‘lexical-binding’ cookie warning for scripts
Date: Mon, 03 Nov 2025 21:41:55 +0000
"Eli Zaretskii" <eliz <at> gnu.org> writes:

>> Date: Mon, 03 Nov 2025 20:52:20 +0100
>> From:  Jonas Bernoulli via "Bug reports for GNU Emacs,
>>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
>>
>> Hello,
>>
>> I get
>>
>>   Missing ‘lexical-binding’ cookie in "da-script"
>>
>> warnings for scripts that have to place the cookie on the second line
>> because they look like this:
>>
>>   #!/bin/sh
>>   ":"; exec emacs -Q --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
>>   (... lisp goes here ...)

Can you try putting "(message "%S" lexical-binding)" there? Unless I
made a mistake copying this, the lexical-binding cookie doesn't actually
work.

>> But when I visit such files, the local value of`lexical-binding' is t
>> as expected.

So this is the unexpected part, right?

> You expect the part after the # to be somehow passed to the Emacs that
> is executed by "exec emacs"?  That's a comment, as far as the shell is
> concerned, isn't it?

> The Lisp code passed to Emacs is what starts with (... lisp goes here
> ...), right?  Or am I missing something?

No, the entire file is passed (by name) to Emacs, including the shebang
itself. Emacs discards it in lread.c, and the second line is valid Lisp.

I don't know whether this helps, but

#!/usr/bin/env -S emacs -x
;; -*- lexical-binding: t -*-
(message "%S" lexical-binding)

appears to work on a GNU/Linux system, where the GNU coreutils env is
installed in this standard location, but only with new emacs versions.

Maybe /bin/env should be used instead. Note that this useful feature
also allows you to pass extra command line parameters to emacs.

The problem is this code in lread.c:

  if (ch != ';')
    /* The first line isn't a comment, just give up.  */
    {
      unreadchar (&source, ch);
      return Cookie_None;
    }

AFAIK, no valid shell script starts with a semicolon, so there's no way
to make this work with #!/bin/sh. Using zsh instead works.

Pip





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Tue, 04 Nov 2025 19:41:01 GMT) Full text and rfc822 format available.

Message #14 received at 79760 <at> debbugs.gnu.org (full text, mbox):

From: Jonas Bernoulli <jonas <at> bernoul.li>
To: Pip Cet <pipcet <at> protonmail.com>, Eli Zaretskii <eliz <at> gnu.org>
Cc: 79760 <at> debbugs.gnu.org
Subject: Re: bug#79760: Missing ‘lexical-binding’ cookie warning for scripts
Date: Tue, 04 Nov 2025 20:39:57 +0100
Pip Cet <pipcet <at> protonmail.com> writes:

> "Eli Zaretskii" <eliz <at> gnu.org> writes:
>
>>> Date: Mon, 03 Nov 2025 20:52:20 +0100
>>> From:  Jonas Bernoulli via "Bug reports for GNU Emacs,
>>>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
>>>
>>> Hello,
>>>
>>> I get
>>>
>>>   Missing ‘lexical-binding’ cookie in "da-script"
>>>
>>> warnings for scripts that have to place the cookie on the second line
>>> because they look like this:
>>>
>>>   #!/bin/sh
>>>   ":"; exec emacs -Q --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
>>>   (... lisp goes here ...)
>
> Can you try putting "(message "%S" lexical-binding)" there? Unless I
> made a mistake copying this, the lexical-binding cookie doesn't actually
> work.

Yes you are absolutely right.
(Sorry, could not resist.  I am in fact a human.)

>>> But when I visit such files, the local value of`lexical-binding' is t
>>> as expected.
>
> So this is the unexpected part, right?

In a way, yes.  What I would like to do is to set the value and then
have that choice respected when the file is executed as a script and
also when it is visited in a buffer.  When doing what I have done
above, it is not respected in the first case but not in the second.
That's worse than if this setting were just never respected.

>> You expect the part after the # to be somehow passed to the Emacs that
>> is executed by "exec emacs"?  That's a comment, as far as the shell is
>> concerned, isn't it?
>
>> The Lisp code passed to Emacs is what starts with (... lisp goes here
>> ...), right?  Or am I missing something?
>
> No, the entire file is passed (by name) to Emacs, including the shebang
> itself. Emacs discards it in lread.c, and the second line is valid Lisp.
>
> I don't know whether this helps, but
>
> #!/usr/bin/env -S emacs -x
> ;; -*- lexical-binding: t -*-
> (message "%S" lexical-binding)

I completely forgot about that (emacs -x).  For the record, that was
added in Emacs 29.1.  I going to use this instead of the hack above.

> appears to work on a GNU/Linux system, where the GNU coreutils env is
> installed in this standard location, but only with new emacs versions.

Setting lexical-binding does not seem to work on GNU GUIX using Emacs
31.0.50.  On this distribution "/usr/bin/env" is a symlink to something
like "/gnu/store/f2rcir6yz0n74jaa6d0fm82f8flmwjnk-coreutils-9.1/bin/env".
Even if I use that explicitly, lexical-binding is still nil.

> Maybe /bin/env should be used instead. Note that this useful feature
> also allows you to pass extra command line parameters to emacs.
>
> The problem is this code in lread.c:
>
>   if (ch != ';')
>     /* The first line isn't a comment, just give up.  */
>     {
>       unreadchar (&source, ch);
>       return Cookie_None;
>     }
>
> AFAIK, no valid shell script starts with a semicolon, so there's no way
> to make this work with #!/bin/sh. Using zsh instead works.

Seems to me this should be changed to also look at the second line
(at least when using "emacs -x").

By the way --and that's just something I though of right now-- maybe
this is an opportunity to *always* look at the second line as well.
That way, one could replace

  ;;; this-library-has-a-long-name.el --- And it also needs a long description  -*- mode: lexical-binding: t;

with

  ;;; this-library-has-a-long-name.el --- And it also needs a long description
  ;; -*- lexical-binding: t;

(Of course we would not be able to actually use this for a few years,
except in new libraries that need at least Emacs 31 for other reasons
anyway.)

>
> Pip

     Thanks!
     Jonas




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Wed, 05 Nov 2025 11:30:03 GMT) Full text and rfc822 format available.

Message #17 received at 79760 <at> debbugs.gnu.org (full text, mbox):

From: Pip Cet <pipcet <at> protonmail.com>
To: Jonas Bernoulli <jonas <at> bernoul.li>
Cc: Eli Zaretskii <eliz <at> gnu.org>, 79760 <at> debbugs.gnu.org
Subject: Re: bug#79760: Missing ‘lexical-binding’ cookie warning for scripts
Date: Wed, 05 Nov 2025 11:29:42 +0000
"Jonas Bernoulli" <jonas <at> bernoul.li> writes:

> Pip Cet <pipcet <at> protonmail.com> writes:
>
>> "Eli Zaretskii" <eliz <at> gnu.org> writes:
>>
>>>> Date: Mon, 03 Nov 2025 20:52:20 +0100
>>>> From:  Jonas Bernoulli via "Bug reports for GNU Emacs,
>>>>  the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>
>>>>
>>>> Hello,
>>>>
>>>> I get
>>>>
>>>>   Missing ‘lexical-binding’ cookie in "da-script"
>>>>
>>>> warnings for scripts that have to place the cookie on the second line
>>>> because they look like this:
>>>>
>>>>   #!/bin/sh
>>>>   ":"; exec emacs -Q --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
>>>>   (... lisp goes here ...)
>>
>> Can you try putting "(message "%S" lexical-binding)" there? Unless I
>> made a mistake copying this, the lexical-binding cookie doesn't actually
>> work.
>
> Yes you are absolutely right.
> (Sorry, could not resist.  I am in fact a human.)
>
>>>> But when I visit such files, the local value of`lexical-binding' is t
>>>> as expected.
>>
>> So this is the unexpected part, right?
>
> In a way, yes.  What I would like to do is to set the value and then
> have that choice respected when the file is executed as a script and
> also when it is visited in a buffer.  When doing what I have done
> above, it is not respected in the first case but not in the second.
> That's worse than if this setting were just never respected.

I agree.

>>> You expect the part after the # to be somehow passed to the Emacs that
>>> is executed by "exec emacs"?  That's a comment, as far as the shell is
>>> concerned, isn't it?
>>
>>> The Lisp code passed to Emacs is what starts with (... lisp goes here
>>> ...), right?  Or am I missing something?
>>
>> No, the entire file is passed (by name) to Emacs, including the shebang
>> itself. Emacs discards it in lread.c, and the second line is valid Lisp.
>>
>> I don't know whether this helps, but
>>
>> #!/usr/bin/env -S emacs -x
>> ;; -*- lexical-binding: t -*-
>> (message "%S" lexical-binding)
>
> I completely forgot about that (emacs -x).  For the record, that was
> added in Emacs 29.1.  I going to use this instead of the hack above.

If it worked, that would be a good idea :-)

>> appears to work on a GNU/Linux system, where the GNU coreutils env is
>> installed in this standard location, but only with new emacs versions.
>
> Setting lexical-binding does not seem to work on GNU GUIX using Emacs
> 31.0.50.  On this distribution "/usr/bin/env" is a symlink to something
> like "/gnu/store/f2rcir6yz0n74jaa6d0fm82f8flmwjnk-coreutils-9.1/bin/env".
> Even if I use that explicitly, lexical-binding is still nil.

#!/usr/bin/env -S emacs --batch -l

works.

#!/usr/bin/env -S emacs -x

doesn't work.

This appears to be because of the code in startup.el.

I would not want to actually touch this code; I think we should merely
document the current behavior rather than changing it.

>> Maybe /bin/env should be used instead. Note that this useful feature
>> also allows you to pass extra command line parameters to emacs.
>>
>> The problem is this code in lread.c:
>>
>>   if (ch != ';')
>>     /* The first line isn't a comment, just give up.  */
>>     {
>>       unreadchar (&source, ch);
>>       return Cookie_None;
>>     }
>>
>> AFAIK, no valid shell script starts with a semicolon, so there's no way
>> to make this work with #!/bin/sh. Using zsh instead works.
>
> Seems to me this should be changed to also look at the second line
> (at least when using "emacs -x").

I don't think we reach this code at all for emacs -x.

> By the way --and that's just something I though of right now-- maybe
> this is an opportunity to *always* look at the second line as well.
> That way, one could replace
>
>   ;;; this-library-has-a-long-name.el --- And it also needs a long description  -*- mode: lexical-binding: t;
>
> with
>
>   ;;; this-library-has-a-long-name.el --- And it also needs a long description
>   ;; -*- lexical-binding: t;
>
> (Of course we would not be able to actually use this for a few years,
> except in new libraries that need at least Emacs 31 for other reasons
> anyway.)

Maybe that's a good idea, but I think our priority here should be to
find a reliable way to run a shebang Elisp script with lexical binding
and document it.

Pip





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#79760; Package emacs. (Wed, 05 Nov 2025 15:01:02 GMT) Full text and rfc822 format available.

Message #20 received at 79760 <at> debbugs.gnu.org (full text, mbox):

From: Eli Zaretskii <eliz <at> gnu.org>
To: Pip Cet <pipcet <at> protonmail.com>, Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: 79760 <at> debbugs.gnu.org, jonas <at> bernoul.li
Subject: Re: bug#79760: Missing ‘lexical-binding’ cookie warning for scripts
Date: Wed, 05 Nov 2025 17:00:29 +0200
> Date: Wed, 05 Nov 2025 11:29:42 +0000
> From: Pip Cet <pipcet <at> protonmail.com>
> Cc: Eli Zaretskii <eliz <at> gnu.org>, 79760 <at> debbugs.gnu.org
> 
> Maybe that's a good idea, but I think our priority here should be to
> find a reliable way to run a shebang Elisp script with lexical binding
> and document it.

Why cannot the Lisp script begin with something like

  (setq-default lexical-binding t)

?

In general, given that our long-term goal is to make lexical-binding
be non-nil by default, I'd prefer not to complicate Emacs due to
temporary problems that soon will go away.

Stefan, WDYT?




This bug report was last modified today.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.