Subject: bug#42168: 26.1; cperl-mode: Bad interpretation of $a++ / $b

From: Harald Jörg <[email-masked]>
Date: Thu, 2 Jul 2020 20:19:21 +0200

How to reproduce:
In a buffer, activate cperl-mode (M-x cperl-mode), then enter:

$a++ / $b;

This is supposed to be a division.  However, cperl-mode interprets the
slash as the start of a regular expression.  Accordingly, the following
text is displayed with the wrong face, and also cperl-mode might
complain that it can't find the end of the regular expression.

Emacs does not crash.

Attached: A patch which fixes the issue for $a++ and $a--, and also
includes a test to verify that the very similar text "$a+ / $b" is still
correctly interpreted as a regular expression.

Disclaimer: I'm rather new to elisp and even newer to ERT.

Cheers,
haj

diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index cdbb59a5ad..2ba6921643 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -3978,6 +3978,9 @@ the sections using `cperl-pod-head-face', `cperl-pod-face',
 				    (and (eq (preceding-char) ?.)
 					 (eq (char-after (- (point) 2)) ?.))
 				    (bobp))
+				;; { $a++ / $b } doesn't start a regex, nor does $a--
+				(not (and (memq (preceding-char) '(?+ ?-))
+					  (eq (preceding-char) (char-before (1- (point))))))
 				;;  m|blah| ? foo : bar;
 				(not
 				 (and (eq c ?\?)
diff --git a/test/lisp/progmodes/cperl-mode/cperl-fontification-tests.el b/test/lisp/progmodes/cperl-mode/cperl-fontification-tests.el
new file mode 100644
index 0000000000..4148db036c
--- /dev/null
+++ b/test/lisp/progmodes/cperl-mode/cperl-fontification-tests.el
@@ -0,0 +1,55 @@
+;;; cperl-fontification-tests.el --- Test fontification in cperl-mode -*- lexical-binding: t -*-
+
+;; Copyright (C) 2020-2020 ...to be decided ...
+
+;; Author: Harald Jörg <[email-masked]>
+;; Maintainer: Harald Jörg
+;; Keywords:       internal
+;; Human-Keywords: internal
+;; Homepage: https://github.com/HaraldJoerg/cperl-mode
+
+;;; Commentary:
+
+;; This is a collection of Tests for the fontification of CPerl-mode.
+;; The primary target is to verify that the refactoring we're doing
+;; right now (May 2020 - ...) doesn't change any behavior, or does the
+;; right thing in cases where new fontification rules are enabled.
+
+;; Run these tests interactively:
+;; (ert-run-tests-interactively '(tag :fontification))
+
+(defun cperl-test-face (text regexp)
+  "Returns the face of the first character matched by REGEXP in TEXT."
+  (interactive)
+  (with-temp-buffer
+    (let ((cperl-hairy nil)
+	  (cperl-font-lock nil)) ;; Does this matter?
+      (insert text)
+      (cperl-mode)
+      (font-lock-fontify-buffer)
+      (goto-char (point-min))
+      (re-search-forward regexp)
+      (message "%s" (match-string 0))
+      (get-text-property (match-beginning 0) 'face))))
+
+(ert-deftest jrockway-issue-45 ()
+  "Verify that '/' is a division after ++ or --, not a regexp.
+Reported in https://github.com/jrockway/cperl-mode/issues/45.
+If seen as regular expression, then the slash is displayed using
+font-lock-constant-face.  If seen as a division, then it doesn't
+have a face property."
+  :tags '(:fontification)
+  ;; The next two Perl expressions have divisions.  Perl "punctuation"
+  ;; operators don't get a face.  The comment at the end of line
+  ;; prevents cperl-mode from tripping over "End of ‘/ ... /’
+  ;; string/RE not found" if it gets it wrong
+  (let ((code "{ $a++ / $b } # /"))
+    (should (equal (cperl-test-face code "/" ) nil)))
+  (let ((code "{ $a-- / $b } # /"))
+    (should (equal (cperl-test-face code "/" ) nil)))
+  ;; The next two Perl expressions have regular expressions.  The
+  ;; delimiter of a RE is fontified with font-lock-constant-face.
+  (let ((code "{ $a+ / $b } # /"))
+    (should (equal (cperl-test-face code "/" ) font-lock-constant-face)))
+  (let ((code "{ $a- / $b } # /"))
+    (should (equal (cperl-test-face code "/" ) font-lock-constant-face))))