emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 81bad84a617: Warn when loading .el files without lexical-binding


From: Mattias Engdegård
Subject: master 81bad84a617: Warn when loading .el files without lexical-binding declaration
Date: Fri, 3 May 2024 13:27:45 -0400 (EDT)

branch: master
commit 81bad84a617be38459da313a75719b5627bb15fe
Author: Mattias Engdegård <mattiase@acm.org>
Commit: Mattias Engdegård <mattiase@acm.org>

    Warn when loading .el files without lexical-binding declaration
    
    This corresponds to the byte-compiler warning for the same issue,
    here emitted for files that aren't compiled but loaded from source.
    It should make the planned change to enable lexical binding by default
    in Emacs 31 go smoother.
    
    * src/lread.c (lexical_cookie_t): New type.
    (lisp_file_lexically_bound_p): Renamed to...
    (lisp_file_lexical_cookie): ...this, with the return value retyped.
    * src/lread.c (warn_missing_cookie): New.
    (Fload): Warn when loading source file and cookie missing.
    (Feval_buffer): Add LOADING arg; warn when set and cookie missing.
    * lisp/international/mule.el (load-with-code-conversion):
    * lisp/startup.el (command-line--load-script):
    Call eval-buffer with LOADING arg set.
    * etc/NEWS: Announce.
---
 etc/NEWS                   |  7 ++++++
 lisp/international/mule.el |  2 +-
 lisp/startup.el            |  2 +-
 src/lread.c                | 56 +++++++++++++++++++++++++++++++++-------------
 4 files changed, 50 insertions(+), 17 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index e2588afeb40..d4177d759f3 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -2422,6 +2422,13 @@ The warning name is 'docstrings-control-chars'.
 *** The warning about wide docstrings can now be disabled separately.
 Its warning name is 'docstrings-wide'.
 
+---
+** Warn about missing 'lexical-binding' directive when loading .el files.
+Emacs now emits a run-time warning if an Elisp source file being loaded
+lacks the '-*- lexical-binding: ... -*-' cookie on the first line.
+See the lexical-binding compiler warning described above for how to make
+the warning go away.
+
 ---
 ** New user option 'native-comp-async-warnings-errors-kind'.
 It allows control of what kinds of warnings and errors from asynchronous
diff --git a/lisp/international/mule.el b/lisp/international/mule.el
index a17221e6d21..8875c4f06af 100644
--- a/lisp/international/mule.el
+++ b/lisp/international/mule.el
@@ -367,7 +367,7 @@ Return t if file exists."
                 (eval-buffer buffer nil
                             ;; This is compatible with what `load' does.
                              (if dump-mode file fullname)
-                            nil t))))
+                            nil t t))))
        (let (kill-buffer-hook kill-buffer-query-functions)
          (kill-buffer buffer)))
       (do-after-load-evaluation fullname)
diff --git a/lisp/startup.el b/lisp/startup.el
index 357a4154e4c..f2532f5254e 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -2935,7 +2935,7 @@ nil default-directory" name)
        ;; buffer is empty.
        (when (looking-at "#!")
          (delete-line))
-       (eval-buffer buffer nil file nil t)))))
+       (eval-buffer buffer nil file nil t t)))))
 
 (defun command-line--eval-script (file)
   (load-with-code-conversion
diff --git a/src/lread.c b/src/lread.c
index 983fdb883ff..f9a1a8562cb 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1053,13 +1053,19 @@ DEFUN ("get-file-char", Fget_file_char, Sget_file_char, 
0, 0, 0,
 
 
 
-/* Return true if the lisp code read using READCHARFUN defines a non-nil
-   `lexical-binding' file variable.  After returning, the stream is
-   positioned following the first line, if it is a comment or #! line,
-   otherwise nothing is read.  */
-
-static bool
-lisp_file_lexically_bound_p (Lisp_Object readcharfun)
+typedef enum {
+  Cookie_None,                 /* no cookie */
+  Cookie_Dyn,                  /* explicit dynamic binding */
+  Cookie_Lex                   /* explicit lexical binding */
+} lexical_cookie_t;
+
+/* Determine if the lisp code read using READCHARFUN defines a
+   `lexical-binding' file variable return its value.
+   After returning, the stream is positioned following the first line,
+   if it is a comment or #! line, otherwise nothing is read.  */
+
+static lexical_cookie_t
+lisp_file_lexical_cookie (Lisp_Object readcharfun)
 {
   int ch = READCHAR;
 
@@ -1070,7 +1076,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun)
         {
           UNREAD (ch);
           UNREAD ('#');
-          return 0;
+          return Cookie_None;
         }
       while (ch != '\n' && ch != EOF)
         ch = READCHAR;
@@ -1083,12 +1089,12 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun)
     /* The first line isn't a comment, just give up.  */
     {
       UNREAD (ch);
-      return 0;
+      return Cookie_None;
     }
   else
     /* Look for an appropriate file-variable in the first line.  */
     {
-      bool rv = 0;
+      lexical_cookie_t rv = Cookie_None;
       enum {
        NOMINAL, AFTER_FIRST_DASH, AFTER_ASTERIX
       } beg_end_state = NOMINAL;
@@ -1170,7 +1176,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun)
              if (strcmp (var, "lexical-binding") == 0)
                /* This is it...  */
                {
-                 rv = (strcmp (val, "nil") != 0);
+                 rv = strcmp (val, "nil") != 0 ? Cookie_Lex : Cookie_Dyn;
                  break;
                }
            }
