Subject: Re: bug#25152: 25.1; Customize: errors for `restricted-sexp' in `repeat'

From: Mauro Aranda <[email-masked]>
Date: Fri, 23 Oct 2020 09:59:45 -0300

Mauro Aranda <[email-masked]> writes:

> Drew Adams <[email-masked]> writes:
>
>>> To sum it up, the prompt is an unfortunate mistake, and maybe we could
>>> protect against that, but I think the real problem comes from the
>>> defcustom, which fails to provide a valid default value.
>>
>> Thanks for this explanation.  Makes sense.
>>
>> Can we somehow help users by raising an error when they
>> do this?  Seems like a simple mistake to make.
>
> I think it makes sense, but I'm not sure where would be the right place
> to do it.

Coming back to this, perhaps a good place to warn about a bad default
value is where we find it for the first time.  So I attach a patch that
makes the restricted-sexp widget warn (but not error out) if the
internal value is not a string.

So, for the defcustom in the recipe:
(defcustom bar
  `(ignore)
  "..."
  :type
  '(repeat (restricted-sexp :match-alternatives (functionp)))
  :group 'emacs)

When the user clicks the INS button, the following warning pops up:
Warning (widget-bad-default-value):
A widget of type restricted-sexp has a bad default value.
value: nil
match function: widget-restricted-sexp-match
match-alternatives: (functionp)

which I hope conveys good enough information to fix the mistake.

I made it just a warning, because this mistake doesn't always result in
a messed up buffer.  But it can be changed to an error, of course.
From dcf1a7ac3f4fe2e7e6da80d0b219479c8b454858 Mon Sep 17 00:00:00 2001
From: Mauro Aranda <[email-masked]>
Date: Fri, 23 Oct 2020 09:17:04 -0300
Subject: [PATCH] Warn about a bad default value in restricted-sexp widget

* lisp/wid-edit.el (restricted-sexp widget): New :value-to-external
function.  If value is not in the internal format, then we might be
dealing with a bad default value for the widget, so display a warning
about that.  (Bug#25152)
---
 lisp/wid-edit.el | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 009c6b4faf..4e2cf7416d 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -3585,7 +3585,30 @@ 'restricted-sexp
   :value-to-internal (lambda (widget value)
 		       (if (widget-apply widget :match value)
                            (widget-sexp-value-to-internal widget value)
-			 value)))
+                         value))
+  :value-to-external (lambda (widget value)
+                       ;; We expect VALUE to be a string, so we can convert it
+                       ;; into the external format just by `read'ing it.
+                       ;; But for a restricted-sexp widget with a bad default
+                       ;; value, we might end up calling read with a nil
+                       ;; argument, resulting in an undesired prompt to the
+                       ;; user.  A bad default value is not always a big
+                       ;; problem, but might end up in a messed up buffer,
+                       ;; so display a warning here.  (Bug#25152)
+                       (unless (stringp value)
+                         (display-warning
+                          'widget-bad-default-value
+                          (format-message
+                           "\nA widget of type %S has a bad default value.
+value: %S
+match function: %S
+match-alternatives: %S"
+                           (widget-type widget)
+                           value
+                           (widget-get widget :match)
+                           (widget-get widget :match-alternatives))
+                          :warning))
+                       (read value)))
 
 (defun widget-restricted-sexp-match (widget value)
   (let ((alternatives (widget-get widget :match-alternatives))
-- 
2.28.0