(Fload, Feval_buffer): Emit a warning when lexbind is unset (bug#74145)

This emits a warning at run-time rather than at compile time.

* src/lread.c (get_lexical_binding): New function.
(Fload, Feval_buffer): Use it.
(syms_of_lread): New var `internal--get-default-lexical-binding-function`.
* lisp/files.el: Set it.
(internal--get-default-lexical-binding): New function.
This commit is contained in:
Stefan Monnier 2025-04-07 21:41:35 -04:00
parent 9663c959c7
commit 9f25d46568
3 changed files with 50 additions and 11 deletions

View file

@ -1828,14 +1828,19 @@ Like 'static-if', these macros evaluate their condition at
macro-expansion time and are useful for writing code that can work
across different Emacs versions.
** Lexical binding
---
** You can change the default value of 'lexical-binding'.
*** You can change the default value of 'lexical-binding'.
While the default is still the use of dynamic binding dialect of Elisp
in those places that don't explicitly set 'lexical-binding' you can
change it globally with:
(set-default-toplevel-value 'lexical-binding t)
---
*** Loading a file displays a warning if there is no 'lexical-binding' cookie.
+++
** New macros 'incf' and 'decf'.
They increment or decrement the value stored in a variable (a symbol),

View file

@ -4277,6 +4277,30 @@ all the specified local variables, but ignores any settings of \"mode:\"."
(push elem file-local-variables-alist)))
(hack-local-variables-apply))))))
(defun internal--get-default-lexical-binding (from)
(let ((mib (lambda (node) (buttonize node (lambda (_) (info node))))))
(or (and (bufferp from) (zerop (buffer-size from)))
(and (stringp from)
(eql 0 (file-attribute-size (file-attributes from))))
(display-warning
'(files missing-lexbind-cookie)
(format-message "Missing `lexical-binding' cookie in %S.
You can add one with `M-x elisp-enable-lexical-binding RET'.
See `%s' and `%s'
for more information."
(if (not (and (bufferp from)
(equal (buffer-name from) " *load*")
load-file-name))
from
(abbreviate-file-name load-file-name))
(funcall mib "(elisp)Selecting Lisp Dialect")
(funcall mib "(elisp)Converting to Lexical Binding"))
:warning))
(default-toplevel-value 'lexical-binding)))
(setq internal--get-default-lexical-binding-function
#'internal--get-default-lexical-binding)
(defun hack-local-variables--find-variables (&optional handle-mode)
"Return all local variables in the current buffer.
If HANDLE-MODE is nil, we gather all the specified local

View file

@ -1271,6 +1271,18 @@ close_file_unwind_android_fd (void *ptr)
#endif
static Lisp_Object
get_lexical_binding (Lisp_Object stream, Lisp_Object from)
{
lexical_cookie_t lexc = lisp_file_lexical_cookie (stream);
return (lexc == Cookie_Lex ? Qt
: lexc == Cookie_Dyn ? Qnil
: (NILP (from) /* Loading a byte-compiled file. */
|| NILP (Vinternal__get_default_lexical_binding_function)
? Fdefault_toplevel_value (Qlexical_binding)
: calln (Vinternal__get_default_lexical_binding_function, from)));
}
DEFUN ("load", Fload, Sload, 1, 5, 0,
doc: /* Execute a file of Lisp code named FILE.
First try FILE with `.elc' appended, then try with `.el', then try
@ -1720,11 +1732,8 @@ Return t if the file exists and loads successfully. */)
}
else
{
lexical_cookie_t lexc = lisp_file_lexical_cookie (Qget_file_char);
Fset (Qlexical_binding,
(lexc == Cookie_Lex ? Qt
: lexc == Cookie_Dyn ? Qnil
: Fdefault_toplevel_value (Qlexical_binding)));
get_lexical_binding (Qget_file_char, compiled ? Qnil : file));
if (! version || version >= 22)
readevalloop (Qget_file_char, &input, hist_file_name,
@ -2609,11 +2618,7 @@ This function preserves the position of point. */)
specbind (Qstandard_output, tem);
record_unwind_protect_excursion ();
BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
lexical_cookie_t lexc = lisp_file_lexical_cookie (buf);
specbind (Qlexical_binding,
lexc == Cookie_Lex ? Qt
: lexc == Cookie_Dyn ? Qnil
: Fdefault_toplevel_value (Qlexical_binding));
specbind (Qlexical_binding, get_lexical_binding (buf, buf));
BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
readevalloop (buf, 0, filename,
!NILP (printflag), unibyte, Qnil, Qnil, Qnil);
@ -5169,7 +5174,7 @@ obarray_index (struct Lisp_Obarray *oa, const char *str, ptrdiff_t size_byte)
return knuth_hash (reduce_emacs_uint_to_hash_hash (hash), oa->size_bits);
}
/* Return the symbol in OBARRAY whose names matches the string
/* Return the symbol in OBARRAY whose name matches the string
of SIZE characters (SIZE_BYTE bytes) at PTR.
If there is no such symbol, return the integer bucket number of
where the symbol would be if it were present.
@ -6145,6 +6150,11 @@ through `require'. */);
DEFSYM (Qchar_from_name, "char-from-name");
DEFVAR_LISP ("internal--get-default-lexical-binding-function",
Vinternal__get_default_lexical_binding_function,
doc: /* Function to decide default lexical-binding. */);
Vinternal__get_default_lexical_binding_function = Qnil;
DEFVAR_LISP ("read-symbol-shorthands", Vread_symbol_shorthands,
doc: /* Alist of known symbol-name shorthands.
This variable's value can only be set via file-local variables.