@@ -1336,6 +1342,17 @@ close_file_unwind_android_fd (void *ptr)
 
 #endif
 
+static void
+warn_missing_cookie (Lisp_Object file)
+{
+  Lisp_Object msg = CALLN (Fformat,
+                          build_string ("File %s lacks `lexical-binding'"
+                                        " directive on its first line"),
+                          file);
+  Vdelayed_warnings_list = Fcons (list2 (Qlexical_binding, msg),
+                                 Vdelayed_warnings_list);
+}
+
 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
@@ -1785,7 +1802,10 @@ Return t if the file exists and loads successfully.  */)
     }
   else
     {
-      if (lisp_file_lexically_bound_p (Qget_file_char))
+      lexical_cookie_t lc = lisp_file_lexical_cookie (Qget_file_char);
+      if (lc == Cookie_None && !compiled)
+       warn_missing_cookie (file);
+      if (lc == Cookie_Lex)
         Fset (Qlexical_binding, Qt);
 
       if (! version || version >= 22)
@@ -2618,7 +2638,7 @@ readevalloop (Lisp_Object readcharfun,
   unbind_to (count, Qnil);
 }
 
-DEFUN ("eval-buffer", Feval_buffer, Seval_buffer, 0, 5, "",
+DEFUN ("eval-buffer", Feval_buffer, Seval_buffer, 0,6, "",
        doc: /* Execute the accessible portion of current buffer as Lisp code.
 You can use \\[narrow-to-region] to limit the part of buffer to be evaluated.
 When called from a Lisp program (i.e., not interactively), this
@@ -2635,6 +2655,8 @@ UNIBYTE, if non-nil, specifies `load-convert-to-unibyte' 
for this
 DO-ALLOW-PRINT, if non-nil, specifies that output functions in the
  evaluated code should work normally even if PRINTFLAG is nil, in
  which case the output is displayed in the echo area.
+LOADING, if non-nil, indicates that this call is part of loading a
+Lisp source file.
 
 This function ignores the current value of the `lexical-binding'
 variable.  Instead it will heed any
@@ -2643,7 +2665,8 @@ settings in the buffer, and if there is no such setting, 
the buffer
 will be evaluated without lexical binding.
 
 This function preserves the position of point.  */)
-  (Lisp_Object buffer, Lisp_Object printflag, Lisp_Object filename, 
Lisp_Object unibyte, Lisp_Object do_allow_print)
+  (Lisp_Object buffer, Lisp_Object printflag, Lisp_Object filename,
+   Lisp_Object unibyte, Lisp_Object do_allow_print, Lisp_Object loading)
 {
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object tem, buf;
@@ -2667,7 +2690,10 @@ 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)));
-  specbind (Qlexical_binding, lisp_file_lexically_bound_p (buf) ? Qt : Qnil);
+  lexical_cookie_t lc = lisp_file_lexical_cookie (buf);
+  if (!NILP (loading) && lc == Cookie_None)
+    warn_missing_cookie (filename);
+  specbind (Qlexical_binding, lc == Cookie_Lex ? Qt : Qnil);
   BUF_TEMP_SET_PT (XBUFFER (buf), BUF_BEGV (XBUFFER (buf)));
   readevalloop (buf, 0, filename,
                !NILP (printflag), unibyte, Qnil, Qnil, Qnil);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]