Subject: bug#37127: [PATCH] cperl-mode: Suppress a misleading message

From: Stefan Monnier <[email-masked]>
Date: Fri, 30 Oct 2020 18:12:13 -0400

>> I mean, I don't necessarily mind it, but as a user I find it odd that
>> typing a `)` which has a matching `(` nearby (which can be found without
>> crossing any string/RE boundary) should emit a warning about some
>> "unrelated" surrounding entity like the RE in which it is located.
> In an unterminated RE, the message will appear for every single
> character you type, until the RE is terminated.  I'd find it odd if the
> `)` was the only character where the message didn't appear :)

I agree that `)` should be no different.  And while as a user I find
such messages more annoying than helpful (I much prefer to be warned by
some font-lock highlighting (e.g. by "bleeding" past what I expected to
be the end) or something like a tooltip), I'm OK with the current
behavior.  I just don't think that not emitting the message should
trigger a test failure.

> In Perl, almost any non-whitespace character can be used as
> a delimiter, including letters, quotes, comment starters, and colons.

Yes, I remember that from when I wrote the corresponding
syntax-propertize code for `perl-mode` ;-)

> I see now that at least this test should be skipped in Perl mode, which
> I failed to do.  Again.  Sorry for that.

No problem.

BTW, I just noticed that if I revert your patch to cperl-mode.el, the
test still succeeds :-(

> In general, handling of REs with non-standard delimiters is one of the
> areas where Perl mode has significant deficiencies.

Hmm... I thought I had managed to make it cover most cases back then.
I'd be interested to know which cases I missed (or which new cases were
introduced since then: after all it was quite some years ago).

In any case, along the way I decided that this bug was in large part to
blame on blink-matching-open because it calls `syntax-propertize` from
within narrowing.  So I changed it which made your `cperl-mode`
patch unnecessary.  I also tweaked your test so that it does fail in the
old code (and passes with the new code) and so it also works in
`perl-mode` (except for the second part which I kept but which would
fail in `perl-mode`).
The patch I installed can be found below.

        Stefan

diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index 94f42cb2bc..ebbea6bed9 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -3225,13 +3225,6 @@ cperl-forward-re
 		 (and cperl-brace-recursing
 		      (or (eq ostart  ?\{)
 			  (eq starter ?\{)))
-		 ;; If we are at the end of a narrowed buffer, then a
-		 ;; scan error should not be reported to the user.
-		 ;; This situation actually happens when a closing
-		 ;; paren is entered in a regular expression.
-		 ;; Reported in Bug#37127.
-		 (and (eobp) (buffer-narrowed-p)
-		      (equal (car bb) 'scan-error))
 		 (message
 		  "End of `%s%s%c ... %c' string/RE not found: %s"
 		  argument
diff --git a/lisp/simple.el b/lisp/simple.el
index 2e40e3261c..b1b9c88b32 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -8014,6 +8014,7 @@ blink-matching-open
 	   (blinkpos
             (save-excursion
               (save-restriction
+		(syntax-propertize (point))
                 (if blink-matching-paren-distance
                     (narrow-to-region
                      (max (minibuffer-prompt-end) ;(point-min) unless minibuf.
@@ -8024,7 +8025,7 @@ blink-matching-open
                             (not blink-matching-paren-dont-ignore-comments))))
                   (condition-case ()
                       (progn
-			(syntax-propertize (point))
+			;; (syntax-propertize (point)) ;??
                         (forward-sexp -1)
                         ;; backward-sexp skips backward over prefix chars,
                         ;; so move back to the matching paren.
diff --git a/test/lisp/progmodes/cperl-mode-tests.el b/test/lisp/progmodes/cperl-mode-tests.el
index 75010f7d0f..33ebcccde8 100644
--- a/test/lisp/progmodes/cperl-mode-tests.el
+++ b/test/lisp/progmodes/cperl-mode-tests.el
@@ -224,28 +224,33 @@ cperl-bug37127
   "Verify that closing a paren in a regex goes without a message.
 Also check that the message is issued if the regex terminator is
 missing."
-  (let (collected-messages)
-    ;; Part one: Regex is ok, no messages
-    (ert-with-message-capture collected-messages
-      (with-temp-buffer
-        (insert "$_ =~ /(./;")
-        (cperl-mode)
-        (goto-char (point-min))
-        (search-forward ".")
-        (let ((last-command-event ?\)))
-          (cperl-electric-rparen 1)
-          (cperl-find-pods-heres (point-min) (point-max) t)))
-      (should (string-equal collected-messages "")))
-    ;; part two: Regex terminator missing -> message
+  ;; Part one: Regex is ok, no messages
+  (ert-with-message-capture collected-messages
+    (with-temp-buffer
+      (insert "$_ =~ /(./;")
+      (funcall cperl-test-mode)
+      (goto-char (point-min))
+      (search-forward ".")
+      (let ((last-command-event ?\))
+            ;; Don't emit "Matches ..." even if not visible (e.g. in batch).
+            (blink-matching-paren 'jump-offscreen))
+        (self-insert-command 1)
+        (blink-matching-open))
+      (syntax-propertize (point-max)))
+    (should (string-equal collected-messages "")))
+  ;; part two: Regex terminator missing -> message
+  (when (eq cperl-test-mode #'cperl-mode)
+    ;; This test is only run in `cperl-mode' because only cperl-mode
+    ;; emits a message to warn about such unclosed REs.
     (ert-with-message-capture collected-messages
       (with-temp-buffer
         (insert "$_ =~ /(..;")
         (goto-char (point-min))
-        (cperl-mode)
+        (funcall cperl-test-mode)
         (search-forward ".")
         (let ((last-command-event ?\)))
-          (cperl-electric-rparen 1)
-          (cperl-find-pods-heres (point-min) (point-max) t)))
+          (self-insert-command 1))
+        (syntax-propertize (point-max)))
       (should (string-match "^End of .* string/RE"
                             collected-messages)))))